1 /*
2  * This file is part of XForms.
3  *
4  *  XForms is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU Lesser General Public License as
6  *  published by the Free Software Foundation; either version 2.1, or
7  *  (at your option) any later version.
8  *
9  *  XForms is distributed in the hope that it will be useful, but
10  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with XForms.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include <string.h>
24 #include <ctype.h>
25 
26 #include "fd_main.h"
27 #include "fd_spec.h"
28 #include "fd_iconinfo.h"
29 
30 
31 /* Pointer to object currently being edited */
32 
33 static FL_OBJECT * curobj;
34 
35 static void save_edited_object( FL_OBJECT * obj );
36 static void restore_edited_object( FL_OBJECT * obj );
37 static void attrib_init( FD_generic_attrib * ui );
38 static int validate_attributes( FL_OBJECT * obj );
39 static void readback_attributes( FL_OBJECT * obj );
40 static void copy_shortcut( FL_OBJECT * dest,
41                            FL_OBJECT * src );
42 static void cleanup_saved_object( void );
43 static void show_attributes( const FL_OBJECT * obj );
44 static int valid_c_identifier( const char * s );
45 static void add_font_choice( const char * p );
46 static int validate_cvar_name( FL_OBJECT * obj,
47                                const char * w );
48 
49 
50 /* Structuure for storing the initial state of the object */
51 
52 static struct {
53 	FL_OBJECT * obj;
54 	char        name[ MAX_VAR_LEN ];
55 	char        cbname[ MAX_VAR_LEN ];
56 	char        argname[ MAX_VAR_LEN ];
57 } saved_object;
58 
59 
60 /* Font sizes, need to do this because of symbolic names */
61 
62 static FL_OBJECT *fnts;
63 
64 typedef struct {
65     int    size;
66     char * name,
67          * sc;
68 } Fsizes;
69 
70 static Fsizes fsizes[ ] =
71 {
72     { FL_TINY_SIZE,   "Tiny",     "Tt#t" },
73     { FL_SMALL_SIZE,  "Small",    "Ss#s" },
74     { FL_NORMAL_SIZE, "Normal",   "Nn#n" },
75     { FL_MEDIUM_SIZE, "Medium",   "Mm#m" },
76     { FL_LARGE_SIZE,  "Large",    "Ll#l" },
77     { FL_HUGE_SIZE,   "Huge",     "Hh#h" },
78     { 11,             "Variable", ""     }
79 };
80 
81 #define NFSIZE ( sizeof fsizes / sizeof *fsizes )
82 
83 /* Character used for newline */
84 
85 #define NL 0x0a
86 
87 
88 
89 /***************************************
90  * Displays and do interaction with the form for changing object attributes.
91  * 'all' indicates whether label, name, etc. should also be changed and is
92  * only set when only a single object is to selected for changing.
93  ***************************************/
94 
95 int
change_object(FL_OBJECT * obj,int all)96 change_object( FL_OBJECT * obj,
97                int         all )
98 {
99     FL_OBJECT *retobj;
100     FD_generic_attrib *ui = fd_generic_attrib;
101     FL_FORM * spec_form = NULL;
102 
103     attrib_init( ui );
104 
105     /* Save current attributes for later restore */
106 
107     curobj = obj;
108     save_edited_object( obj );
109 
110     /* Show only required parts */
111 
112     if ( all )
113     {
114         fl_show_object( ui->labelobj  );
115         fl_show_object( ui->scobj     );
116         fl_show_object( ui->nameobj   );
117         fl_show_object( ui->cbnameobj );
118         fl_show_object( ui->argobj    );
119 
120         if ( ( spec_form = create_spec_form( curobj ) ) )
121         {
122             FL_OBJECT *t;
123 
124             t = fl_addto_tabfolder( fd_attrib->attrib_folder,
125                                     "Spec", spec_form );
126             fl_set_object_shortcut( t, "#S", 1 );
127         }
128     }
129     else
130     {
131         fl_hide_object( ui->labelobj  );
132         fl_hide_object( ui->scobj     );
133         fl_hide_object( ui->nameobj   );
134         fl_hide_object( ui->cbnameobj );
135         fl_hide_object( ui->argobj    );
136     }
137 
138     /* Show attributes of the current object */
139 
140     show_attributes( obj );
141 
142     /* Do interaction */
143 
144     fl_deactivate_all_forms( );
145 
146     /* Disable selection */
147 
148     no_selection = 1;
149 
150     fl_show_form( fd_attrib->attrib, FL_PLACE_GEOMETRY, FL_FULLBORDER,
151                   "Attributes" );
152     fl_set_app_mainform( fd_attrib->attrib );
153 
154     /* Both cancel and readyobj should have their own callbacks, so we don't
155        need to call fl_do_forms(), but since attribute editing can't be
156        invoked for more than one item at a time we need to block the
157        process_xevent. TODO */
158 
159     do
160     {
161         XEvent xev;
162 
163         retobj = fl_do_forms( );
164         if ( retobj == FL_EVENT )
165             fl_XNextEvent( &xev );
166     } while ( ! (    (    retobj == fd_attrib->readyobj
167                        && validate_attributes( curobj ) )
168                   || retobj == fd_attrib->cancelobj ) );
169 
170     if ( retobj == fd_attrib->cancelobj )
171     {
172         restore_edited_object( obj );
173         redraw_the_form( 0 );
174     }
175     else
176     {
177         reread_spec_form( obj );
178         readback_attributes( obj );
179         spec_to_superspec( obj );
180     }
181 
182     cleanup_saved_object( );
183 
184     fl_set_app_mainform( fd_control->control );
185     fl_set_folder_bynumber( fd_attrib->attrib_folder, 1 );
186     if ( spec_form )
187         fl_delete_folder( fd_attrib->attrib_folder, spec_form );
188     fl_hide_form( fd_attrib->attrib );
189     fl_activate_all_forms( );
190 
191     no_selection = 0;
192 
193     return retobj == fd_attrib->readyobj;
194 }
195 
196 
197 /***************************************
198  * Called on switching between "Generic" and "Spec" folder
199  ***************************************/
200 
201 void
folder_switch_cb(FL_OBJECT * obj,long data FL_UNUSED_ARG)202 folder_switch_cb( FL_OBJECT * obj,
203                   long        data  FL_UNUSED_ARG )
204 {
205     int active = fl_get_active_folder_number(
206                         ( ( FD_attrib * ) obj->form->fdui )->attrib_folder );
207 
208     if ( active == 1 )
209         reread_spec_form( curobj );
210     else
211     {
212         readback_attributes( curobj );
213         prepare_spec_form( curobj );
214     }
215 }
216 
217 
218 /***************************************
219  * Callback for most of the objects for setting generic attributes
220  * (instead of having one for each of them) - evaluates the settings
221  * from all the objects in the generic attributes form and sets the
222  * object accordingly.
223  ***************************************/
224 
225 void
apply_cb(FL_OBJECT * obj FL_UNUSED_ARG,long arg FL_UNUSED_ARG)226 apply_cb( FL_OBJECT * obj  FL_UNUSED_ARG,
227           long        arg  FL_UNUSED_ARG )
228 {
229     readback_attributes( curobj );
230     show_attributes( curobj );
231 }
232 
233 
234 /***************************************
235  * Callback for the "Restore" button
236  ***************************************/
237 
238 void
restore_cb(FL_OBJECT * ob FL_UNUSED_ARG,long data FL_UNUSED_ARG)239 restore_cb( FL_OBJECT * ob    FL_UNUSED_ARG,
240             long        data  FL_UNUSED_ARG )
241 {
242     restore_edited_object( curobj );
243     show_attributes( curobj );
244     redraw_the_form( 0 );
245 }
246 
247 
248 /***************************************
249  * Stores all information about the initial settings of the object
250  * being edited (for restoring it)
251  ***************************************/
252 
253 static void
save_edited_object(FL_OBJECT * obj)254 save_edited_object( FL_OBJECT * obj )
255 {
256     /* Get memory for the object to save and copy everything */
257 
258 	/* Now get memory for the saved object and store what it contains */
259 
260     saved_object.obj = fl_malloc( sizeof *saved_object.obj );
261     *saved_object.obj = *obj;
262     saved_object.obj->spec = NULL;
263 
264 	/* Get the objects name, name of the callback function and
265 	   the argument string and store them */
266 
267     get_object_name( obj, saved_object.name, saved_object.cbname,
268 					 saved_object.argname );
269 
270 	/* Now get memory for the saved object, store what it contains and
271        also save the label and the shortcut for real */
272 
273     /* Make a copy of allocated memory in the obejct */
274 
275 	saved_object.obj->label = fl_strdup( obj->label );
276 	copy_shortcut( saved_object.obj, obj );
277 
278     saved_object.obj->u_vdata = saved_object.obj->c_vdata = NULL;
279 
280     copy_superspec( saved_object.obj, obj );
281     copy_iconinfo( saved_object.obj, obj );
282 }
283 
284 
285 /***************************************
286  * Restores the object state to what it was when editing started
287  ***************************************/
288 
289 static void
restore_edited_object(FL_OBJECT * obj)290 restore_edited_object( FL_OBJECT * obj )
291 {
292     void *sp = obj->spec;
293 
294     fl_free( obj->label );
295     fl_free( obj->shortcut );
296 
297     free_superspec( obj );
298     free_iconinfo( obj );
299 
300 	*obj = *saved_object.obj;
301     obj->spec = sp;
302 
303 	obj->label = fl_strdup( saved_object.obj->label );
304 	copy_shortcut( obj, saved_object.obj );
305 
306     obj->u_vdata = obj->c_vdata = NULL;
307 
308     copy_superspec( obj, saved_object.obj );
309     superspec_to_spec( obj );
310 
311     copy_iconinfo( obj, saved_object.obj );
312     restore_spec( obj );
313 
314     set_object_name( obj, saved_object.name, saved_object.cbname,
315 					 saved_object.argname );
316 
317     /* It the "Spec" folder is currently been shown set its content to
318        the restored object settings and redraw it */
319 
320     if ( fl_get_active_folder_number( fd_attrib->attrib_folder ) == 2 )
321     {
322         prepare_spec_form( obj );
323         fl_redraw_object( fd_attrib->attrib_folder );
324     }
325 }
326 
327 
328 /***************************************
329  * Remove all data allocated in the structure for saving the objects
330  * initial state
331  ***************************************/
332 
333 static void
cleanup_saved_object(void)334 cleanup_saved_object( void )
335 {
336     if ( ! saved_object.obj )
337         return;
338 
339 	fl_free( saved_object.obj->label );
340 	fl_free( saved_object.obj->shortcut );
341     free_superspec( saved_object.obj );
342     free_iconinfo( saved_object.obj );
343     fli_safe_free( saved_object.obj );
344 }
345 
346 
347 /***************************************
348  * Initialize the form used for editing the objects attributes, needs to
349  * be run only once
350  ***************************************/
351 
352 static void
attrib_init(FD_generic_attrib * ui)353 attrib_init( FD_generic_attrib * ui )
354 {
355     static int attrib_initialized;
356     int i;
357     VN_pair *vp;
358 
359     if ( attrib_initialized )
360         return;
361 
362     attrib_initialized = 1;
363 
364     fl_clear_choice( ui->boxobj );
365     for ( i = 1, vp = vn_btype; vp->val >= 0; vp++, i++ )
366     {
367         fl_addto_choice( ui->boxobj, vp->shown );
368         fl_set_choice_item_shortcut( ui->boxobj, i, vp->hotkey );
369     }
370 
371     fl_set_object_return( ui->nameobj, FL_RETURN_END );
372     fl_set_object_return( ui->cbnameobj, FL_RETURN_END );
373 
374     /* Resize */
375 
376     fl_set_choice_fontsize( ui->resize, fd_align_fontsize );
377     for ( vp = vn_resize; vp->val >= 0; vp++ )
378         fl_addto_choice( ui->resize, vp->name + 3 );
379 
380     /* Gravity. Due to compatibility issues there are more than is needed
381 	   in vn_gravity */
382 
383     for ( i = 0, vp = vn_gravity; vp->val >= 0 && i < 9; vp++, i++ )
384     {
385         fl_addto_choice( ui->nwgravity, vp->name + 3 );
386         fl_addto_choice( ui->segravity, vp->name + 3 );
387     }
388 
389     /* Align (only show the first 9 elements of 'vn_align' the rest is
390        in there for backward compatibility reasons when reading in a file) */
391 
392     fl_set_choice_fontsize( ui->align, fd_align_fontsize );
393     for ( vp = vn_align, i = 0; vp->val >= 0 && i < 9; vp++, i++ )
394         fl_addto_choice( ui->align, vp->name + 9 );
395     fl_addto_choice( ui->inside, "Inside|Outside" );
396 
397     /* Font stuff */
398 
399     fnts = ui->fontobj;
400     fl_enumerate_fonts( add_font_choice, 1 );
401     fl_addto_choice( ui->styleobj, "Normal|Shadow|Engraved|Embossed" );
402 
403     /* Size */
404 
405     for ( i = 0; i < ( int ) NFSIZE; i++ )
406     {
407         if ( fsizes[ i ].size == FL_NORMAL_SIZE )
408         {
409             fsizes[ i ].name = "Normal";
410             fsizes[ i ].sc = "Nn#n";
411         }
412 
413         fl_addto_choice_f( ui->sizeobj,
414                            "%2d  %s%%r1", fsizes[ i ].size, fsizes[ i ].name );
415         fl_set_choice_item_shortcut( ui->sizeobj, i + 1, fsizes[ i ].sc );
416     }
417 }
418 
419 
420 /***************************************
421  * Sets up the "Generic" attributes form with the properties of the
422  * object currently being edited
423  ***************************************/
424 
425 static void
show_attributes(const FL_OBJECT * obj)426 show_attributes( const FL_OBJECT * obj )
427 {
428     char objname[ MAX_VAR_LEN ],
429          cbname[  MAX_VAR_LEN ],
430          argname[ MAX_VAR_LEN ];
431     char *label;
432     int i,
433         lstyle,
434         spstyle,
435         oksize,
436         align = fl_to_outside_lalign( obj->align );
437     static char othersize[ 32 ];
438 
439     fl_freeze_form( fd_generic_attrib->generic_attrib );
440 
441     /* Fill in list of types */
442 
443     fl_clear_choice( fd_generic_attrib->typeobj );
444     fl_set_choice_fontsize( fd_generic_attrib->typeobj, fd_type_fontsize );
445 
446     if ( obj->objclass != FL_BOX )
447     {
448         for ( i = 0; i < find_class_maxtype( obj->objclass ); i++ )
449             fl_addto_choice_f( fd_generic_attrib->typeobj,
450                                "%s%%r1", find_type_name( obj->objclass, i ) );
451 
452         fl_set_choice( fd_generic_attrib->typeobj, obj->type + 1 );
453     }
454 
455     /* Fill in settings */
456 
457 	/* a) boxtype */
458 
459     fl_set_choice( fd_generic_attrib->boxobj, obj->boxtype + 1 );
460 
461 	/* b) label alignment */
462 
463     fl_set_choice_text( fd_generic_attrib->align, align_name( align, 0 ) + 9 );
464     fl_set_choice( fd_generic_attrib->inside,
465                    fl_is_outside_lalign( obj->align ) + 1 );
466 
467 	/* c) label font and style */
468 
469     lstyle  = obj->lstyle % FL_SHADOW_STYLE;
470     spstyle = obj->lstyle / FL_SHADOW_STYLE;
471 
472     if ( spstyle >= 3 )
473         spstyle = 3;
474 
475     fl_set_choice( fd_generic_attrib->fontobj, lstyle + 1 );
476     fl_set_choice( fd_generic_attrib->styleobj, spstyle + 1 );
477 
478 	/* d) label font size */
479 
480     for ( oksize = i = 0; ! oksize && i < ( int ) NFSIZE; i++ )
481         if ( ( oksize = ( obj->lsize == fsizes[ i ].size ) ) )
482             fl_set_choice( fd_generic_attrib->sizeobj, i + 1 );
483 
484     if ( ! oksize )
485     {
486         sprintf( othersize, "%d (Variable)", obj->lsize );
487         fsizes[ NFSIZE - 1 ].size = obj->lsize;
488         fsizes[ NFSIZE - 1 ].name = othersize;
489         fl_replace_choice( fd_generic_attrib->sizeobj, NFSIZE, othersize );
490         fl_set_choice( fd_generic_attrib->sizeobj, NFSIZE );
491     }
492 
493     /* e) gravity settings */
494 
495     fl_set_choice_text( fd_generic_attrib->nwgravity,
496                         gravity_name( obj->nwgravity ) + 3 );
497     fl_set_choice_text( fd_generic_attrib->segravity,
498                         gravity_name( obj->segravity ) + 3 );
499 
500 	/* f) resize behaviour */
501 
502     fl_set_choice_text( fd_generic_attrib->resize,
503                         resize_name( obj->resize ) + 3 );
504 
505 	/* g) Label string */
506 
507     label = get_label( obj, 0 );
508     fl_set_input( fd_generic_attrib->labelobj, label );
509     fl_free( label );
510 
511 	/* h) name, callback function name and argument */
512 
513     get_object_name( obj, objname, cbname, argname );
514 
515     fl_set_input( fd_generic_attrib->nameobj, objname );
516     fl_set_input( fd_generic_attrib->cbnameobj, cbname );
517     fl_set_input( fd_generic_attrib->argobj, argname );
518 
519 	/* h) shortcut */
520 
521     fl_set_input( fd_generic_attrib->scobj, get_shortcut_string( obj ) );
522 
523 	/* i) object and label colors */
524 
525     fl_set_object_color( fd_generic_attrib->col1obj, obj->col1, obj->col1 );
526     fl_set_object_color( fd_generic_attrib->col2obj, obj->col2, obj->col2 );
527     fl_set_object_color( fd_generic_attrib->lcolobj, obj->lcol, obj->lcol );
528 
529     fl_unfreeze_form( fd_generic_attrib->generic_attrib );
530 }
531 
532 
533 /***************************************
534  * Sets the objects attributes from  the content of the "Generic" attributes
535  * form
536  ***************************************/
537 
538 static void
readback_attributes(FL_OBJECT * obj)539 readback_attributes( FL_OBJECT * obj )
540 {
541     int v1, v2;
542     char * name,
543          * cbname;
544     char tmpbuf[ 128 ];
545 
546     obj->boxtype = fl_get_choice( fd_generic_attrib->boxobj ) - 1;
547 
548     /* Take care: for some objects the "Type" choice is empty! */
549 
550     if (    ( v1 = fl_get_choice( fd_generic_attrib->typeobj ) - 1 ) >= 0
551          && v1 != obj->type )
552         spec_change_type( obj, v1 );
553 
554     /* Label style consists of two parts */
555 
556     v1 = fl_get_choice( fd_generic_attrib->fontobj ) - 1;
557     v2 = fl_get_choice( fd_generic_attrib->styleobj ) - 1;
558     v1 += v2 == 3 ? FL_EMBOSSED_STYLE : ( v2 * FL_SHADOW_STYLE );
559     fl_set_object_lstyle( obj, v1 );
560 
561     fl_set_object_color( obj, fd_generic_attrib->col1obj->col1,
562                          fd_generic_attrib->col2obj->col1 );
563 
564     fl_set_object_lcol( obj, fd_generic_attrib->lcolobj->col1 );
565 
566     sprintf( tmpbuf, "FL_ALIGN_%s",
567              fl_get_choice_text( fd_generic_attrib->align ) );
568     v1 = align_val( tmpbuf );
569 
570     if ( fl_get_choice( fd_generic_attrib->inside ) == 1 )
571         v1 = fl_to_inside_lalign( v1 );
572     else
573         v1 = fl_to_outside_lalign( v1 );
574 
575     fl_set_object_lalign( obj, v1 );
576 
577     sprintf( tmpbuf, "FL_%s",
578              fl_get_choice_text( fd_generic_attrib->nwgravity ) );
579     v1 = gravity_val( tmpbuf );
580 
581     sprintf( tmpbuf, "FL_%s",
582              fl_get_choice_text( fd_generic_attrib->segravity ) );
583     v2 = gravity_val( tmpbuf );
584 
585     fl_set_object_gravity( obj, v1, v2 );
586 
587     /* Set resize property after making sure it fits with the gravity
588        settings */
589 
590     sprintf( tmpbuf, "FL_%s", fl_get_choice_text( fd_generic_attrib->resize ) );
591     fl_set_object_resize( obj, check_resize( resize_val( tmpbuf ),
592                                              obj->nwgravity, obj->segravity ) );
593 
594     fl_set_object_lsize( obj, fsizes[ fl_get_choice(
595                                       fd_generic_attrib->sizeobj ) - 1 ].size );
596 
597     set_label( obj, fl_get_input( fd_generic_attrib->labelobj ) );
598     set_shortcut( obj, fl_get_input( fd_generic_attrib->scobj ) );
599 
600     name = fl_strdup( fl_get_input( fd_generic_attrib->nameobj ) );
601     cbname = fl_strdup( fl_get_input( fd_generic_attrib->cbnameobj ) );
602 
603     if ( ! valid_c_identifier( name ) )
604         *name = '\0';
605 
606     if ( ! valid_c_identifier( cbname ) )
607         *cbname = '\0';
608 
609     set_object_name( obj, name, cbname,
610                      fl_get_input( fd_generic_attrib->argobj ) );
611 
612     fl_free( cbname );
613     fl_free( name );
614 
615     redraw_the_form( 0 );
616 }
617 
618 
619 /***************************************
620  * Turns the string from the label input field into a string suitable for
621  * an object label (taking care of newlines and shortcut markers) and
622  * the sets the label of the object being edited.
623  ***************************************/
624 
625 void
set_label(FL_OBJECT * obj,const char * str)626 set_label( FL_OBJECT  * obj,
627            const char * str )
628 {
629     int i = 0,
630         j = 0;
631     char *tmpstr = fl_malloc( strlen( str ) + 1 );
632 
633     *tmpstr = '\0';
634 
635     do
636     {
637         if ( str[ i ] == '\\' && str[ i + 1 ] == 'n' )
638         {
639             tmpstr[ j++ ] = NL;
640             i++;
641         }
642         else if ( str[ i ] == '\\' && strncmp( str + i + 1, "010", 3 ) == 0 )
643         {
644             if ( ! obj->shortcut || ! *obj->shortcut )
645                 tmpstr[ j++ ] = *fl_ul_magic_char;
646             i += 3;
647         }
648         else
649             tmpstr[ j++ ] = str[ i ];
650     } while ( str[ i++ ] );
651 
652     fl_set_object_label( obj, tmpstr );
653     fl_free( tmpstr );
654 }
655 
656 
657 /***************************************
658  * Sets the shortcut of the object being edited.
659  ***************************************/
660 
661 void
set_shortcut(FL_OBJECT * obj,const char * sc)662 set_shortcut( FL_OBJECT  * obj,
663               const char * sc )
664 {
665     if (    obj->type != FL_RETURN_BUTTON
666          && obj->type != FL_HIDDEN_RET_BUTTON )
667         fl_set_object_shortcut( obj, sc, 1 );
668 }
669 
670 
671 /* if \ preceeds c, \ does not need quote */
672 
673 #define Ok( c ) \
674     ( c== '"' || c== '\\' || c == 't' || c == 'n' \
675       || isdigit( ( unsigned char )  c ) )
676 
677 
678 /***************************************
679  * Decides if label need quotes ('\')
680  ***************************************/
681 
682 static int
need_quote(const char * s,int i)683 need_quote( const char * s,
684             int          i )
685 {
686     int c = s[ i ],
687         p =  i ? s[ i - 1 ] : 0,     /* prev char */
688         n = *s ? s[ i + 1 ] : 0;     /* next char */
689 
690     if ( c == '"' && p != '\\' )
691         return 1;
692     else if ( c == '\\' && p != '\\' )
693         return ! isdigit( ( unsigned char ) n ) && ! Ok( n );
694     else
695         return 0;
696 }
697 
698 
699 /***************************************
700  * Takes an objects label string and converts it into a string suitable
701  * for setting as the content of the label input field
702  ***************************************/
703 
704 char *
get_label(const FL_OBJECT * obj,int c_source)705 get_label( const FL_OBJECT * obj,
706            int               c_source )
707 {
708     int i = 0,
709         j = 0;
710     const char *label = obj->label;
711     int len = strlen( label );
712     int tlen = len + 1;
713     char *tmpstr = fl_malloc( tlen );
714 
715     for ( i = 0; i < len; i++ )
716     {
717         if ( label[ i ] == NL )
718         {
719             tmpstr = fl_realloc( tmpstr, tlen += 1 );
720             tmpstr[ j++ ] = '\\';
721             tmpstr[ j++ ] = 'n';
722         }
723         else if ( label[ i ] == *fl_ul_magic_char )
724         {
725             if ( ! obj->shortcut || ! *obj->shortcut )
726             {
727                 tmpstr = fl_realloc( tmpstr, tlen += 3 );
728                 tmpstr[ j++ ] = '\\';
729                 tmpstr[ j++ ] = '0';
730                 tmpstr[ j++ ] = '1';
731                 tmpstr[ j++ ] = '0';
732             }
733         }
734         else if ( c_source && need_quote( label, i ) )
735         {
736             tmpstr = fl_realloc( tmpstr, tlen += 1 );
737             tmpstr[ j++ ] = '\\';
738             tmpstr[ j++ ] = label[ i ];
739         }
740         else
741             tmpstr[ j++ ] = label[ i ];
742     }
743 
744     tmpstr[ j ] = '\0';
745 
746     return tmpstr;
747 }
748 
749 
750 /***************************************
751  * Convert shortcut into string representation.
752  * ESC -> ^[, F1 -> &1 etc.
753  ***************************************/
754 
755 static int
special_key(int key,char * outbuf)756 special_key( int    key,
757              char * outbuf )
758 {
759     char *start = outbuf;
760 
761     if ( key >= XK_F1 && key <= XK_F30 )
762     {
763         int p = ( key - XK_F1 + 1 ) / 10,
764             q = ( key - XK_F1 + 1 ) % 10;
765 
766         *outbuf++ = '&';
767         if ( p )
768             *outbuf++ = '0' + p;
769         *outbuf++ = '0' + q;
770     }
771     else if ( IsUp( key ) )
772     {
773         *outbuf++ = '&';
774         *outbuf++ = 'A';
775     }
776     else if ( IsDown( key ) )
777     {
778         *outbuf++ = '&';
779         *outbuf++ = 'B';
780     }
781     else if ( IsRight( key ) )
782     {
783         *outbuf++ = '&';
784         *outbuf++ = 'C';
785     }
786     else if ( IsLeft( key ) )
787     {
788         *outbuf++ = '&';
789         *outbuf++ = 'D';
790     }
791     else
792         *outbuf++ = key;
793 
794     *outbuf = '\0';
795 
796     return outbuf - start;
797 }
798 
799 
800 /***************************************
801  * Converts the objects shortcut string into a string suitable for
802  * setting as the content of the shortcut input object
803  ***************************************/
804 
805 char *
get_shortcut_string(const FL_OBJECT * obj)806 get_shortcut_string( const FL_OBJECT * obj )
807 {
808     static char tmps[ 127 ];
809     char *p = tmps;
810     long *sc = obj->shortcut;
811     int n;
812 
813     for ( *p = '\0'; sc && *sc; sc++ )
814     {
815         if ( *sc >= FL_ALT_MASK )
816         {
817             *p++ = '#';
818             n = special_key( *sc - FL_ALT_MASK, p );
819             p += n;
820         }
821         else if ( *sc == '#' || *sc == '&' || *sc == '^' )  /* prefixed w/ ^ */
822         {
823             *p++ = '^';
824             *p++ = *sc;
825         }
826         else if ( *sc < 30 )
827         {
828             *p++ = '^';
829             if ( *sc <= 'Z' )
830                 *p++ = 'A' + *sc - 1;
831             else if ( *sc == 27 )   /* Escape */
832                 *p++ = '[';
833         }
834         else if ( *sc > 255 )
835         {
836             n = special_key( *sc, p );
837             p += n;
838         }
839         else
840             *p++ = *sc;
841     }
842 
843     *p = '\0';
844 
845     return tmps;
846 }
847 
848 
849 /***************************************
850  * Callback for fl_enumerate_fonts() used in intializing the form
851  ***************************************/
852 
853 static void
add_font_choice(const char * p)854 add_font_choice( const char * p )
855 {
856     fl_addto_choice( fnts, p );
857 }
858 
859 
860 /***************************************
861  * Callback routine for getting a color from the user
862  ***************************************/
863 
864 void
setcolor_cb(FL_OBJECT * obj,long arg FL_UNUSED_ARG)865 setcolor_cb( FL_OBJECT * obj,
866              long        arg  FL_UNUSED_ARG )
867 {
868     int col1 = fl_show_colormap( obj->col1 );
869 
870     fl_set_object_color( obj, col1, col1 );
871     readback_attributes( curobj );
872 }
873 
874 
875 /***************************************
876  * Validates the name of the object and the callback function
877  ***************************************/
878 
879 static int
validate_attributes(FL_OBJECT * obj)880 validate_attributes( FL_OBJECT * obj )
881 {
882     FL_OBJECT *o;
883     const char *name,
884                *cn;
885 
886     if (    ! validate_cvar_name( fd_generic_attrib->nameobj, "object name" )
887          || ! validate_cvar_name( fd_generic_attrib->cbnameobj, "callback" ) )
888         return 0;
889 
890     name = fl_get_input( fd_generic_attrib->nameobj );
891 
892     if ( ! name || ! *name )
893         return 1;
894 
895     /* Make sure the name diesn't clash with its forms name */
896 
897     if ( ( cn = get_form_name( obj->form ) ) && ! strcmp( name, cn ) )
898     {
899         fl_show_alert( "Error", "Invalid C identifier:",
900                        "Object has same name as the form it belongs to!", 0 );
901         fl_set_focus_object( fd_generic_attrib->nameobj->form,
902                              fd_generic_attrib->nameobj );
903         return 0;
904     }
905 
906     /* Make sure the name doesn clash with the name of another object in
907        the form it belongs to */
908 
909     for ( o = obj->form->first; o; o = o->next )
910         if (    o != obj
911              && ( cn = get_object_c_name( o ) )
912              && ! strcmp( name, cn ) )
913         {
914             fl_show_alert( "Error", "Invalid C identifier:",
915                            "Object has sam name as another one!", 0 );
916             fl_set_focus_object( fd_generic_attrib->nameobj->form,
917                                  fd_generic_attrib->nameobj );
918             return 0;
919         }
920 
921 	return 1;
922 }
923 
924 
925 /***************************************
926  ***************************************/
927 
928 void
validate_cvar_name_cb(FL_OBJECT * obj,long data)929 validate_cvar_name_cb( FL_OBJECT * obj,
930                        long        data )
931 {
932     validate_cvar_name( obj, data == 0 ? "object name" : "callback" );
933 }
934 
935 
936 /***************************************
937  * Checks if the string of an input field  is a valid C indentifier
938  ***************************************/
939 
940 static int
validate_cvar_name(FL_OBJECT * obj,const char * w)941 validate_cvar_name( FL_OBJECT  * obj,
942                     const char * w )
943 {
944     const char *s = fl_get_input( obj );
945 
946     if ( valid_c_identifier( s ) )
947 		return 1;
948 
949 	/* If something's wrong switch to the form with the generic attributs,
950 	   since there's were the input fields with the name and the callback
951 	   function are */
952 
953 	fl_set_folder_bynumber( fd_attrib->attrib_folder, 1 );
954 
955 	if ( ! w || ! *w )
956         fl_show_alert_f( 0, "Error\fInvalid C identifier:\n'%s'", s );
957     else
958         fl_show_alert_f( 0, "Error\fInvalid C identifier\nfor %s: '%s'", w, s );
959 
960 	fl_set_focus_object( obj->form, obj );
961 	return 0;
962 }
963 
964 
965 /* Check for obvious errors */
966 
967 #define OK_letter( c )    (    *c == '_'                       \
968                             || *c == '['                       \
969                             || *c == ']'                       \
970                             || * c== '.'                       \
971                             || ( *c == ':' && *++c == ':' )    \
972                             || ( *c == '-' && *++c == '>' ) )
973 
974 
975 /***************************************
976  ***************************************/
977 
978 static int
valid_c_identifier(const char * s)979 valid_c_identifier( const char * s )
980 {
981     if ( fdopt.lax )
982         return 1;
983 
984     /* Empty is considered to be valid */
985 
986     if ( ! s || ! *s || ( *s == ' ' && *( s + 1 ) == '\0' ) )
987         return 1;
988 
989     if ( ! isalpha( ( unsigned char ) *s ) && *s != '_' )
990         return 0;
991 
992     for ( s++; *s; s++ )
993         if ( ! isalnum( ( unsigned char ) *s ) && ! OK_letter( s ) )
994             return 0;
995 
996     return 1;
997 }
998 
999 
1000 /***************************************
1001  ***************************************/
1002 
1003 static void
copy_shortcut(FL_OBJECT * dest,FL_OBJECT * src)1004 copy_shortcut( FL_OBJECT * dest,
1005 			   FL_OBJECT * src )
1006 {
1007 	if ( src->shortcut )
1008     {
1009         size_t i = 0;
1010 
1011         while ( src->shortcut[ i++ ] )
1012             /* empty */ ;
1013 
1014         if ( i )
1015         {
1016             dest->shortcut = malloc( i * sizeof *dest->shortcut );
1017             memcpy( dest->shortcut, src->shortcut, i * sizeof *dest->shortcut );
1018         }
1019     }
1020 	else
1021 		dest->shortcut = NULL;
1022 }
1023 
1024 
1025 /*
1026  * Local variables:
1027  * tab-width: 4
1028  * indent-tabs-mode: nil
1029  * End:
1030  */
1031