1 /* Trying to isolate SUMA's help functions so that they can
2 be used from AFNI */
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <assert.h>
6 #include <string.h>
7 #include <sys/time.h>
8 #include <math.h>
9 #include "mrilib.h"
10 #include "niml.h"
11 #include "../niml/niml_private.h"
12 #include "suma_objs.h" /* 21 Apr 2020 */
13 #include <X11/Intrinsic.h>
14 
15 /*------------------------------------------------------------*/
16 #ifndef _MCW_XUTIL_HEADER_   /* 10 Jul 2020 */
17 extern void MCW_register_hint( RwcWidget , char * ) ;
18 extern void MCW_reghint_children( RwcWidget , char * ) ;
19 extern void MCW_register_help( RwcWidget , char * ) ;
20 extern void MCW_reghelp_children( RwcWidget , char * ) ;
21 #endif
22 /*------------------------------------------------------------*/
23 
24 static DList *All_GUI_Help = NULL;
25 static char *DocumentedWidgets = NULL; /*!< Widget names for which a Sphinx
26                                      documentation entry has been created */
SUMA_get_DocumentedWidgets(void)27 char *SUMA_get_DocumentedWidgets(void) { return(DocumentedWidgets); }
SUMA_set_DocumentedWidgets(char ** s)28 char *SUMA_set_DocumentedWidgets(char **s) {
29    static char FuncName[]={"SUMA_set_DocumentedWidgets"};
30    if (!s || !*s) {
31       SUMA_S_Err("Come on friend!");
32       SUMA_RETURN(DocumentedWidgets);
33    }
34    SUMA_ifree(DocumentedWidgets);
35    DocumentedWidgets = *s; *s = NULL;
36    SUMA_RETURN(DocumentedWidgets);
37 }
38 
SUMA_free_DocumentedWidgets(void)39 void SUMA_free_DocumentedWidgets(void) {
40                         SUMA_ifree(DocumentedWidgets); return; }
41 
SUMA_Free_Widget_Help(void * data)42 void SUMA_Free_Widget_Help(void *data)
43 {
44    static char FuncName[]={"SUMA_Free_Widget_Help"};
45    GUI_WIDGET_HELP *gwh = (GUI_WIDGET_HELP *)data;
46 
47    SUMA_ENTRY;
48    if (data) SUMA_free(data);
49    SUMA_RETURNe;
50 }
51 
52 /* Format help key string */
SUMA_hkf_eng(char * keyi,TFORM target,char * cm)53 char * SUMA_hkf_eng(char *keyi, TFORM target, char *cm)
54 {
55    static char FuncName[]={"SUMA_hkf_eng"};
56    static char ss[20][512];
57    char key1[256], key2[256], *direc="kbd";
58    static int c;
59    char *s, cs[5]={""}, *wname_URI=NULL;
60    int ichar=-1;
61 
62    if (!cm) cm = "";
63 
64    ++c;
65    if (c > 19) c = 0;
66    s = (char *)ss[c]; s[0] = s[511] = '\0';
67    if (!keyi) return(s);
68    switch (target) {
69       default:
70       case TXT: /* SUMA */
71          /* Actually COMMA, PERIOD, STAR mods are not needed, leave
72          for posterity.*/
73          if (strstr(keyi,"COMMA")) {
74             snprintf(key1, 255, ",");
75          } else if (strstr(keyi,"PERIOD")) {
76             snprintf(key1, 255, ".");
77          } else if (strstr(keyi,"STAR")) {
78             snprintf(key1, 255, "*");
79          } else {
80             snprintf(key1, 255, "%s", keyi);
81          }
82             snprintf(s, 511, "  %s", key1);
83          return(s);
84          break;
85       case SPX: /* Sphinx */
86          if (strstr(keyi,"->") == keyi) {
87             /* Won't work if you pass key with blanks before '->'
88                But why do such a thing? */
89             snprintf(key1, 255, "%s", keyi+2);
90             snprintf(key2, 255, "%s", keyi+2);
91             direc = "menuselection";
92          } else {
93             snprintf(key1, 255, "%s", keyi);
94             snprintf(key2, 255, "%s", keyi);
95             direc = "kbd";
96          }
97 
98          if (key1[1] == '\0') {
99             ichar = 0;
100          } else if (key1[strlen(key1)-2] == '+'){
101             ichar = strlen(key1)-1;
102          } else ichar = -1;
103 
104          if (ichar > -1) {
105             if (SUMA_IS_UPPER_C(key1[ichar])) {
106                sprintf(cs,"UC_");
107             } else {
108                sprintf(cs,"LC_");
109             }
110          } else {
111             cs[0] = '\0';
112          }
113 
114          #if 0 /* Good for sphinx, not good for having permalinks ! */
115          snprintf(s, 511, "\n.. _%s%s%s:\n\n:%s:`%s`"
116             , cm, cs, deblank_allname(key1,'_')
117             , direc, deblank_name(key2));
118          #elif 1 /* Good for sphinx and for permalinks */
119          direc = "";
120          snprintf(s, 511, "\n.. _%s%s%s:\n\n:ref:`%s %s<%s%s%s>`"
121             , cm, cs, deblank_allname(key1,'_')
122             , deblank_name(key2), direc, cm, cs, deblank_allname(key1,'_'));
123          #else
124          /* Brute force, and a pain, as you can see below.
125             Left here as illustration for 'raw html use */
126          /* Note that I endup with two labels for the key,
127          one as an html permalink and another sphinx one
128          preceding the text of the first line. The reason
129          this was done has to do with how the help
130          for each key is defined explicitly in
131          a series of SUMA_StringAppend() calls.
132          I could skip the second (sphinx) label but then
133          my html page would have ':' at the beginning of
134          each line. The solution would be to store the
135          help for each key much as I do for each widget
136          and make SUMA_hkf() take the 1st line and body (a parallel
137          to the widget hint and help) as options. That's a lot
138          of tediousness I don't care for quite yet. */
139          wname_URI = SUMA_append_replace_string(cm,
140                               deblank_allname(key1,'_'),cs,0);
141          SUMA_Sphinx_Widget_Name_2_Link(wname_URI);
142                snprintf(s, 511, "\n"
143            ".. _%s%s%s:\n"
144            "\n"
145            ".. only:: latex or latexpdf\n"
146            "\n"
147            "   :%s:`%s`\n"
148            "\n"
149            "..\n"
150            "\n"
151            ".. only:: html\n"
152            "\n"
153            "   .. raw:: html\n"
154            "\n"
155            "      <div class=\"section\" id=\"%s\">\n"
156            "      <p><a class=\"section\" href=\"#%s\" title=\"%s keyb link\">"
157            "<strong>%s</strong>:</a> </p></div>\n"
158            "\n"
159            "..\n"
160            "\n"
161            ":%s:`%s`",
162                   cm, cs, deblank_allname(key1,'_') ,
163                   direc, deblank_name(key2),
164                   wname_URI, wname_URI,
165                   deblank_name(key2), deblank_name(key2),
166                   direc, deblank_name(key2));
167 
168             SUMA_ifree(wname_URI);
169 
170          #endif
171 
172          return(s);
173          break;
174    }
175    return(s);
176 }
177 
SUMA_hkf(char * keyi,TFORM target)178 char * SUMA_hkf(char *keyi, TFORM target) {
179    /* for main suma area keys */
180    return(SUMA_hkf_eng(keyi,target,""));
181 }
182 
SUMA_hkcf(char * keyi,TFORM target)183 char * SUMA_hkcf(char *keyi, TFORM target) {
184    /* for colormap area keys */
185    return(SUMA_hkf_eng(keyi,target,"CM_"));
186 }
187 
SUMA_Sphinx_Widget_Name_2_Link(char * name)188 char *SUMA_Sphinx_Widget_Name_2_Link(char *name)
189 {
190    static char FuncName[]={"SUMA_Sphinx_Widget_Name_2_Link"};
191    int m_i, m_c=0;
192 
193    SUMA_ENTRY;
194 
195    if (name)   {
196       SUMA_TO_LOWER(name);
197       if (name[strlen(name)-1] == '.') name[strlen(name)-1]='\0';
198 
199       for (m_i=0, m_c=0; m_i<strlen(name); ++m_i) {
200          if (SUMA_IS_BLANK(name[m_i]) || name[m_i] == '/' ||
201              name[m_i] == '[' || name[m_i] == ']' || name[m_i] == '.' ||
202              name[m_i] == '_' || name[m_i] == '+') {
203             name[m_c++] = '-';
204          } else if (name[m_i] == '>') {
205             /* ignore it */
206          } else {
207             name[m_c++] = name[m_i];
208          }
209       }
210    }
211    name[m_c] = '\0';
212 
213    SUMA_RETURN(name);
214 }
215 
216 /* Format GUI section */
SUMA_gsf_eng(char * uwname,TFORM target,char ** hintout,char ** helpout)217 char * SUMA_gsf_eng(char *uwname, TFORM target, char **hintout, char **helpout)
218 {
219    static char FuncName[]={"SUMA_gsf_eng"};
220    static char ss[20][512], wnameclp[256];
221    char key1[256], key2[256], *direc="kbd", *lnm=NULL;
222    static int c;
223    char *s=NULL, *su=NULL, *shh=NULL, *sii=NULL, *stmp=NULL,
224          *wname = NULL, *wname_URI=NULL;
225    int ichar=-1, i, ntip=0;
226    SUMA_Boolean found = NOPE;
227    GUI_WIDGET_HELP *gwh=NULL, *gwhi=NULL;
228    SUMA_Boolean LocalHead = NOPE;
229 
230    ++c;
231    if (c > 19) c = 0;
232    s = (char *)ss[c]; s[0] = s[511] = '\0';
233 
234    if ((helpout && *helpout) || (hintout && *hintout)) {
235       SUMA_S_Err("string init error");
236       return(s);
237    }
238 
239 
240    if (!uwname) return(s);
241    /* make a copy, uwname is likely a static pointer from a convenience
242    function. Could change underneath you */
243    wname = SUMA_copy_string(uwname);
244 
245    switch (target) {
246       default:
247       case TXT: /* SUMA */
248          snprintf(s, 511, "  %s", wname);
249          SUMA_ifree(wname); return(s);
250          break;
251       case WEB:
252          SUMA_LH("Webbing with %s", wname);
253          if (helpout || hintout) {
254             SUMA_S_Err("Not supposed to call WEB as target with "
255                        "helpout or hintout");
256             SUMA_ifree(wname); return(s);
257          }
258          lnm = SUMA_copy_string(wname);
259          if (!(gwh = SUMA_Get_GUI_Help(lnm, target, NULL, NULL,0))) {
260             SUMA_S_Err("No help for %s\n", lnm);
261             SUMA_suggest_GUI_Name_Match(lnm, 8, NULL);
262          }
263          SUMA_ifree(lnm);
264 
265          if (!gwh) { SUMA_ifree(wname); return(s); }
266 
267 
268          found = NOPE;
269          lnm = SUMA_copy_string(wname);
270          if (gwh->type == 1) { /* a regular olde widget */
271             /* DO NOT rely on SUMA_is_Documented_Widget()
272                outside of targ == WEB */
273             if (!SUMA_is_Documented_Widget(lnm)) {
274                /* Not all widget in a table have entries so get
275                rid of .c??, .r??, .[*], or .[*,*] and
276                try again. */
277                ntip = strlen(lnm)-1;
278                if (ntip > 4) {
279                   while (ntip > 0 &&lnm[ntip] != '.') --ntip;
280                }
281                if ( (strlen(lnm)-ntip) == 4 &&
282                      (lnm[ntip+1] == 'r' || lnm[ntip+1] == 'c') &&
283                      SUMA_IS_DIGIT(lnm[ntip+2]) &&
284                      SUMA_IS_DIGIT(lnm[ntip+3]) ) {
285                   lnm[ntip] = '\0';
286                } else if ((strlen(lnm)-ntip) > 3 &&
287                           lnm[strlen(lnm)-1] == ']' &&
288                           lnm[ntip+1] == ']') {
289                   lnm[ntip] = '\0';
290                }
291                /* try again */
292                if (SUMA_is_Documented_Widget(lnm)) {
293                   SUMA_LH("Found after much suffering as %s", lnm);
294                   found = YUP;
295                }
296             } else {
297                SUMA_LH("Got Widget with %s, %s", s, lnm);
298                found=YUP;
299             }
300          }
301          snprintf(s,511,"https://afni.nimh.nih.gov/pub/dist/doc/htmldoc");
302          if (found) {
303             SUMA_Sphinx_Widget_Name_2_Link(lnm);
304             SUMA_strncat(s,"/SUMA/Controllers.html#", 511);
305             SUMA_strncat(s,lnm,511);
306             SUMA_ifree(sii);
307             SUMA_ifree(lnm);
308          } else { /* Either a container widget
309                     or a widget for which no match
310                     was found */
311             /*  backup until you find a container widget then try for it */
312             gwhi = NULL;
313             i = 1;
314             while (!gwhi && i < gwh->name_lvl){
315                         /* Try some guessing.
316                            Before March 4 2015 only headings had permalinks
317                            created by SPHINX.
318                            Now I manually insert permalinks for the vast
319                            majority of the widgets. Still, some widgets
320                            such as table cells, or frames, might not have their
321                            own entries so we try to get help from their container
322                            */
323                stmp = SUMA_copy_string(SUMA_Name_GUI_Help_eng(gwh,-i));
324                SUMA_LH("Now at %s", stmp);
325                gwhi = SUMA_Get_GUI_Help(stmp, target, NULL, NULL, 0);
326                if (gwhi && gwhi->type == 0) {
327                   SUMA_LH("Got one at %s!",stmp);
328 
329                } else {
330                   gwhi = NULL; /* try again */
331                }
332                SUMA_ifree(stmp);
333                ++i; /* keep going lower */
334             }
335 
336             gwh = gwhi;
337             if (!gwh || gwh->name_lvl<1) {
338                SUMA_LH("No good link found, going with default");
339                SUMA_ifree(wname); return(s);
340             }
341 
342             /* Turn the container name to a link */
343             if (gwh->hint) {
344                sii = SUMA_copy_string(gwh->hint);
345                SUMA_Sphinx_Widget_Name_2_Link(sii);
346                SUMA_strncat(s,"/SUMA/Controllers.html#", 511);
347                SUMA_strncat(s,sii,511);
348                SUMA_ifree(sii);
349             }
350          }
351          SUMA_ifree(wname);
352          return(s);
353          break;
354       case SPX: /* Sphinx */
355          if (!(gwh = SUMA_Get_GUI_Help(wname, target, &shh, &sii,3))) {
356             SUMA_S_Err("No help for %s\n", wname);
357             SUMA_suggest_GUI_Name_Match(wname, 8, NULL);
358             shh = SUMA_copy_string(wname);
359             sii = SUMA_copy_string(wname);
360          }
361 
362          if (!sii) sii = SUMA_copy_string("No Hint");
363          if (helpout) *helpout = shh;
364          if (hintout) *hintout = sii;
365 
366          if (!gwh) {
367             if (!hintout) SUMA_ifree(sii); SUMA_ifree(wname); return(s);
368          }
369 
370          su = (char *)SUMA_calloc(strlen(sii)+2, sizeof(char));
371 
372          lnm = gwh->name[gwh->name_lvl-1];
373          snprintf(wnameclp, 255, "%s", lnm);
374          if (strstr(wnameclp,".r00")) { /* get rid of .r00 */
375             wnameclp[strlen(lnm)-4]='\0';
376          }
377          if (strstr(wnameclp,".c00")) { /* get rid of .c00 */
378             wnameclp[strlen(lnm)-4]='\0';
379          }
380 
381          switch (gwh->type) {
382             case 0: /* container only */
383                if (gwh->name_lvl == 1) {
384                   for (i=0; i<strlen(sii); ++i) {su[i] = '-';} su[i] = '\0';
385                   snprintf(s, 511, "\n"
386                                    ".. _%s:\n"
387                                    "\n"
388                                    "%s\n"
389                                    "%s\n",
390                               wname, sii, su);
391                } else if (gwh->name_lvl == 2) {
392                   for (i=0; i<strlen(sii); ++i) {su[i] = '^';} su[i] = '\0';
393                   snprintf(s, 511, "\n"
394                                    ".. _%s:\n"
395                                    "\n"
396                                    "%s\n"
397                                    "%s\n",
398                               wname, sii, su);
399                } else if (gwh->name_lvl == 3) {
400                   for (i=0; i<strlen(sii); ++i) {su[i] = '"';} su[i] = '\0';
401                   snprintf(s, 511, "\n"
402                                    ".. _%s:\n"
403                                    "\n"
404                                    "%s\n"
405                                    "%s\n",
406                               wname, sii, su);
407                } else if (gwh->name_lvl == 4) {
408                   for (i=0; i<strlen(sii); ++i) {su[i] = '.';} su[i] = '\0';
409                   snprintf(s, 511, "\n"
410                                    ".. _%s:\n"
411                                    "\n"
412                                    "%s\n"
413                                    "%s\n",
414                               wname, sii, su);
415                } else {
416                   snprintf(s, 511, "\n"
417                                    "   .. _%s:\n"
418                                    "\n"
419                                    "**%s**: %s\n"
420                                    "\n",
421                               wname, wnameclp,sii);
422                }
423                break;
424             case 1: /* actual widget */
425                #if 0 /* Looks nice, but no permalinks */
426                snprintf(s, 511, "\n"
427                                 "   .. _%s:\n"
428                                 "\n"
429                                 "**%s**: %s\n"
430                                 "\n",
431                               wname, wnameclp,sii);
432                #elif 1 /* Good for sphinx and for permalinks */
433                snprintf(s, 511, "\n"
434                                 "   .. _%s:\n"
435                                 "\n"
436                                 ":ref:`%s<%s>`: %s\n"
437                                 "\n",
438                               wname, wnameclp, wname, sii);
439                 #else
440                /* Brute force, and a pain, as you can see below.
441                Left here as illustration for 'raw html use */
442                /* The "only::" directives below are not
443                necessary if we are only producing html
444                output. I kept them in should we build
445                other than html in the future */
446                wname_URI = SUMA_copy_string(wname);
447                SUMA_Sphinx_Widget_Name_2_Link(wname_URI);
448                snprintf(s, 511, "\n"
449         " .. _%s:\n"
450         "\n"
451         "   .. only:: latex or latexpdf\n"
452         "\n"
453         "      **%s**:\n"
454         "\n"
455         "   ..\n"
456         "\n"
457         "   .. only:: html\n"
458         "\n"
459         "      .. raw:: html\n"
460         "\n"
461         "         <div class=\"section\" id=\"%s\">\n"
462         "         <p><a class=\"section\" href=\"#%s\" title=\"%s widget link\">"
463         "<strong>%s</strong>:</a> %s</p></div>\n"
464         "\n"
465         "   ..\n"
466         "\n",
467                               wname,
468                               wnameclp,
469                               wname_URI, wname_URI, wnameclp,
470                               wnameclp,sii);
471                SUMA_ifree(wname_URI);
472                #endif
473                break;
474             case 2: /* Just permalink, no text to appear on purpose */
475                wname_URI = SUMA_copy_string(wname);
476                SUMA_Sphinx_Widget_Name_2_Link(wname_URI);
477                snprintf(s, 511, "\n"
478            ".. only:: html\n"
479            "\n"
480            "   .. raw:: html\n"
481            "\n"
482            "      <div class=\"section\" id=\"%s\">\n"
483            "      <p><a class=\"section\" href=\"#%s\" title=\"%s widget link\">"
484            "</a> </p></div>\n"
485            "\n"
486            "..\n"
487            "\n",
488                               wname_URI, wname_URI, wnameclp);
489                SUMA_ifree(wname_URI);
490                break;
491             default:
492                SUMA_S_Err("Bad type %d", gwh->type);
493                break;
494          }
495 
496          if (!hintout) SUMA_ifree(sii);
497          if (!helpout) SUMA_ifree(shh);
498          SUMA_ifree(su);
499          SUMA_ifree(wname);
500          return(s);
501          break;
502    }
503 
504    return(s);
505 }
506 
507 
508 /*
509    Register help for a widget. This is to replace all individual
510 calls to MCW_register_help and _hint.
511 
512    Pointer to help is copied so make sure help is not freed.
513 
514    Widget help becoming centralized to help with auto generation of
515    help webpage
516 */
517 
SUMA_Register_Widget_Help(RwcWidget w,int type,char * name,char * hint,char * help)518 SUMA_Boolean SUMA_Register_Widget_Help(RwcWidget w, int type, char *name,
519                                        char *hint, char *help)
520 {
521    static char FuncName[]={"SUMA_Register_Widget_Help"};
522    char *s=NULL, *st=NULL;
523 
524    SUMA_ENTRY;
525 
526    if (!SUMA_Register_GUI_Help(name, hint, help, w, type)) {
527       SUMA_S_Err("Failed at string level registration");
528       SUMA_RETURN(NOPE);
529    }
530 
531    if (w) {
532       if (help) {
533          s = SUMA_copy_string(help);
534          s = SUMA_Sphinx_String_Edit(&s, TXT, 0);
535          st = s;
536          s = SUMA_Break_String(st, 60); SUMA_ifree(st);
537          /* DO not free s, MCW_register_help uses the pointer as
538             data to the help callback */
539          MCW_register_help(w, s);
540       }
541       if (hint) {
542          /* Just make a copy of the hint and don't worry about
543          what got passed! */
544          s = SUMA_copy_string(hint);
545          MCW_register_hint(w, s);
546       }
547    }
548 
549    SUMA_RETURN(YUP);
550 }
551 
SUMA_Register_Widget_Children_Help(RwcWidget w,int type,char * name,char * hint,char * help)552 SUMA_Boolean SUMA_Register_Widget_Children_Help(RwcWidget w, int type, char *name,
553                                                 char *hint, char *help)
554 {
555    static char FuncName[]={"SUMA_Register_Widget_Children_Help"};
556    char *s=NULL, *st=NULL;
557 
558    SUMA_ENTRY;
559 
560    if (!w || !help) {
561       SUMA_S_Err("NULL widget!!! or No Help");
562       SUMA_RETURN(NOPE);
563    }
564 
565    if (!SUMA_Register_GUI_Help(name, hint, help, w, type)) {
566       SUMA_S_Err("Failed at string level registration");
567       SUMA_RETURN(NOPE);
568    }
569 
570    if (help) {
571       s = SUMA_copy_string(help);
572       s = SUMA_Sphinx_String_Edit(&s, TXT, 0);
573       st = s;
574       s = SUMA_Break_String(st, 60); SUMA_ifree(st);
575          /* DO not free s, MCW_register_help uses the pointer as
576             data to the help callback */
577       MCW_reghelp_children(w, s);
578    }
579 
580    if (hint) {
581       /* Just make a copy of the hint and don't worry about
582       what got passed! */
583       s = SUMA_copy_string(hint);
584       MCW_register_hint(w, s);
585    }
586    SUMA_RETURN(YUP);
587 }
588 
589 /*!
590    Return the help and hint strings stored in the GUI help list.
591 
592    \param gname (char *)Name of widget
593    \param format (int) 0: Default
594                        1: Sphinx format
595    \param helpout (char **): If Not NULL, this will contain
596                              the pointer to a copy of the help string
597                              formatted per format.
598                              Note that if helpout, *helpout
599                              must be NULL at the function call.
600                              You must also free *helpout when
601                              done with it.
602    \param hintout (char **): If Not NULL, this will contain
603                              the pointer to a copy of the hint string
604                              formatted per format.
605                              if (hintout), *hintout must be NULL at
606                              function call. You must also free *hintout
607                              when done with it.
608    \param whelp_off (int ): If non-zero, number of blank charcters by which
609                             to offset the lines of a widget's help string
610    \return gwh (GUI_WIDGET_HELP *)  A pointer to the structure containing
611                                     the widget help.
612                                     NULL if nothing was found.
613 */
SUMA_Get_GUI_Help(char * gname,TFORM format,char ** helpout,char ** hintout,int whelp_off)614 GUI_WIDGET_HELP *SUMA_Get_GUI_Help( char *gname, TFORM format,
615                                     char **helpout, char **hintout,
616                                     int whelp_off)
617 {
618    static char FuncName[]={"SUMA_Get_GUI_Help"};
619    char *s = NULL, *ss=NULL;
620    DListElmt *el = NULL;
621    int nn;
622    GUI_WIDGET_HELP *gwhc=NULL;
623    SUMA_Boolean LocalHead = NOPE;
624 
625    SUMA_ENTRY;
626 
627    if (!gname) { SUMA_S_Err("NULL name"); SUMA_RETURN(gwhc);}
628 
629    if (!All_GUI_Help || !dlist_size(All_GUI_Help)) {
630       SUMA_S_Err("No help list");
631       SUMA_RETURN(gwhc);
632    }
633    if ((helpout && *helpout) || (hintout && *hintout)) {
634       SUMA_S_Err("string init error");
635       SUMA_RETURN(gwhc);
636    }
637 
638    /* Seek name in list */
639    SUMA_LH("Seeking %s", gname);
640    /* Find name in list.
641       Note that no attempt at fast search is done here even though the
642       list is alphabetical... */
643    gwhc = NULL;
644    do {
645       if (!el) el = dlist_head(All_GUI_Help);
646       else el = dlist_next(el);
647       gwhc = (GUI_WIDGET_HELP *)el->data;
648       ss = SUMA_Name_GUI_Help(gwhc);
649       SUMA_LH("Comparing %s to %s (nn=%d)",
650                   ss, gname,
651                   strcmp(SUMA_Name_GUI_Help(gwhc),
652                        gname));
653       if (ss == gname) { /* Safety oblige */
654          SUMA_S_Crit("Collision handsome! Don't send me gname pointers "
655                      "returned by the evil SUMA_Name_GUI_Help()!");
656       }
657       if ((nn = strcmp(ss, gname)) == 0) {
658          el = NULL;
659       } else {
660          gwhc = NULL;
661       }
662    } while (el && el != dlist_tail(All_GUI_Help));
663 
664    if (gwhc) {
665       SUMA_LH("Got new: %s, nn=%d", SUMA_Name_GUI_Help(gwhc), nn);
666       if (helpout) {
667          *helpout = SUMA_copy_string(gwhc->help);
668          if (gwhc->type == 1 && whelp_off) {/* widget and offset requested */
669             SUMA_Sphinx_String_Edit(helpout, format, whelp_off);
670          } else {
671             SUMA_Sphinx_String_Edit(helpout, format, 0);
672          }
673       }
674       if (hintout) {
675          *hintout = SUMA_copy_string(gwhc->hint);
676          SUMA_Sphinx_String_Edit(hintout, format, 0);
677       }
678    } else {
679       SUMA_LH("Got nothing for %s", gname);
680    }
681 
682    SUMA_RETURN(gwhc);
683 }
684 
685 /*!
686    Return the help struct for a certain widget.
687 
688 */
SUMA_Get_Widget_Help(RwcWidget w)689 GUI_WIDGET_HELP *SUMA_Get_Widget_Help( RwcWidget w )
690 {
691    static char FuncName[]={"SUMA_Get_Widget_Help"};
692    char *s = NULL;
693    DListElmt *el = NULL;
694    int nn;
695    GUI_WIDGET_HELP *gwhc=NULL;
696 
697    SUMA_ENTRY;
698 
699    if (!w) { SUMA_S_Err("NULL w"); SUMA_RETURN(gwhc);}
700 
701    if (!All_GUI_Help || !dlist_size(All_GUI_Help)) {
702       SUMA_S_Err("No help list");
703       SUMA_RETURN(gwhc);
704    }
705 
706 
707 
708    /* Find widget in list. */
709    gwhc = NULL;
710    el = NULL;
711    do {
712       if (!el) el = dlist_head(All_GUI_Help);
713       else el = dlist_next(el);
714       gwhc = (GUI_WIDGET_HELP *)el->data;
715       if (w == gwhc->w) {
716          el = NULL;
717       } else {
718          gwhc = NULL;
719       }
720    } while (el && el != dlist_tail(All_GUI_Help));
721 
722    if (!gwhc && (s = XtName(w))) { /* Try matching the hint to the widget name,
723                    This is done for container widgets */
724       el = NULL;
725       do {
726          if (!el) el = dlist_head(All_GUI_Help);
727          else el = dlist_next(el);
728          gwhc = (GUI_WIDGET_HELP *)el->data;
729          if (gwhc->hint && !strcmp(s, gwhc->hint)) {
730             el = NULL;
731          } else {
732             gwhc = NULL;
733          }
734       } while (el && el != dlist_tail(All_GUI_Help));
735    }
736 
737    SUMA_RETURN(gwhc);
738 }
739 
SUMA_Show_All_GUI_Help(DList * dl,FILE * fout,int detail,int format)740 void SUMA_Show_All_GUI_Help(DList *dl, FILE *fout, int detail, int format)
741 {
742    static char FuncName[]={"SUMA_Show_All_GUI_Help"};
743    char *s=NULL;
744 
745    SUMA_ENTRY;
746 
747    if (!fout) fout = stdout;
748 
749    s = SUMA_All_GUI_Help_Info(dl, detail, format);
750 
751    fprintf(fout, "%s", s);
752 
753    SUMA_ifree(s);
754 
755    SUMA_RETURNe;
756 }
757 
SUMA_Register_GUI_Help(char * which,char * hint,char * help,RwcWidget widget,int type)758 int SUMA_Register_GUI_Help(char *which, char *hint, char *help,
759                            RwcWidget widget, int type)
760 {
761    static char FuncName[]={"SUMA_Register_GUI_Help"};
762    GUI_WIDGET_HELP *gwh=NULL, *gwhc=NULL;
763    char *sstmp = NULL, *s=NULL, buf[64]={""};
764    DListElmt *el=NULL;
765    static char WhinedNames[1025]={""};
766    int nn;
767    SUMA_Boolean LocalHead = NOPE;
768 
769    SUMA_ENTRY;
770 
771    if (!hint && !which) {
772       SUMA_S_Err("No hint, no which");
773       SUMA_RETURN(NOPE);
774    }
775 
776    if (!which) { /* get from hint if it has "which" string between
777                    ":which:" directives.
778                    ":which:SurfCont->more:which:hint goes here"*/
779       if ( !(strstr(hint,":which:") == hint) ||
780            !(sstmp = strstr(hint+strlen(":which:"),":which:"))  ) {
781          SUMA_S_Err("No which and no :which: in hint. No good");
782          SUMA_RETURN(NOPE);
783       }
784       which = hint+strlen(":which:");
785       hint = sstmp+strlen(":which:");
786    }
787 
788    gwh = (GUI_WIDGET_HELP *)SUMA_calloc(1,sizeof(GUI_WIDGET_HELP));
789 
790    gwh->w = (void *)widget;
791    gwh->type = type; /* 1, regular widget,
792                         0, container widget, used for organizing the help,
793                         2, a way to insert an html URI for widgets not
794                            individually tracked in the help. For now
795                            this is only done for table entries. */
796 
797    /* parse which: SurfCont->more */
798    sstmp = which;
799    gwh->name_lvl = 0;
800    while( (s = strstr(sstmp, "->")) && gwh->name_lvl < 9 ) {
801       if (s == sstmp) {
802          SUMA_S_Err("Empty child in %s\n", which);
803          SUMA_free(gwh);
804          SUMA_RETURN(NOPE);
805       }
806       nn = s - sstmp;
807       if (nn > 63) {
808          SUMA_S_Err("Too wordy for me.");
809          SUMA_free(gwh);
810          SUMA_RETURN(NOPE);
811       }
812       strncpy(gwh->name[gwh->name_lvl], sstmp, nn);
813                      gwh->name[gwh->name_lvl][nn+1] = '\0';
814       sstmp = s+2; /* skip -> */
815       ++gwh->name_lvl;
816    }
817    /* copy last one */
818    strncpy(gwh->name[gwh->name_lvl], sstmp, 63);
819                      gwh->name[gwh->name_lvl][63] = '\0';
820    ++gwh->name_lvl;
821 
822    /* store the hint */
823    if (hint) {
824       if (strlen(hint)>255) {
825          SUMA_S_Err("Hint too long");
826          SUMA_free(gwh);
827          SUMA_RETURN(NOPE);
828       }
829       strncpy(gwh->hint, hint, 255); gwh->hint[255] = '\0';
830    }
831 
832    /* store the help */
833    gwh->help = help;
834 
835    /* Put it all in */
836    if (!All_GUI_Help) {
837       All_GUI_Help = (DList *)SUMA_calloc(1, sizeof(DList));
838       dlist_init(All_GUI_Help, SUMA_Free_Widget_Help);
839    }
840 
841    /* insert in list */
842    if (!dlist_size(All_GUI_Help)) {
843       dlist_ins_next(All_GUI_Help, dlist_head(All_GUI_Help), (void *)gwh);
844       SUMA_RETURN(YUP);
845    }
846 
847    SUMA_LH("Inserting '%s' with  %s %s",
848                SUMA_Name_GUI_Help(gwh), gwh->hint, gwh->help);
849    /* Insert in alphabetical order */
850    el = dlist_head(All_GUI_Help);
851    do {
852       gwhc = (GUI_WIDGET_HELP *)el->data;
853       if ((nn = strcmp(SUMA_Name_GUI_Help(gwhc),
854                        SUMA_Name_GUI_Help(gwh))) == 0) {
855          snprintf(buf, 63, "%s;",SUMA_Name_GUI_Help(gwh));
856          if (LocalHead || !(sstmp=strstr(WhinedNames, buf))) {
857             SUMA_S_Note("GUI Name %s already in use. No special help entry."
858                         "%s",
859                         SUMA_Name_GUI_Help(gwh),
860                   LocalHead ? "":"\nFurther warnings for this name curtailed.");
861             if (!sstmp) SUMA_strncat(WhinedNames,buf, 1023);
862             if (LocalHead) SUMA_DUMP_TRACE("Trace at duplicate GUI name");
863             SUMA_free(gwh);
864          }
865          SUMA_RETURN(YUP);
866       } else if (nn < 0) {
867          dlist_ins_next(All_GUI_Help, el, (void *)gwh);
868          SUMA_RETURN(YUP);
869       } else {
870          el = dlist_next(el);
871       }
872    } while (el && el != dlist_tail(All_GUI_Help));
873 
874    /* Reached bottom without going over, put on the top */
875    dlist_ins_prev(All_GUI_Help, dlist_head(All_GUI_Help), (void *)gwh);
876 
877    /* A debug for when you get to the bottom condition */
878    if (LocalHead) {
879       SUMA_Show_All_GUI_Help(All_GUI_Help, NULL, 0, 0);
880    }
881 
882    SUMA_RETURN(YUP);
883 }
884 
SUMA_Name_GUI_Help(GUI_WIDGET_HELP * gwh)885 char *SUMA_Name_GUI_Help(GUI_WIDGET_HELP *gwh) {
886    static char FuncName[]={"SUMA_Name_GUI_Help"};
887    return(SUMA_Name_GUI_Help_eng(gwh, 0));
888 }
889 
SUMA_Name_GUI_Help_eng(GUI_WIDGET_HELP * gwh,int lvl)890 char *SUMA_Name_GUI_Help_eng(GUI_WIDGET_HELP *gwh, int lvl)
891 {
892    static char FuncName[]={"SUMA_Name_GUI_Help_eng"};
893    static char sa[10][641], *s=NULL;
894    static int nc=0;
895    int k;
896 
897    SUMA_ENTRY;
898 
899    ++nc; if (nc > 9) nc = 0;
900    s = (char *)sa[nc]; s[0] = '\0';
901 
902    if (!gwh) SUMA_RETURN(s);
903 
904    if (lvl <= 0) lvl = gwh->name_lvl+lvl;
905    if (lvl > gwh->name_lvl) lvl = gwh->name_lvl;
906 
907    for (k=0; k<lvl; ++k) {
908       SUMA_strncat(s,gwh->name[k], 640);
909       if (k<lvl-1) SUMA_strncat(s,"->", 640);
910    }
911 
912    SUMA_RETURN(s);
913 }
914 
SUMA_All_GUI_Help_Info(DList * dl,int detail,int format)915 char *SUMA_All_GUI_Help_Info(DList *dl, int detail, int format)
916 {
917    static char FuncName[]={"SUMA_All_GUI_Help_Info"};
918    SUMA_STRING *SS=NULL;
919    DListElmt *el=NULL;
920    char *s=NULL;
921    GUI_WIDGET_HELP *gwh=NULL;
922 
923    SUMA_ENTRY;
924 
925    SS = SUMA_StringAppend (NULL, NULL);
926 
927    if (!dl) {
928       SS = SUMA_StringAppend(SS,"NULL dl");
929    } else {
930       SS = SUMA_StringAppend_va(SS,
931                                 "Help for %d widgets. Detail %d, Format %d\n"
932                                 "--------------------------------------------\n",
933                                 dlist_size(dl), detail, format);
934       el = dlist_head(dl);
935       do {
936          gwh = (GUI_WIDGET_HELP *)el->data;
937          if (!gwh) SUMA_StringAppend(SS,"NULL widget data!");
938          else {
939                SUMA_StringAppend_va(SS,"Widget: %s (%p)\n",
940                            SUMA_Name_GUI_Help(gwh), gwh->w);
941             if (detail > 0)
942                SUMA_StringAppend_va(SS,"  hint: %s\n", gwh->hint);
943             if (detail > 1) {
944                s = SUMA_copy_string(gwh->help);
945                switch (format) {
946                   case 0:
947                      SUMA_Sphinx_String_Edit(&s, TXT, 0);
948                      SUMA_StringAppend_va(SS,"  help: %s\n", s);
949                      SUMA_ifree(s);
950                      break;
951                   default:
952                   case 1:
953                      SUMA_Sphinx_String_Edit(&s, SPX, 0);
954                      SUMA_StringAppend_va(SS,"  help: %s\n", s);
955                      SUMA_ifree(s);
956                      break;
957                }
958             }
959             SUMA_StringAppend_va(SS,"\n");
960          }
961          el = dlist_next(el);
962       } while (el);
963    }
964 
965    SUMA_StringAppend_va(SS,"\n");
966 
967    SUMA_SS2S(SS, s);
968    SUMA_RETURN(s);
969 }
970 
SUMA_is_Documented_Widget(char * wname)971 SUMA_Boolean SUMA_is_Documented_Widget(char *wname)
972 {
973    static char FuncName[]={"SUMA_is_Documented_Widget"};
974    SUMA_Boolean LocalHead = NOPE;
975 
976    SUMA_ENTRY;
977 
978    if (!wname) SUMA_RETURN(NOPE);
979    if (!DocumentedWidgets) {
980       SUMA_S_Err("Must call SUMA_set_DocumentedWidgets() first!");
981       SUMA_RETURN(NOPE);
982    }
983    if (strstr(DocumentedWidgets, wname)) SUMA_RETURN(YUP);
984 
985    SUMA_LH("Widget %s not in:\n%s", wname, DocumentedWidgets);
986    SUMA_RETURN(NOPE);
987 }
988 
989 
SUMA_suggest_GUI_Name_Match(char * wname,int nmx,DList * dl)990 void SUMA_suggest_GUI_Name_Match(char *wname, int nmx, DList *dl)
991 {
992    static char FuncName[]={"SUMA_suggest_GUI_Name_Match"};
993    int i, nlot;
994    char **lot=NULL, **slot=NULL;
995    DListElmt *el=NULL;
996    GUI_WIDGET_HELP *gwhc=NULL;
997 
998    SUMA_ENTRY;
999 
1000    if (!dl) dl = All_GUI_Help;
1001 
1002    if (!dl || !dlist_size(dl)) {
1003       SUMA_S_Err("No list to be had");
1004       SUMA_RETURNe;
1005    }
1006    lot = (char **)SUMA_calloc(dlist_size(dl), sizeof(char *));
1007    nlot = 0; i = 0;
1008    gwhc = NULL;
1009    do {
1010       if (!el) el = dlist_head(dl);
1011       else el = dlist_next(el);
1012       gwhc = (GUI_WIDGET_HELP *)el->data;
1013       lot[i] = SUMA_copy_string(SUMA_Name_GUI_Help(gwhc));
1014       ++i;
1015    } while (el && el != dlist_tail(dl));
1016    nlot = i;
1017 
1018    slot = approx_str_sort(lot, nlot, wname, 0, NULL, 0, NULL, NULL);
1019 
1020    if (nmx < 0) nmx = nlot;
1021    fprintf(SUMA_STDERR,
1022                "Suggestions for %s\n"
1023                "---------------\n", wname);
1024    for (i=0; i < nlot && i < nmx; ++i) {
1025       fprintf(SUMA_STDERR,
1026                "                %s\n", slot[i]);
1027    }
1028 
1029    for (i=0; i < nlot; ++i) {
1030       SUMA_ifree(lot[i]);
1031       SUMA_ifree(slot[i]);
1032    }
1033    SUMA_ifree(lot);
1034    SUMA_ifree(slot);
1035    SUMA_RETURNe;
1036 }
1037 
1038