File: part1_free_objects.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 (753 lines) | stat: -rw-r--r-- 29,556 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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
@node Part I Free Objects
@chapter Free Objects

In some applications the standard object classes as provided by the
Forms Library may not be enough for your task. There are three ways of
solving this problem. First of all, the application program can also
open its own window or use a canvas (the preferred way) in which it
does interaction with the user). A second way is to add your own
object classes (see Part IV). This is especially useful when your new
type of objects is of general use.

The third way is to add free objects to your form. Free objects are
objects for which the application program handles the drawing and
interaction. This chapter will give all the details needed to design
and use free objects.


@ifnottex

@menu
* Free Object::        Free Object
* Example::            An Example
@end menu

@end ifnottex


@node Free Object
@section Free Object

To add a free object to a form use the call
@findex fl_add_free()
@anchor{fl_add_free()}
@example
FL_OBJECT *fl_add_free(int type, FL_Coord x, FL_Coord y,
                       FL_Coord w, FL_Coord h,
                       const char *label, int (*handle)());
@end example
@noindent
@code{type} indicates the type of free object, see below for a list
and their meaning. @code{x}, @code{y}, @code{w} and @code{h} are the
bounding box. The @code{label} is normally not drawn unless the
@code{handle} routine takes care of this. @code{handle} is the routine
that does the redrawing and handles the interaction with the free
object. The application program must supply this routine.

This routine @code{handle} is called by the library whenever
an action has to be performed. The routine should have the form:
@example
int handle(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my,
           int key, void *xev);
@end example
@noindent
where @code{obj} is the object to which the event applies.
@code{event} indicates what has to happen to the object. See below for
a list of possible events. @code{mx} and @code{my} indicate the
position of the mouse (only meaningful with mouse related events)
relative to the form origin and @code{key} is the KeySym of the key
typed in by the user (only for @code{FL_KEYPRESS} events). @code{xev}
is the (cast) XEvent that causes the invocation of this handler.
@code{event} and @code{xev->type} can both be used to obtain the event
types. The routine should return whether the status of the object has
changed, i.e., whether @code{@ref{fl_do_forms()}} or
@code{@ref{fl_check_forms()}} should return this object.

The following types of events exist for which the routine must take action:
@table @code
@tindex FL_DRAW
@item FL_DRAW
The object has to be redrawn. To figure out the size of the object you
can use the fields @code{obj->x}, @code{obj->y}, @code{obj->w} and
@code{obj->h}. Some other aspects might also influence the way the
object has to be drawn. E.g., you might want to draw the object
differently when the mouse is on top of it or when the mouse is
pressed on it. This can be figured out as follows. The field
@code{obj->belowmouse} indicates whether the object is below the
mouse. The field @code{obj->pushed} indicates whether the object is
currently being pushed with the mouse. Finally, @code{obj->focus}
indicates whether input focus is directed towards this object. When
required, the label should also be drawn. This label can be found in
the field @code{obj->label}. The drawing should be done such that it
works correctly in the visual/depth the current form is in. Complete
information is available on the state of the current form as well as
several routines that will help you to tackle the trickiest (also the
most tedious) part of X programming. In particular, the return value
of @code{@ref{fl_get_vclass()}} can be used as an index into a table
of structures, @code{FL_STATE fl_state[]}, from which all information
about current active visual can be obtained. @xref{Part IV Drawing
Objects, , Drawing Objects}, for details on drawing objects and the
routines.

@tindex FL_DRAWLABEL
@item FL_DRAWLABEL
This event is not always generated. It typically follows
@code{FL_DRAW} and indicates the object label needs to be (re)drawn.
You can ignore this event if (a) the object handler always draws the
label upon receiving @code{FL_DRAW} or (b) the object label is not
drawn at all@footnote{Label for free objects can't be drawn outside of
the bounding box because of the clippings by the dispatcher.}.

@tindex FL_ENTER
@item FL_ENTER
This event is sent when the mouse has entered the bounding box. This
might require some action. Note that also the
field @code{belowmouse} in the object is being set. If
entering only changes the appearance redrawing the object normally
suffices. Don't do this directly! Always redraw the object using the
routine @code{fl_redraw_object()}. It will
send an @code{FL_DRAW} event to the object but also does
some other things (like setting window id's, taking care of double
buffering and some other bookkeeping tasks).

@tindex FL_LEAVE
@item FL_LEAVE
The mouse has left the bounding box. Again, normally a redraw is
enough (or nothing at all).

@tindex FL_MOTION
@item FL_MOTION
A motion event is sent between @code{FL_ENTER} and @code{FL_LEAVE}
events when the mouse position changes on the object. The mouse
position is given with the routine.

@tindex FL_PUSH
@item FL_PUSH
The user has pushed a mouse button in the object. Normally this
requires some action.

@tindex FL_RELEASE
@item FL_RELEASE
The user has released the mouse button. This event is only sent if a
@code{FL_PUSH} event was sent earlier.

@tindex FL_DBLCLICK
@item FL_DBLCLICK
The user has pushed a mouse button twice within a certain time limit
(@code{FL_CLICK_TIMEOUT}), which by default is about @w{400 msec}.

@tindex FL_TRPLCLICK
@item FL_TRPLCLICK
The user has pushed a mouse button three times within a certain time
window between each push. This event is sent after a
@code{FL_DBLCLICK}, @code{FL_PUSH}, @code{FL_RELEASE} sequence.

@tindex FL_UPDATE
@item FL_UPDATE
The mouse position has changed. This event is sent to an object
between an @code{FL_PUSH} and an @code{FL_RELEASE} event (actually
this event is sent periodically, even if mouse has not moved). The
mouse position is given as the parameter @code{mx} and @code{my} and
action can be taken based on the position.

@tindex FL_FOCUS
@item FL_FOCUS
Input got focussed to this object. This event and the next two are
only sent to a free object of type @code{FL_INPUT_FREE} (see below).

@tindex FL_UNFOCUS
@item FL_UNFOCUS
Input is no longer focussed on this object.

@tindex FL_KEYPRESS
@item FL_KEYPRESS
A key was pressed. The KeySym is given with the routine. This event
only happens between @code{FL_FOCUS} and @code{FL_UNFOCUS} events.

@tindex FL_STEP
@item FL_STEP
A step event is sent all the time (at most 50 times per second but
often less because of time consuming redraw operations) to a free
object of type @code{FL_CONTINUOUS_FREE} such that it can update its
state or appearance.

@tindex FL_SHORTCUT
@item FL_SHORTCUT
Hotkeys for the object have been triggered. Typically this should
result in the returning of the free object.

@tindex FL_FREEMEM
@item FL_FREEMEM
Upon receiving this event, the handler should free all object class
specific memory allocated.

@tindex FL_OTHER
@item FL_OTHER
Some other events typically caused by window manager events or
inter-client events. All information regarding the details of the
events is in @code{xev}.
@end table
@noindent
Many of these events might make it necessary to (partially) redraw the
object. Always do this using the routine
@code{@ref{fl_redraw_object()}}.

As indicated above not all events are sent to all free objects. It
depends on their types. The following types exist (all objects are sent
@code{FL_OTHER} when it occurs):
@table @code
@tindex FL_NORMAL_FREE
@item FL_NORMAL_FREE
The object will receive the events @code{FL_DRAW}, @code{FL_ENTER},
@code{FL_LEAVE}, @code{FL_MOTION}, @code{FL_PUSH}, @code{FL_RELEASE}
and @code{FL_MOUSE}.
@tindex FL_INACTIVE_FREE
@item FL_INACTIVE_FREE
The object only receives @code{FL_DRAW} events. This should be used
for objects without interaction (e.g. a picture).
@tindex FL_INPUT_FREE
@item FL_INPUT_FREE
Same as @code{FL_NORMAL_FREE} but the object also receives
@code{FL_FOCUS}, @code{FL_UNFOCUS} and @code{FL_KEYPRESS} events. The
@code{obj->wantkey} is by default set to @code{FL_KEY_NORMAL}, i.e.,
the free object will receive all normal keys (0-255) except
@code{<Tab>} and @code{<Return>} key. If you're interested in
@code{<Tab>} or @code{<Return>} key, you need to change
@code{obj->wantkey} to @code{FL_KEY_TAB} or @code{FL_KEY_ALL}.
@xref{Part IV Events, , Events}, for details.
@tindex FL_CONTINUOUS_FREE
@item FL_CONTINUOUS_FREE
Same as @code{FL_NORMAL_FREE} but the object also receives
@code{FL_STEP} events. This should be used for objects that change
themselves continuously.
@tindex FL_ALL_FREE
@item FL_ALL_FREE
The object receives all types of events.
@end table

See @file{free1.c} for a (terrible) example of the use of free
objects. See also @file{freedraw.c}, which is a nicer example of the
use of free objects.

Free objects provide all the generality you want from the Forms Library.
Because free objects behave a lot like new object classes it is
recommended that you also read part IV of this documentation before
designing free objects.


@node Example
@section An Example

We conclude our discussion of the free object by examining a simple
drawing program capable of drawing simple geometric figures like
squares, circles, and triangles of various colors and sizes, and of
course it also utilizes a free object.

The basic UI consists of three logical parts. A drawing area onto which
the squares etc. are to be drawn; a group of objects that control what
figure to draw and with what size; and a group of objects that control
the color with which the figure is to be drawn.

The entire UI is designed interactively using the GUI builder
@code{fdesign} with most objects having their own callbacks.
@code{fdesign} writes two files, one is a header file containing
forward declarations of callback functions and other function
prototypes:
@example
#ifndef FD_drawfree_h_
#define FD_drawfree_h_

extern void change_color(FL_OBJECT *, long);
extern void switch_figure(FL_OBJECT *, long);

/* more callback declarations omitted */

typedef struct @{
    FL_FORM   * drawfree;
    FL_OBJECT * freeobj;
    FL_OBJECT * figgrp;
    FL_OBJECT * colgrp;
    FL_OBJECT * colorobj;
    FL_OBJECT * miscgrp;
    FL_OBJECT * sizegrp;
    FL_OBJECT * wsli;
    FL_OBJECT * hsli;
    FL_OBJECT * drobj[3];
    void      * vdata;
    long        ldata;
@} FD_drawfree;

extern FD_drawfree *create_form_drawfree(void);
#endif /* FD_drawfree_h_ */
@end example

The other file contains the actual C-code that creates the form when
compiled and executed. Since free objects are not directly supported by
fdesign, a box was used as a stub for the location and size of the
drawing area. After the C-code was generated, the box was changed
manually to a free object by replacing
@code{fl_add_box(FL_DOWN_BOX,...)} with
@code{fl_add_free(FL_NORMAL_FREE,...)}. We list below the output
generated by fdesign with some comments:
@example
FD_drawfree *create_form_drawfree(void) @{
    FL_OBJECT *obj;
    FD_drawfree *fdui = fl_calloc(1, sizeof *fdui);

    fdui->drawfree = fl_bgn_form(FL_NO_BOX, 530, 490);
    obj = fl_add_box(FL_UP_BOX, 0, 0, 530, 490, "");
@end example
@noindent
This is almost always the same for any form definition: we allocate a
structure that will hold all objects on the form as well as the form
itself. In this case, the first object on the form is a box of type
@code{FL_UP_BOX}.

@example
    fdui->figgrp = fl_bgn_group();

    obj = fl_add_button(FL_RADIO_BUTTON, 10, 60, 40, 40,
                        "@@#circle");
    fl_set_object_lcol(obj,FL_YELLOW);
    fl_set_object_callback(obj, switch_figure, 0);

    obj = fl_add_button(FL_RADIO_BUTTON, 50, 60, 40, 40,
                        "@@#square");
    fl_set_object_lcol(obj, FL_YELLOW);
    fl_set_object_callback(obj, switch_figure, 1);

    obj = fl_add_button(FL_RADIO_BUTTON, 90, 60, 40, 40,
                        "@@#8*>");
    fl_set_object_lcol(obj, FL_YELLOW);
    fl_set_object_callback(obj, switch_figure, 2);

    fl_end_group();
@end example

This creates three buttons that control what figures are to be drawn.
Since figure selection is mutually exclusive, we use
@code{RADIO_BUTTON} for this. Further, the three buttons are placed
inside a group so that they won't interfere with other radio buttons
on the same form. Notice that the callback function
@code{switch_figure()} is bound to all three buttons but with
different arguments. Thus the callback function can resolve the
associated object via the callback function argument. In this case, 0
is used for circle, 1 for square and 2 for triangle. This association
of a callback function with a piece of user data can often reduce the
amount of code substantially, especially if you have a large group of
objects that control similar things. The advantage will become clear
as we proceed.

Next we add three sliders to the form. By using appropriate colors for
these sliding bars (red, green, blue), there is no need to label them.
There's also no need to store their addresses as their callback routine
@code{change_color()} will receive them automatically.
@example
   fdui->colgrp = fl_bgn_group();

   obj = fl_add_slider(FL_VERT_FILL_SLIDER, 25, 170, 30, 125, "");
   fl_set_object_color(obj, FL_COL1, FL_RED);
   fl_set_object_callback(obj, change_color, 0);

   obj = fl_add_slider(FL_VERT_FILL_SLIDER, 55, 170, 30, 125, "");
   fl_set_object_color(obj, FL_COL1, FL_GREEN);
   fl_set_object_callback(obj, change_color, 1);

    obj = fl_add_slider(FL_VERT_FILL_SLIDER, 85, 170, 30, 125, "");
    fl_set_object_color(obj, FL_COL1, FL_BLUE);
    fl_set_object_callback(obj, change_color, 2);

    fdui->colorobj = obj = fl_add_box(FL_BORDER_BOX,
                                      25, 140, 90, 25, "");
    fl_set_object_color(obj, FL_FREE_COL1, FL_FREE_COL1);

    fl_end_group();
@end example

Again, a single callback function, @code{change_color()}, is bound to
all three sliders. In addition to the sliders, a box object is added
to the form. This box is set to use the color indexed by
@code{FL_FREE_COL1} and will be used to show visually what the current
color setting looks like. This implies that in the
@code{change_color()} callback function, the entry @code{FL_FREE_COL1}
in the Forms Library's internal colormap will be changed. We also
place all the color related objects inside a group even though they
are not of radio buttons. This is to facilitate gravity settings which
otherwise require setting the gravities of each individual object.

Next we create our drawing area which is simply a free object of type
@code{NORMAL_FREE} with a handler to be written
@example
    obj = fl_add_frame(FL_DOWN_FRAME, 145, 30, 370, 405, "");
    fl_set_object_gravity(obj, FL_NorthWest, FL_SouthEast);

    fdui->freeobj = obj = fl_add_free(FL_NORMAL_FREE,
                                      145, 30, 370, 405, "",
                                      freeobject_handler);
    fl_set_object_boxtype(obj, FL_FLAT_BOX);
    fl_set_object_gravity(obj, FL_NorthWest, FL_SouthEast);
@end example

The frame is added for decoration purposes only. Although a free
object with a down box would appear the same, the down box can be
written over by the free object drawing while the free object can't
draw on top of the frame since the frame is outside of the free
object. Notice the gravity settings. This kind of setting maximizes
the real estate of the free object when the form is resized.

Next, we need to have control over the size of the object. For this,
two sliders are added, using the same callback function but with
different user data (0 and 1 in this case):
@example
    fdui->sizegrp = fl_bgn_group();

    fdui->wsli = obj = fl_add_valslider(FL_HOR_SLIDER,
                                        15, 370, 120, 25, "Width");
    fl_set_object_lalign(obj, FL_ALIGN_TOP);
    fl_set_object_callback(obj, change_size, 0);

    fdui->hsli = obj = fl_add_valslider(FL_HOR_SLIDER,
                                        15, 55, 410,25, "Height");
    fl_set_object_lalign(obj, FL_ALIGN_TOP);
    fl_set_object_callback(obj, change_size, 1);

    fl_end_group();
@end example

The rest of the UI consists of some buttons the user can use to exit
the program, elect to draw outlined instead of filled figures etc. The
form definition ends with @code{@ref{fl_end_form()}}. The structure
that holds the form as well as all the objects within it is returned
to the caller:
@example
    fdui->miscgrp = fl_bgn_group();

    obj = fl_add_button(FL_NORMAL_BUTTON, 395, 445, 105, 30,
                        "Quit");
    fl_set_button_shortcut(obj, "Qq#q", 1);

    obj = fl_add_button(FL_NORMAL_BUTTON, 280, 445, 105, 30,
                        "Refresh");
    fl_set_object_callback(obj, refresh_cb, 0);
    
    obj = fl_add_button(FL_NORMAL_BUTTON, 165, 445, 105, 30,
                        "Clear");
    fl_set_object_callback(obj,clear_cb,0); fl_end_group();

    obj = fl_add_checkbutton(FL_PUSH_BUTTON, 15, 25, 100, 35,
                             "Outline");
    fl_set_object_color(obj, FL_MCOL, FL_BLUE);
    fl_set_object_callback(obj, fill_cb, 0);
    fl_set_object_gravity(obj, FL_NorthWest, FL_NorthWest);

    fl_end_form();

    return fdui;
@}
@end example

After creating the UI we need to write the callback functions and the
free object handler. The callback functions are relatively easy since
each object is designed to perform a very specific task.

Before we proceed to code the callback functions we first need to
define the overall data structure that will be used to glue together
the UI and the routines that do real work.

The basic structure is the DrawFigure structure that holds the current
drawing function as well as object attributes such as size and color:
@example
#define MAX_FIGURES 500

typedef void (*DrawFunc)(int                  /* fill */,
                         int, int, int, int,  /* x,y,w,h */
                         FL_COLOR             /* color */ );

typedef struct @{
    DrawFunc drawit;      /* how to draw this figure */
    int      fill,        /* is it to be filled? */
             x, y, w, h;  /* position and sizes */
    int      pc[3];       /* primary color R,G,B */
    int      newfig;      /* indicate a new figure */
    FL_COLOR col;         /* color index */
@} DrawFigure;

static DrawFigure saved_figure[MAX_FIGURES],
                  *cur_fig;
static FD_drawfree *drawui;
int max_w = 30,               /* max size of figures */
    max_h = 30;
@end example
@noindent
All changes to the figure attributes will be buffered in
@code{cur_fig} and when the actual drawing command is issued (mouse
click inside the free object), @code{cur_fig} is copied into
@code{saved_figure} array buffer.

Forms Library contains some low-level drawing routines that can draw and
optionally fill arbitrary polygonal regions, so in principle, there is
no need to use Xlib calls directly. To show how Xlib drawing routines are
combined with Forms Library, we use Xlib routines to draw a triangle:
@example
void draw_triangle(int fill, int x, int y,
                   int w, int h, FL_COLOR col) @{
    XPoint xp[4];
    GC gc = fl_state[fl_get_vclass()].gc[0];
    Window win = fl_winget();
    Display *disp = fl_get_display();

    xp[0].x = x;
    xp[0].y = y + h - 1;
    xp[1].x = x + w / 2;
    xp[1].y = y;
    xp[2].x = x + w - 1;
    xp[2].y = y + h - 1;
    XSetForeground(disp, gc, fl_get_pixel(col));

    if (fill)
        XFillPolygon(disp, win, gc, xp, 3, Nonconvex, Unsorted);
    else @{
        xp[3].x = xp[0].x;
        xp[3].y = xp[0].y;
        XDrawLines(disp, win, gc, xp, 4, CoordModeOrigin);
    @}
@}
@end example
@noindent
Although more or less standard stuff, some explanation is in order. As
you have probably guessed, @code{@ref{fl_winget()}} returns the
current "active" window, defined to be the window the object receiving
dispatcher's messages (@code{FL_DRAW} e.g.) belongs to@footnote{If
@code{@ref{fl_winget()}} is called while not handling messages, the
return value must be checked.}. Similarly the routine
@code{@ref{fl_get_display()}} returns the current connection to the X
server. Part IV has more details on the utility functions in the Forms
Library.

The structure @code{fl_state[]} keeps much "inside" information on the
state of the Forms Library. For simplicity, we choose to use the Forms
Library's default GC. There is no fundamental reason that this has be
so. We certainly can copy the default GC and change the foreground
color in the copy. Of course unlike using the default GC directly, we
might have to set the clip mask in the copy whereas the default GC
always have the proper clip mask (in this case, to the bounding box of
the free object).

We use the Forms Library's built-in drawing routines to draw circles
and rectangles. Then our drawing functions can be defined as follows:
@example
static DrawFunc drawfunc[] = @{
    fl_oval, fl_rectangle, draw_triangle @};
@end example

Switching what figure to draw is just changing the member
@code{drawit} in @code{cur_fig}. By using the proper object callback
argument, figure switching is achieved by the following callback
routine that is bound to all figure buttons
@example
void switch_object(FL_OBJECT *obj, long which) @{
    cur_fig->drawit = drawfunc[which];
@}
@end example

So this takes care of the drawing functions. Similarly, the color
callback function can be written as follows
@example
void change_color(FL_OBJECT *obj, long which) @{
    cur_fig->c[which] = 255 * fl_get_slider_value(obj);
    fl_mapcolor(cur_fig->col,
                cur_fig->c[0], cur_fig->c[1], cur_fig->c[2]);
    fl_mapcolor(FL_FREE_COL1,
                cur_fig->c[0], cur_fig->c[1], cur_fig->c[2]);
    fl_redraw_object(drawui->colorobj);
@}
@end example
@noindent
The first call of @code{@ref{fl_mapcolor()}} defines the RGB
components for index @code{cur_fig->col} and the second
@code{@ref{fl_mapcolor()}} call defines the RGB component for index
@code{FL_FREE_COL1}, which is the color index used by @code{colorobj}
that serves as current color visual feedback.

Object size is taken care of in a similar fashion by using a callback
function bound to both size sliders:
@example
void change_size(FL_OBJECT * obj, long which) @{
    if (which == 0)
        cur_fig->w = fl_get_slider_value(obj);
    else
        cur_fig->h = fl_get_slider_value(obj);
@}
@end example

Lastly, we toggle the fill/outline option by querying the state of the
push button
@example
void outline_callback(FL_OBJECT *obj, long data) @{
    cur_fig->fill = !fl_get_button(obj);
@}
@end example

To clear the drawing area and delete all saved figures, a Clear button
is provided with the following callback:
@example
void clear_cb(FL_OBJECT *obj, long notused) @{
    saved_figure[0] = *cur_fig;  /* copy attributes */
    cur_fig = saved_figure;
    fl_redraw_object(drawui->freeobj);
@}
@end example

To clear the drawing area and redraw all saved figures, a Refresh
button is provided with the following callback:
@example
void refresh_cb(FL_OBJECT *obj, long notused) @{
    fl_redraw_object(drawui->freeobj);
@}
@end example

With all attributes and other services taken care of, it is time to
write the free object handler. The user can issue a drawing command
inside the free object by clicking either the left or right mouse
button.
@example
int freeobject_handler(FL_OBJECT *obj, int event,
                       FL_Coord mx, FL_Coord my,
                       int key, void *xev) @{
    DrawFigure *dr;

    switch (event) @{
        case FL_DRAW:
            if (cur_fig->newfig == 1)
                 cur_fig->drawit(cur_fig->fill,
                                 cur_fig->x + obj->x,
                                 cur_fig->y + obj->y,
                                 cur_fig->w, cur_fig->h,
                                 cur_fig->col);
            else @{
                fl_drw_box(obj->boxtype, obj->x, obj->y, obj->w,
                           obj->h, obj->col1, obj->bw);
            
                for (dr = saved_figure; dr < cur_fig; dr++) @{
                    fl_mapcolor(FL_FREE_COL1,
                                dr->c[0], dr->c[1], dr->c[2]);
                    dr->drawit(dr->fill,dr->x + obj->x,
                               dr->y + obj->y,
                               dr->w, dr->h, dr->col);
                @}
            @}
            cur_fig->newfig = 0;
            break;

        case FL_PUSH:
            if (key == FL_MIDDLE_MOUSE)
                break;

            cur_fig->x = mx - cur_fig->w / 2;
            cur_fig->y = my - cur_fig->h / 2;

            /* convert figure center to relative to the object*/
            cur_fig->x -= obj->x;
            cur_fig->y -= obj->y;

            cur_fig->newfig = 1;
            fl_redraw_object(obj);
            *(cur_fig + 1) = *cur_fig;
            fl_mapcolor(cur_fig->col + 1, cur_fig->c[0],
                        cur_fig->c[1], cur_fig->c[2] );
            cur_fig++;
            cur_fig->col++;
            break;
    @}

    return FL_RETURN_NONE;
@}
@end example

In this particular program, we are only interested in mouse clicks and
redraw. The event dispatching routine cooks the X event and drives the
handler via a set of events (messages). For a mouse click inside the
free object, its handler is notified with an FL_PUSH together with the
current mouse position mx, my. In addition, the driver also sets the
clipping mask to the bounding box of the free object prior to sending
@code{FL_DRAW}. Mouse position (always relative to the origin of the
form) is directly usable in the drawing function. However, it is a
good idea to convert the mouse position so it is relative to the
origin of the free object if the position is to be used later. The
reason for this is that the free object can be resized or moved in
ways unknown to the handler and only the position relative to the free
object is meaningful in these situations.

It is tempting to call the drawing function in response to
@code{FL_PUSH} since it is @code{FL_PUSH} that triggers the drawing.
However, it is a (common) mistake to do this. The reason is that much
bookkeeping is performed prior to sending @code{FL_DRAW}, such as
clipping, double buffer preparation and possibly active window setting
etc. All of these is not done if the message is anything else than
@code{FL_DRAW}. So always use @code{@ref{fl_redraw_object()}} to draw
unless it is a response to @code{FL_DRAW}. Internally
@code{@ref{fl_redraw_object()}} calls the handler with @code{FL_DRAW}
(after some bookkeeping), so we only need to mark @code{FL_PUSH} with
a flag @code{newfig} and let the drawing part of the handler draw the
newly added figure.

@code{FL_DRAW} has two parts. One is simply to add a figure indicated
by @code{newfig} being true and in this case, we only need to draw the
figure that is being added. The other branch might be triggered as a
response to damaged drawing area resulting from @code{Expose} event or
as a response to @code{Refresh} command. We simply loop over all saved
figures and (re)draw each of them.

The only thing left to do is to initialize the program, which includes
initial color and size, and initial drawing function. Since we will
allow interactive resizing and also some of the objects on the form
are not resizeable, we need to take care of the gravities.
@example
void draw_initialize(FD_drawfree *ui) @{
    fl_set_form_minsize(ui->drawfree, 530, 490);
    fl_set_object_gravity(ui->colgrp, FL_West, FL_West);
    fl_set_object_gravity(ui->sizegrp, FL_SouthWest, FL_SouthWest);
    fl_set_object_gravity(ui->figgrp, FL_NorthWest, FL_NorthWest);
    fl_set_object_gravity(ui->miscgrp, FL_South, FL_South);
    fl_set_object_resize(ui->miscgrp, FL_RESIZE_NONE);

    cur_fig = saved_figure;
    cur_fig->pc[0] = cur_fig->pc[1] = cur_fig->pc[2] = 127;
    cur_fig->w = cur->fig->h = 30;
    cur_fig->drawit = fl_oval;
    cur_fig->col = FL_FREE_COL1 + 1;
    cur_fig->fill = 1;
    fl_set_button(ui->drobj[0], 1);  /* show current selection */

    fl_mapcolor(cur_fig->col, cur_fig->pc[0],
                cur->fig->pc[1], cur->fig->pc[2]);
    fl_mapcolor(FL_FREE_COL1, cur_fig->pc[0],
                cur->fig->pc[1], cur->fig->pc[2]);

    fl_set_slider_bounds(ui->wsli, 1, max_w);
    fl_set_slider_bounds(ui->hsli, 1, max_h);
    fl_set_slider_precision(ui->wsli, 0);
    fl_set_slider_precision(ui->hsli, 0);
    fl_set_slider_value(ui->wsli, cur_fig->w);
    fl_set_slider_value(ui->hsli, cur_fig->h);
@}
@end example

With all the parts in place, the main program simply creates,
initializes and shows the UI, then enters the main loop:
@example
int main(int argc, char *argv[]) @{
    fl_initialize(&argc, argv, "FormDemo", 0, 0);
    drawui = create_form_drawfree();
    draw_initialize(drawui);
    fl_show_form(drawui->drawfree, FL_PLACE_CENTER|FL_FREE_SIZE,
                 FL_FULLBORDER, "Draw");
    fl_do_forms();
    return 0;
@}
@end example

Since the only object that does not have a callback is the Quit
button, @code{@ref{fl_do_forms()}} will return only if that button is
pushed. Full source code to this simple drawing program can be found
in @file{demos/freedraw.c}.