File: part2_saving_loading.texi

package info (click to toggle)
libforms 1.0.93sp1-1
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 11,548 kB
  • ctags: 9,107
  • sloc: ansic: 97,227; sh: 9,236; makefile: 858
file content (326 lines) | stat: -rw-r--r-- 10,226 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
@node Part II Saving and Loading Forms
@chapter Saving and Loading Forms

 To save the set of forms created select the item "Save" or "Save As"
from the "File" menu. You will be prompted for a file name using the
file selector if the latter is selected. Choose a name that ends with
@code{.fd}, e.g. @file{ttt.fd}.

The program will now generate three files: @file{ttt.c}, @file{ttt.h}
and @file{ttt.fd}. If these files already exist, backup copies of
these are made (by appending @code{.bak} to the already existing file
names). @file{ttt.c} contains a piece of C-code that builds up the
forms and @file{ttt.h} contains all the object and form names as
indicated by the user. It also contains declaration of the defined
callback routines.

Depending on the options selected from the "Options" menu, two more
files may be emitted. Namely the main program and callback function
templates. They are named @file{ttt_main.c} and @file{ttt_cb.c}
respectively.

There are two different kind of formats for the C-code generated. The
default format allows more than one instance of the form created and
uses no global variables. The other format, activated by the
@code{altformat} option given on the command line or switched on via
the "Options" menu by selecting "Alt Format", uses global variables
and does not allow more than one instantiation of the designed forms.
However, this format has a global routine that creates all the forms
defined, which by default is named @code{create_the_forms()} but that
can be changed (see below).

Depending on which format is output, the application program typically
only needs to include the header file and call the form creation
routine.

To illustrate the differences between the two output formats and the
typical way an application program is setup, we look at the following
hypothetical situation: We have two forms, @code{foo} and @code{bar},
each of which contains several objects, say @code{fnobj1},
@code{fnobj2} etc. where @code{n = 1, 2}. The default output format
will generate the following header file (@file{foobar.h}):
@example
#ifndef FD_foobar_h_
#define FD_foobar_h_

/* call back routines if any */

extern void callback(FL_OBJECT *, long);

typedef struct @{
    FL_FORM *   foo;
    void *      vdata;
    char *      cdata;
    long        ldata;
    FL_OBJECT * f1obj1;
    FL_OBJECT * f1obj2;
@} FD_foo;

typedef struct @{
    FL_FORM *   bar;
    void *      vdata;
    char *      cdata;
    long        ldata;
    FL_OBJECT * f2obj1;
    FL_OBJECT * f2obj2;
@} FD_bar;

extern FD_foo *create_form_foo(void);
extern FD_bar *create_form_bar(void);

#endif /* FD_foobar_h */
@end example
@noindent
 and the corresponding C file:
@example
#include <forms.h>
#include "foobar.h"

FD_foo *create_form_foo(void) @{
    FD_foo *fdui = fl_calloc(1, sizeof *dhui);

    fdui->foo = fl_bgn_form(....);
    fdui->f1obj1 = fl_add_aaaa(....);
    fdui->f1obj1 = fl_add_bbbb(....);
    fl_end_form();

    fdui->foo->fdui = fdui;
    return fdui;
@}

FD_bar *create_form_foo(void) @{
    FD_bar *fdui = fl_calloc(1, sizeof *fdui);

    fdui->bar = fl_bgn_form(....);
    fdui->f2obj1 = fl_add_cccc(....);
    fdui->f2obj2 = fl_add_dddd(....);
    fl_end_form();

    fdui->bar->fdui = fdui;
    return fdui;
@}
@end example

The application program would look something like the following:
@example
#include <forms.h>
#include "foobar.h"

/* add call back routines here */

int main(int argc, char *argv[]) @{
    FD_foo *fd_foo;
    FD_bar *fd_bar;

    fl_initialize(...);
    fd_foo = create_form_foo();
    init_fd_foo(fd_foo);  /* application UI init routine */

    fd_bar = create_form_bar();
    init_fd_bar(fd_bar)   /* application UI init routine */

    fl_show_form(fd_foo->foo, ...);

    /* rest of the program */
@}
@end example

As you see, @code{fdesign} generates a structure that groups together
all objects on a particular form and the form itself into a structure
for easy maintenance and access. The other benefit of doing this is
that the application program can create more than one instances of the
form if needed.

It is difficult to avoid globals in an event-driven callback scheme
with the most difficulties occurring inside the callback function
where another object on the same form may need to be accessed. The
current setup makes this possible and relatively painless to achieve
this.

There are a couple of ways to do this. The easiest and most robust way
is to use the member @code{form->fdui}, which fdesign set up to point
to the @code{FD_} structure in which the form is a member. To
illustrate how this is done, let's take the above two forms and try to
access a different object from within a callback function.
@example
fd_foo = create_form_foo();
...
@end example
@noindent
and in the callback function of @code{ob} on form @code{foo}, you can
access other objects as follows:
@example
void callback(FL_OBJECT *obj, long data) @{
    FD_foo *fd_foo = obj->form->fdui;
    fl_set_object_dddd(fd_foo->f1obj2, ....);
@}
@end example

Of course this setup still leaves the problems accessing objects on
other forms unsolved although you can manually set the @code{form->u_vdata}
to the other @code{FD_} structure:
@example
fd_foo->form->u_vdata = fd_bar;
@end example
@noindent
or use the @code{vdata} field in the @code{FD_} structure itself:
@example
fd_foo->vdata = fd_bar;
@end example

The other method, not as easy as using @code{form->fdui} (because you
get no help from fdesign), but just as workable, is simply use the
@code{u_vdata} field in the @code{FD_} structure to hold the ID of the
object that needs to be accessed. In case of the need to access
multiple objects, there is a field @code{u_vdata} in both the
@code{FL_FORM} and @code{FL_OBJECT} structures you can use. You simply
use the field to hold the @code{FD_} structure:
@example
fd_foo = create_form_foo();
fd_foo->foo->u_vdata = fd_foo;
...
@end example
@noindent
and in the callback function you can access other objects as follows:
@example
void callback(FL_OBJECT *obj, long data) @{
    FD_foo *fd_foo = obj->form->u_vdata;
    fl_set_object_dddd(fd_foo->f1obj2, ....);
@}
@end example

Not pretty, but adequate for practical purposes. Note that the
@code{FD_} structure always has a pointer to the form as the first
member, followed by @code{vdata}, @code{cdata} and @code{ldata}.
There's also a @code{typedef} for a structure of type @code{FD_Any}
in @code{forms.h}:
@example
typedef struct @{
    FL_FORM * form;
     void *   vdata;
     char *   cdata;
     long     ldata;
@} FD_Any;
@end example
@noindent
you can use to cast a specific @code{FD_} structure get at @code{vdata}
etc. Another alternative is to use the @code{FD_} structure as the
user data in the callback@footnote{Unfortunately, this scheme isn't
legal C as a pointer may be longer than a long, but in practice, it
should work out ok on virtually all platforms.}
@example
fl_set_object_callback(obj, callback, (long) fdui);
@end example
@noindent
and use the callback as follows
@example
void callback(FL_OBJECT *obj, long arg) @{
    FD_foo *fd_foo = (FD_foo *) arg;
    fl_set_object_lcol(fd + foo->f1obj1, FL_RED);
    ...
@}
@end example

Avoiding globals is, in general, a good idea, but as everything else,
also an excess of a good things can be bad. Sometimes, simply making
the @code{FD_} structure global makes a program clearer and more
maintainable.

There still is another difficulty that might arise with the current
setup. For example, in @code{f1obj1}'s callback we change the state of
some other objects , say, @code{f1obj2} via
@code{@ref{fl_set_button()}} or @code{@ref{fl_set_input()}}. Now the
state of @code{f1obj2} is changed and it needs to be handled. You
probably don't want to put much code for handling @code{f1obj2} in
@code{f1obj1}'s callback. In this situation, the following function
comes in handy
@example
void fl_call_object_callback(FL_OBJECT *obj);
@end example
@noindent
@code{fl_call_object_callback(fdfoo->f1obj2)} will invoke the callback
for @code{f1obj2} callback in exactly the same way the main loop would
do and as far as @code{f1obj2} is concerned, it just handles the state
change as if the user changed it.

The alternative format outputs something like the following:
@example
/* callback routines */
extern void callback(FL_OBJECT *, long);

extern FL_FORM *foo,
               *bar;
 extern FL_OBJECT *f1obj1,
                  *f1obj2,
                  ...;
extern FL_OBJECT *f2obj1,
                 *f2obj2,
                 ...;

extern void create_form_foo(void);
extern create_form_bar(void);
extern void create_the_forms(void);
@end example

The C-routines:
@example
FL_FORM *foo,
        *bar;

FL_OBJECT *f1obj1,
          *f1obj2,
          ...;
FL_OBJECT *f2obj1,
          *f2obj2,
          ...;

void create_form_foo(void) @{
    if (foo)
        return;
    foo = fl_bgn_form(....);
    ...
@}

void create_form_bar(void) @{
    if (bar)
        return;
    bar = fl_bgn_form(....);
    ...
@}

void create_the_forms(void) @{
    create_form_foo();
    create_form_bar();
@}
@end example

Normally the application program would look something like this:
@example
#include <forms.h>
#include "foobar.h"

/* Here go the callback routines */
....

int main(int argc, char *argv[]) @{
    fl_initialize(....);
    create_the_forms();
    /* rest of the program follows*/
    ...
@}
@end example

Note that although the C-routine file in both cases is easily
readable, editing it is strongly discouraged. If you were to do so,
you will have to redo the changes whenever you call fdesign again to
modify the layout.

The third file created, @file{ttt.fd}, is in a format that can be read
in by the Form Designer. It is easy readable ASCII but you had better
not change it because not much error checking is done when reading it
in. To load such a file select the "Open" item from the "File" menu.
You will be prompted for a file name using the file selector. Press
your mouse on the file you want to load and press the button labeled
"Ready". The current set of forms will be discarded, and replaced by
the new set. You can also merge the forms in a file with the current
set. To this end select "Merge" from the "File" menu.