LCOV - code coverage report
Current view: top level - gdraw - hotkeys.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 32 228 14.0 %
Date: 2017-08-04 Functions: 3 23 13.0 %

          Line data    Source code
       1             : /* Copyright (C) 2012 by Ben Martin */
       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 "gdraw.h"
      29             : #include "gfile.h"
      30             : #include "hotkeys.h"
      31             : #include <locale.h>
      32             : #include <string.h>
      33             : #include <ustring.h>
      34             : #include <errno.h>
      35             : #include <unistd.h>
      36             : #include "intl.h"
      37             : 
      38             : #ifdef __MINGW32__
      39             : #define fsync _commit
      40             : #endif
      41             : 
      42             : static int hotkeySystemCanUseMacCommand = 0;
      43             : 
      44             : struct dlistnode* hotkeys = 0;
      45             : 
      46           0 : static char* hotkeyGetWindowTypeString( Hotkey* hk ) 
      47             : {
      48           0 :     char* pt = strchr(hk->action,'.');
      49           0 :     if( !pt )
      50           0 :         return 0;
      51           0 :     int len = pt - hk->action;
      52             :     static char buffer[HOTKEY_ACTION_MAX_SIZE+1];
      53           0 :     strncpy( buffer, hk->action, len );
      54           0 :     buffer[len] = '\0';
      55           0 :     return buffer;
      56             : }
      57             : 
      58             : /**
      59             :  * Does the hotkey 'hk' have the right window_type to trigger its
      60             :  * action on the window 'w'.
      61             :  */
      62           0 : static int hotkeyHasMatchingWindowTypeString( char* windowType, Hotkey* hk ) {
      63           0 :     if( !windowType )
      64           0 :         return 0;
      65           0 :     char* pt = strchr(hk->action,'.');
      66           0 :     if( !pt )
      67           0 :         return 0;
      68             :     
      69           0 :     int len = pt - hk->action;
      70           0 :     if( strlen(windowType) < len )
      71           0 :         return 0;
      72           0 :     int rc = strncmp( windowType, hk->action, len );
      73           0 :     if( !rc )
      74           0 :         return 1;
      75           0 :     return 0;
      76             : }
      77             : 
      78             : /**
      79             :  * Does the hotkey 'hk' have the right window_type to trigger its
      80             :  * action on the window 'w'.
      81             :  */
      82             : /*
      83             :  * Unused
      84             : static int hotkeyHasMatchingWindowType( GWindow w, Hotkey* hk ) {
      85             :     char* windowType = GDrawGetWindowTypeName( w );
      86             :     return hotkeyHasMatchingWindowTypeString( windowType, hk );
      87             : }
      88             : */
      89             : 
      90             : static struct dlistnodeExternal*
      91           0 : hotkeyFindAllByStateAndKeysym( char* windowType, uint16 state, uint16 keysym ) {
      92             : 
      93           0 :     struct dlistnodeExternal* ret = 0;
      94           0 :     struct dlistnode* node = hotkeys;
      95           0 :     for( ; node; node=node->next ) {
      96           0 :         Hotkey* hk = (Hotkey*)node;
      97           0 :         if( hk->keysym ) {
      98           0 :             if( keysym == hk->keysym ) {
      99           0 :                 if( state == hk->state ) {
     100           0 :                     if( hotkeyHasMatchingWindowTypeString( windowType, hk ) ) {
     101           0 :                         dlist_pushfront_external( (struct dlistnode **)&ret, hk );
     102             :                     }
     103             :                 }
     104             :             }
     105             :         }
     106             :     }
     107           0 :     return ret;
     108             : }
     109             : 
     110             : 
     111           0 : static Hotkey* hotkeyFindByStateAndKeysym( char* windowType, uint16 state, uint16 keysym ) {
     112             : 
     113           0 :     struct dlistnode* node = hotkeys;
     114           0 :     for( ; node; node=node->next ) {
     115           0 :         Hotkey* hk = (Hotkey*)node;
     116           0 :         if( hk->keysym ) {
     117           0 :             if( keysym == hk->keysym ) {
     118           0 :                 if( state == hk->state ) {
     119           0 :                     if( hotkeyHasMatchingWindowTypeString( windowType, hk ) ) {
     120           0 :                         return hk;
     121             :                     }
     122             :                 }
     123             :             }
     124             :         }
     125             :     }
     126           0 :     return 0;
     127             : }
     128             : 
     129             : 
     130           0 : struct dlistnodeExternal* hotkeyFindAllByEvent( GWindow w, GEvent *event ) {
     131           0 :     if( event->u.chr.autorepeat )
     132           0 :         return 0;
     133           0 :     char* windowType = GDrawGetWindowTypeName( w );
     134           0 :     return hotkeyFindAllByStateAndKeysym( windowType,
     135           0 :                                           event->u.chr.state,
     136           0 :                                           event->u.chr.keysym );
     137             : }
     138             : 
     139             : 
     140           0 : Hotkey* hotkeyFindByEvent( GWindow w, GEvent *event ) {
     141             : 
     142           0 :     if( event->u.chr.autorepeat )
     143           0 :         return 0;
     144             : 
     145           0 :     char* windowType = GDrawGetWindowTypeName( w );
     146           0 :     return hotkeyFindByStateAndKeysym( windowType, event->u.chr.state, event->u.chr.keysym );
     147             : }
     148             : 
     149             : /**
     150             :  * Return the file name of the user defined hotkeys.
     151             :  * The return value must be freed by the caller.
     152             :  *
     153             :  * If extension is not null it will be postpended to the returned path.
     154             :  * This way you get to avoid dealing with appending to a string in c.
     155             :  */
     156          40 : static char* getHotkeyFilename(char* extension) {
     157          40 :     char *ret = NULL;
     158             :     char buffer[1025];
     159          40 :     char *ffdir = getFontForgeUserDir(Config);
     160             : 
     161          40 :     if ( ffdir==NULL ) {
     162           0 :         fprintf(stderr,_("Cannot find your hotkey definition file!\n"));
     163           0 :         return NULL;
     164             :     }
     165          40 :     if( !extension )
     166          40 :         extension = "";
     167             :     
     168          40 :     sprintf(buffer,"%s/hotkeys%s", ffdir, extension);
     169          40 :     ret = copy(buffer);
     170          40 :     free(ffdir);
     171          40 :     return ret;
     172             : }
     173             : 
     174             : /**
     175             :  * Remove leading and trailing " " characters. N memory allocations
     176             :  * are performed, null is injected at the end of string and if there are leading
     177             :  * spaces the return value will be past them.
     178             :  */
     179           0 : static char* trimspaces( char* line ) {
     180           0 :    while ( line[strlen(line)-1]==' ' )
     181           0 :         line[strlen(line)-1] = '\0';
     182           0 :    while( *line == ' ' )
     183           0 :         ++line;
     184           0 :     return line;
     185             : }
     186             : 
     187           0 : static Hotkey* hotkeySetFull( char* action, char* keydefinition, int append, int isUserDefined ) 
     188             : {
     189           0 :     Hotkey* hk = calloc(1,sizeof(Hotkey));
     190           0 :     if ( hk==NULL ) return 0;
     191           0 :     strncpy( hk->action, action, HOTKEY_ACTION_MAX_SIZE );
     192           0 :     HotkeyParse( hk, keydefinition );
     193             : 
     194             :     // If we didn't get a hotkey (No Shortcut)
     195             :     // then we move along
     196           0 :     if( !hk->state && !hk->keysym ) {
     197           0 :         free(hk);
     198           0 :         return 0;
     199             :     }
     200             :         
     201             :     // If we already have a binding for that hotkey combination
     202             :     // for this window, forget the old one. One combo = One action.
     203           0 :     if( !append ) {
     204           0 :         Hotkey* oldkey = hotkeyFindByStateAndKeysym( hotkeyGetWindowTypeString(hk),
     205           0 :                                                      hk->state, hk->keysym );
     206           0 :         if( oldkey ) {
     207           0 :             dlist_erase( &hotkeys, (struct dlistnode *)oldkey );
     208           0 :             free(oldkey);
     209             :         }
     210             :     }
     211             :         
     212           0 :     hk->isUserDefined = isUserDefined;
     213           0 :     dlist_pushfront( &hotkeys, (struct dlistnode *)hk );
     214           0 :     return hk;
     215             : }
     216           0 : Hotkey* hotkeySet( char* action, char* keydefinition, int append )
     217             : {
     218           0 :     int isUserDefined = 1;
     219           0 :     return hotkeySetFull( action, keydefinition, append, isUserDefined );
     220             : }
     221             : 
     222             : 
     223             : 
     224             : /**
     225             :  * Load all the hotkeys from the file at filename, marking them as userdefined
     226             :  * if isUserDefined is set.
     227             :  */
     228         120 : static void loadHotkeysFromFile( const char* filename, int isUserDefined, int warnIfNotFound ) 
     229             : {
     230             :     char line[1100];
     231         120 :     FILE* f = fopen(filename,"r");
     232         120 :     if( !f ) {
     233         120 :         if( warnIfNotFound ) {
     234          40 :             fprintf(stderr,_("Failed to open hotkey definition file: %s\n"), filename );
     235             :         }
     236         240 :         return;
     237             :     }
     238             : 
     239           0 :     while ( fgets(line,sizeof(line),f)!=NULL ) {
     240           0 :         int append = 0;
     241             :         
     242           0 :         if ( *line=='#' )
     243           0 :             continue;
     244           0 :         char* pt = strchr(line,':');
     245           0 :         if ( pt==NULL )
     246           0 :             continue;
     247           0 :         *pt = '\0';
     248           0 :         char* keydefinition = pt+1;
     249           0 :         chomp( keydefinition );
     250           0 :         keydefinition = trimspaces( keydefinition );
     251           0 :         char* action = line;
     252           0 :         if( line[0] == '+' ) {
     253           0 :             append = 1;
     254           0 :             action++;
     255             :         }
     256             :         
     257           0 :         hotkeySetFull( action, keydefinition, append, isUserDefined );
     258             :     }
     259           0 :     fclose(f);
     260             : }
     261             : 
     262             : /**
     263             :  * Load all the default hotkeys for this locale and then the users
     264             :  * ~/.Fontforge/hotkeys.
     265             :  */
     266          40 : void hotkeysLoad()
     267             : {
     268             :     char localefn[PATH_MAX+1];
     269          40 :     char* p = 0;
     270          40 :     char* sharedir = getShareDir();
     271             : 
     272          40 :     snprintf(localefn,PATH_MAX,"%s/hotkeys/default", sharedir );
     273          40 :     loadHotkeysFromFile( localefn, false, true );
     274             :     
     275             :     // FUTURE: perhaps find out how to convert en_AU.UTF-8 that setlocale()
     276             :     //   gives to its fallback of en_GB. There are likely to be a bunch of other
     277             :     //   languages which are similar but have specific locales
     278          40 :     char* currentlocale = copy(setlocale(LC_MESSAGES, 0));
     279          40 :     snprintf(localefn,PATH_MAX,"%s/hotkeys/%s", sharedir, currentlocale);
     280          40 :     loadHotkeysFromFile( localefn, false, false );
     281          80 :     while((p = strrchr( currentlocale, '.' ))) {
     282           0 :         *p = '\0';
     283           0 :         snprintf(localefn,PATH_MAX,"%s/hotkeys/%s", sharedir, currentlocale);
     284           0 :         loadHotkeysFromFile( localefn, false, false );
     285             :     }
     286          80 :     while((p = strrchr( currentlocale, '_' ))) {
     287           0 :         *p = '\0';
     288           0 :         snprintf(localefn,PATH_MAX,"%s/hotkeys/%s", sharedir, currentlocale);
     289           0 :         loadHotkeysFromFile( localefn, false, false );
     290             :     }
     291          40 :     free(currentlocale);
     292             :     
     293          40 :     char* fn = getHotkeyFilename(0);
     294          40 :     if( !fn ) {
     295          40 :         return;
     296             :     }
     297          40 :     loadHotkeysFromFile( fn, true, false );
     298          40 :     free(fn);
     299             : }
     300             : 
     301           0 : static void hotkeysSaveCallback(Hotkey* hk,FILE* f) {
     302           0 :     if( hk->isUserDefined ) {
     303           0 :         fprintf( f, "%s:%s\n", hk->action, hk->text );
     304             :     }
     305           0 : }
     306             : 
     307             : /**
     308             :  * Save all the user defined hotkeys back to the users
     309             :  * ~/.Fontforge/hotkeys file.
     310             :  */
     311           0 : void hotkeysSave() {
     312           0 :     char* fn = getHotkeyFilename(".new");
     313           0 :     if( !fn ) {
     314           0 :         return;
     315             :     }
     316           0 :     FILE* f = fopen(fn,"w");
     317           0 :     if( !f ) {
     318           0 :         free(fn);
     319           0 :         fprintf(stderr,_("Failed to open your hotkey definition file for updates.\n"));
     320           0 :         return;
     321             :     }
     322             : 
     323           0 :     dlist_foreach_reverse_udata( &hotkeys,
     324             :                                  (dlist_foreach_udata_func_type)hotkeysSaveCallback, f );
     325           0 :     fsync(fileno(f));
     326           0 :     fclose(f);
     327             : 
     328             :     //
     329             :     // Atomic rename of new over the old.
     330             :     //
     331           0 :     char* newpath = getHotkeyFilename(0);
     332             : #ifdef __MINGW32__
     333             :     //Atomic rename doesn't exist on Windows.
     334             :     unlink(newpath);
     335             : #endif
     336           0 :     int rc = rename( fn, newpath );
     337           0 :     int e = errno;
     338           0 :     free(fn);
     339           0 :     free(newpath);
     340           0 :     if( rc == -1 ) {
     341           0 :         fprintf(stderr,_("Failed to rename the new hotkeys file over your old one!\n"));
     342           0 :         fprintf(stderr,_("Reason:%s\n"), strerror(e));
     343             :     }
     344             : }
     345             : 
     346             : 
     347           0 : char* hotkeysGetKeyDescriptionFromAction( char* action ) {
     348           0 :     struct dlistnode* node = hotkeys;
     349           0 :     for( ; node; node=node->next ) {
     350           0 :         Hotkey* hk = (Hotkey*)node;
     351           0 :         if(!strcmp(hk->action,action)) {
     352           0 :             return hk->text;
     353             :         }
     354             :     }
     355           0 :     return 0;
     356             : }
     357             : 
     358             : 
     359             : /**
     360             :  * Find a hotkey by the action. This is useful for menus to find out
     361             :  * what hotkey is currently bound to them. So if the user changes
     362             :  * file/open to be alt+j then the menu can adjust the hotkey is is
     363             :  * displaying to show the user what key they have assigned.
     364             :  */
     365           0 : static Hotkey* hotkeyFindByAction( char* action ) {
     366           0 :     struct dlistnode* node = hotkeys;
     367           0 :     for( ; node; node=node->next ) {
     368           0 :         Hotkey* hk = (Hotkey*)node;
     369           0 :         if(!strcmp(hk->action,action)) {
     370           0 :             return hk;
     371             :         }
     372             :     }
     373           0 :     return 0;
     374             : }
     375             : 
     376           0 : Hotkey* isImmediateKey( GWindow w, char* path, GEvent *event )
     377             : {
     378           0 :     char* wt = GDrawGetWindowTypeName(w);
     379           0 :     if(!wt)
     380           0 :         return 0;
     381             : 
     382           0 :     char* subMenuName = "_ImmediateKeys";
     383             :     char line[PATH_MAX+1];
     384           0 :     snprintf(line,PATH_MAX,"%s.%s.%s",wt, subMenuName, path );
     385             : //    printf("line:%s\n",line);
     386           0 :     Hotkey* hk = hotkeyFindByAction( line );
     387           0 :     if( !hk )
     388           0 :         return 0;
     389           0 :     if( !hk->action )
     390           0 :         return 0;
     391             :     
     392           0 :     if( event->u.chr.keysym == hk->keysym )
     393           0 :         return hk;
     394             : 
     395           0 :     return 0;
     396             : }
     397             : 
     398           0 : Hotkey* hotkeyFindByMenuPathInSubMenu( GWindow w, char* subMenuName, char* path ) {
     399             : 
     400           0 :     char* wt = GDrawGetWindowTypeName(w);
     401           0 :     if(!wt)
     402           0 :         return 0;
     403             : 
     404             :     char line[PATH_MAX+1];
     405           0 :     snprintf(line,PATH_MAX,"%s.%s%s%s",wt, subMenuName, ".Menu.", path );
     406             : //    printf("line:%s\n",line);
     407           0 :     return(hotkeyFindByAction(line));
     408             : }
     409             : 
     410           0 : Hotkey* hotkeyFindByMenuPath( GWindow w, char* path ) {
     411             : 
     412           0 :     char* wt = GDrawGetWindowTypeName(w);
     413           0 :     if(!wt)
     414           0 :         return 0;
     415             : 
     416             :     char line[PATH_MAX+1];
     417           0 :     snprintf(line,PATH_MAX,"%s%s%s",wt, ".Menu.", path );
     418           0 :     return(hotkeyFindByAction(line));
     419             : }
     420             : 
     421             : 
     422           0 : char* hotkeyTextToMacModifiers( char* keydesc )
     423             : {
     424           0 :     keydesc = copy( keydesc );
     425           0 :     keydesc = str_replace_all( keydesc, "Ctl", "⌘", 1 );
     426           0 :     keydesc = str_replace_all( keydesc, "Command", "⌘", 1 );
     427           0 :     keydesc = str_replace_all( keydesc, "Cmd", "⌘", 1 );
     428           0 :     keydesc = str_replace_all( keydesc, "Shft", "⇧", 1 );
     429           0 :     keydesc = str_replace_all( keydesc, "Alt", "⎇", 1 );
     430           0 :     keydesc = str_replace_all( keydesc, "+", "", 1 );
     431           0 :     return keydesc;
     432             : }
     433             : 
     434             : 
     435           0 : char* hotkeyTextWithoutModifiers( char* hktext ) {
     436           0 :     if( !strcmp( hktext, "no shortcut" )
     437           0 :         || !strcmp( hktext, "No shortcut" )
     438           0 :         || !strcmp( hktext, "No Shortcut" ))
     439           0 :         return "";
     440             :     
     441           0 :     char* p = strrchr( hktext, '+' );
     442           0 :     if( !p )
     443           0 :         return hktext;
     444             : 
     445             :     //
     446             :     // Handle Control++ by moving back over the last plus
     447             :     //
     448           0 :     if( p > hktext )
     449             :     {
     450           0 :         char* pp = p - 1;
     451           0 :         if( *pp == '+' )
     452           0 :             --p;
     453             :     }
     454           0 :     return p+1;
     455             : }
     456             : 
     457             : 
     458           0 : void hotkeySystemSetCanUseMacCommand( int v )
     459             : {
     460           0 :     hotkeySystemCanUseMacCommand = v;
     461           0 : }
     462             : 
     463           0 : int hotkeySystemGetCanUseMacCommand()
     464             : {
     465           0 :     return hotkeySystemCanUseMacCommand;
     466             : }

Generated by: LCOV version 1.10