Use C++ linker
[oweals/cde.git] / cde / programs / dtlogin / vglang.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: vglang.c /main/7 1998/03/04 19:28:18 mgreess $ */
24 /*                                                                      *
25  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
26  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
27  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
28  * (c) Copyright 1993, 1994 Novell, Inc.                                *
29  */
30
31  /****************************************************************************
32  **
33  **   File:        ui.c
34  **
35  **   Project:     Common Desktop Environment
36  **
37  **   Description: common ui code for login manager
38  **
39  **
40  ****************************************************************************
41  ************************************<+>*************************************/
42
43
44
45 /***************************************************************************
46  *
47  *  Includes
48  *
49  ***************************************************************************/
50
51 #include        <stdio.h>
52 #include        "vg.h"
53 #include        "vgmsg.h"
54 #include        <Xm/CascadeBG.h>
55 #include        <Xm/RowColumn.h>
56 #include        <Xm/ToggleBG.h>
57
58 /***************************************************************************
59  *
60  *  GetLangName
61  *
62  *  Convert an actual locale name to a meaningful language name which will
63  *  be shown in the language menu. This function may be platform dependent.
64  ***************************************************************************/
65
66 static char *
67 GetLangName( char *label )
68 {
69 /*
70  * Default implementation is to use langName resource. Language names can be
71  * set in Dtlogin file as follows. (In this case, en_US is a locale name which
72  * can be set to LANG environment variable.
73  *
74  *    Dtlogin*en_US*languageName: English (ISO8859-1)
75  */
76     char rmname[50];
77     char rmclass[50];
78     char *rmtype;
79     XrmValue rmvalue;
80
81     sprintf(rmname, "Dtlogin*%s*languageName", label);
82     sprintf(rmclass, "Dtlogin*%s*LanguageName", label);
83     if(XrmGetResource(XtDatabase(dpyinfo.dpy), rmname, rmclass, &rmtype,
84                       &rmvalue)) {
85         return(rmvalue.addr);
86     }
87     else
88         return(NULL);
89 }
90
91 #if defined (ENABLE_DYNAMIC_LANGLIST)
92 /***************************************************************************
93  *
94  *  Methods for dynamic language list
95  *
96  ***************************************************************************/
97
98 /*
99  * _enumLangCmdStart() - start enumeration of languages and descriptions
100  *
101  *  The command specified by Dtlogin*languageListCmd command returns
102  *  the list of locales and a translated description of each.
103  */
104 static void *
105 _enumLangCmdStart(void)
106 {
107   if (appInfo.languageListCmd)
108   {
109     return((void *)popen(appInfo.languageListCmd, "r"));
110   }
111   return(NULL);
112 }
113
114 /*
115  * _enumLangCmdNext() - get next language description pair
116  *                   note: *lang and *desc must be freed by free()
117  *
118  * Read the next locale and description from pipe. Default description
119  * comes from system, but user may override with Xresources description.
120  */
121 static Boolean
122 _enumLangCmdNext(
123   void *state,
124   char **lang,
125   char **desc)
126 {
127   char buf[200];
128   Boolean rc = False;
129
130   if (fgets(buf, sizeof(buf), (FILE *)state) != NULL)
131   {
132    /*
133     * The buf format is "locale desc ...\n". For example:
134     *
135     * pl_PL Polish ISO8859-2 
136     * pt_BR Portuguese (Brazil) ISO8859-1
137     */
138     char *loclang = strtok(buf, " ");  /* lang name from system */
139     char *locdesc = strtok(NULL, "\n"); /* description from system */
140     char *userdesc; /* user provided description from Xresources */
141
142     if ((userdesc = GetLangName(loclang)) != NULL)
143     {
144       locdesc = userdesc; /* use user provided description */
145     }
146
147     *lang = strdup(loclang);
148     *desc = strdup(locdesc);
149     rc = True;
150   }
151   return(rc);
152 }
153
154 /*
155  * _enumLangCmdEnd() - end enumeration of language names and descriptions
156  *
157  * Close pipe.
158  */
159 static void
160 _enumLangCmdEnd(
161   void *state)
162 {
163   pclose((FILE *)state);
164 }
165 #endif /* ENABLE_DYNAMIC_LANGLIST */
166
167 /***************************************************************************
168  *
169  *  Methods for language list of type 'LANGLIST'
170  *
171  ***************************************************************************/
172
173 #define DELIM           " \t"   /* delimiters in language list             */
174
175 struct _enumState 
176 {
177   char *dupstr;
178   char *tokstr;
179 };
180
181 /*
182  * _enumLanglistStart() - start enumeration of languages and descriptions
183  * 
184  * Dtlogin sets up the LANGLIST env var which contains the list
185  * of locale names to display in the language menus. 
186  */
187 static void *
188 _enumLanglistStart(void)
189 {
190   char *p;
191   struct _enumState *state = malloc(sizeof(struct _enumState));
192  
193   if (state)
194   { 
195     if ((p = (char *)getenv(LANGLIST)) == NULL )
196     {
197       free(state);
198       state = NULL;
199     }
200     else
201     {
202       state->dupstr = strdup(p);
203       state->tokstr = state->dupstr;
204     }
205   }
206
207   return((void *)state);
208 }
209
210 /*
211  * _enumLanglistNext() - get next language description pair
212  *                   note: *lang and *desc must be freed by free()
213  *
214  * Get next locale from LANGLIST and get possible description from
215  * Xresources.
216  */
217 static Boolean
218 _enumLanglistNext(
219   void *state,
220   char **lang,
221   char **desc)
222 {
223   Boolean rc = False;
224   struct _enumState *enumstate = (struct _enumState *)state;
225   char *loclang, *locdesc;
226
227   loclang = strtok(enumstate->tokstr, DELIM);
228   if (enumstate->tokstr)
229   {
230     enumstate->tokstr = NULL;
231   }
232
233   if (loclang != NULL)
234   {
235     if ((locdesc = GetLangName(loclang)) == NULL)
236     {
237       locdesc = loclang;
238     }
239     
240     *lang = strdup(loclang);
241     *desc = strdup(locdesc);
242
243     rc = True;
244   }
245   return(rc);
246 }
247
248 /*
249  * _enumLanglistEnd() - end enumeration of language names and descriptions
250  *
251  * Free memory.
252  */
253 static void
254 _enumLanglistEnd(
255   void *state)
256 {
257   struct _enumState *enumstate = (struct _enumState *)state;
258
259   free(enumstate->dupstr);
260   free((char *)state); 
261 }
262
263 /***************************************************************************
264  *
265  *  Methods for language list
266  *
267  ***************************************************************************/
268
269 struct _enumObject
270 {
271   Boolean (*methodNext)();
272   void (*methodEnd)();
273   void *enumstate;
274 };
275
276 /*
277  * _enumLangStart() - start enumeration of languages and descriptions
278  * 
279  * ENABLE_DYNAMIC_LANGLIST defined
280  *   Enumerate LANGLIST. If unsucessful, try 'LangCmd'. LANGLIST will only
281  *   be set if user specified Dtlogin*languageList.
282  *
283  * ENABLE_DYNAMIC_LANGLIST undefined
284  *   Enumerate LANGLIST.
285  * 
286  */
287 static void *
288 _enumLangStart(void)
289 {
290   char *p;
291   struct _enumObject *state = malloc(sizeof(struct _enumObject));
292  
293   if (state)
294   { 
295     state->enumstate = _enumLanglistStart();
296     if (state->enumstate != NULL)
297     {
298       state->methodNext = _enumLanglistNext;
299       state->methodEnd = _enumLanglistEnd;
300     }
301
302     #if defined (ENABLE_DYNAMIC_LANGLIST)
303     if (state->enumstate == NULL)
304     {
305       state->enumstate = _enumLangCmdStart(); 
306       if (state->enumstate != NULL)
307       {
308         state->methodNext = _enumLangCmdNext;
309         state->methodEnd = _enumLangCmdEnd;
310       }
311     }
312     #endif /* ENABLE_DYNAMIC_LANGLIST */
313
314     if (state->enumstate == NULL)
315     {
316       free(state);
317       state = NULL; 
318     }
319   }
320
321   return((void *)state);
322 }
323
324 /*
325  * _enumLangNext() - get next language description pair
326  *                   note: *lang and *desc must be freed by free()
327  */
328 static Boolean
329 _enumLangNext(
330   void *state,
331   char **lang,
332   char **desc)
333 {
334   Boolean rc;
335   struct _enumObject *object = (struct _enumObject *)state;
336
337   rc = (*object->methodNext)(object->enumstate, lang, desc);
338
339   return(rc);
340 }
341
342 /*
343  * _enumLangEnd() - end enumeration of language names and descriptions
344  */
345 static void
346 _enumLangEnd(
347   void *state)
348 {
349   struct _enumObject *object = (struct _enumObject *)state;
350
351   (*object->methodEnd)(object->enumstate);
352
353   free((char *)state); 
354 }
355
356 /***************************************************************************
357  *
358  *  MakeLangMenu
359  *
360  *  Widgets: lang_menu
361  *
362  *  The language menu contains the list of locales available to the 
363  *  the desktop session. This may be a subset of the actual installed
364  *  locales. The list of locales to display in the language menu can
365  *  be provided by the sysadmin, or determined by the login manager.
366  *
367  *  * Sysadmin provided language list
368  *
369  *    A sysadmin can set the Dtlogin.languageList resource to set the list
370  *    of languages. The dtlogin process provides this list to dtgreet
371  *    in the LANGLIST environment variable. This has priority.
372  *
373  *  * Login manager determined language list
374  * 
375  *    If the sysadm does not set Dtlogin.languageList, ie. LANGLIST unset, 
376  *    the login manager will generate the list. There are two methods for
377  *    doing this, one of which is selected at compile time with the
378  *    ENABLE_DYNAMIC_LANGLIST define. 
379  *
380  *    * dynamic list (ENABLE_DYNAMIC_LANGLIST defined)
381  *   
382  *      This method executes the command specified by the 
383  *      Dtlogin*languageListCmd resource. The default is
384  *      /usr/dt/bin/dtlslocale. The languageListCmd command is expected
385  *      to write to stdout a series of language names and descriptions:
386  *        
387  *        lang_name description
388  *        lang_name description
389  *        ...
390  *        
391  *        Example:
392  * 
393  *        En_US English (United States) - IBM-850
394  *        Fr_BE French (Belgium) - IBM-850
395  *
396  *      Also, since languageListCmd is run under dtgreet's locale, a
397  *      localized description can be returned.
398  *
399  *    * static list (ENABLE_DYNAMIC_LANGLIST undefined)
400  *
401  *      This method has dtlogin querying the system and generating
402  *      the language list to be provided to dtgreet via the LANGLIST
403  *      environment variable. In this case dtlogin takes care to use the
404  *      sysadmin provided list if necessary. 
405  *
406  * * Language descriptions
407  * 
408  *   The sysadmin can set the Dtlogin*<lang>.languageName resource to 
409  *   provide a descriptive name for a particular language. If languageName 
410  *   unset, the value used depends on ENABLE_DYNAMIC_LANGLIST. If 
411  *   ENABLE_DYNAMIC_LANGLIST set, the value used is the descriptive text
412  *   provided by languageListCmd. If ENABLE_DYNAMIC_LANGLIST unset, the value
413  *   used is simply the locale name.
414  *
415  * * Default language
416  *  
417  *   The sysadmin can set the Dtlogin*language resource to specify the
418  *   default language in the language menu.
419  *
420  ***************************************************************************/
421
422 #define MAX_LANG_ITEMS  16      /* maximum number of items in one lang menu */
423 #define MAX_NAME_LEN    128     /* maximum length of a language name       */
424
425 struct Langlist {
426   char *lang;                       /* lang name ie En_US, Ja_JP       */
427   char *desc;                       /* lang description ie English     */
428 };
429
430 /*
431  * compareLangDesc() - compare language descriptions in qsort()
432  */
433 static int
434 compareLangDesc(
435   const void *first, 
436   const void *second)
437 {
438   return(strcmp(((struct Langlist *)first)->desc, 
439                 ((struct Langlist *)second)->desc));
440 }
441
442
443 void 
444 MakeLangMenu( void )
445 {
446     register int i, k;
447     char        cblab[MAX_NAME_LEN];    /* pushbutton label                */
448     int         nlang;                  /* total number of languages       */
449     int         nlangMenus;             /* number of language sub-menus    */
450     int         maxitems;               /* max no. of items in sub-lang menu*/
451     Widget      item_menu;
452     Widget      button;
453     char        *tostr;
454
455     struct Langlist {
456       char *lang;                       /* lang name ie En_US, Ja_JP       */
457       char *desc;                       /* lang description ie English     */
458     };
459
460     struct Langlist list[500];
461     void *state;
462
463    /*
464     * Generate list of langname/description pairs.
465     */
466     nlang = 0;
467     state = _enumLangStart();
468     if (state)
469     {
470       while (_enumLangNext(state, &list[nlang].lang, &list[nlang].desc))
471       {
472         nlang++;
473       }
474       _enumLangEnd(state);
475     } 
476
477     if (nlang > 0) {
478
479        /*
480         * Sort by description
481         */
482         qsort((char *)list, nlang, sizeof(list[0]), compareLangDesc);
483
484         /*
485          *  determine number of language sub-menus ...
486          *  (MAX_LANG_ITEMS per menu)
487          */
488          
489         nlangMenus = 0;
490         do {
491             nlangMenus++;
492             maxitems = nlang/nlangMenus;
493         } while ( maxitems > MAX_LANG_ITEMS );
494         if (nlang%nlangMenus != 0) maxitems++;  /* allow for stragglers    */
495
496
497         /*
498          *  build language menu(s)...
499          */
500         i = 0;
501         lang_menu = XmCreatePulldownMenu(options_menu, "lang_menu", argt, i);
502         item_menu = lang_menu;
503
504         for (k = 0; k < nlang; k++) {
505
506             if ( nlangMenus > 1 && k%maxitems == 0) {
507                 i = 0;
508                 item_menu = XmCreatePulldownMenu(lang_menu, "item_menu",
509                                                  argt, i);
510             }
511
512             /*
513              *  build toggle buttons...
514              */
515             i = InitArg(ToggleBG);
516             XtSetArg(argt[i], XmNrecomputeSize,         True            ); i++;
517             if (langenv && (strcmp(langenv, list[k].lang) == 0)) {
518               XtSetArg(argt[i], XmNset,         True            ); i++;
519             }
520     
521             xmstr = XmStringCreateLocalized(list[k].desc);
522             XtSetArg(argt[i], XmNlabelString,   xmstr           ); i++;
523
524             button = XmCreateToggleButtonGadget(
525               item_menu, list[k].lang, argt, i);
526             XtAddCallback (button, XmNvalueChangedCallback,
527               RespondLangCB, (XtPointer) list[k].lang);
528             XmStringFree(xmstr);
529
530             XtManageChild(button);
531             tostr =
532               (char*) ReadCatalog(MC_LABEL_SET, MC_TO_LABEL, MC_DEF_TO_LABEL);
533
534             if ( nlangMenus > 1 && k%maxitems == 0 ) {
535                 int first = k;
536                 int last = k+maxitems >= nlang ? nlang-1 : k+maxitems-1;
537
538                 i = InitArg(CascadeBG);
539
540                 sprintf(cblab, "%s %s %s", 
541                         list[first].desc, tostr, list[last].desc);
542
543                 xmstr = XmStringCreateLocalized(cblab);
544                 XtSetArg(argt[i], XmNlabelString,       xmstr           ); i++;
545                 XtSetArg(argt[i], XmNsubMenuId,         item_menu       ); i++;
546                 XtSetArg(argt[i], XmNrecomputeSize,     True            ); i++;
547                 button = XmCreateCascadeButtonGadget(lang_menu, cblab, argt, i);
548                 XtManageChild(button);
549                 XmStringFree(xmstr);
550             }
551         }
552        /*
553         * Free language list
554         */
555         for (k = 0; k < nlang; k++)
556         {
557           /* free(list[k].lang); -- used as callback data, don't free */
558           free(list[k].desc);
559         }
560     }
561 }