XForms: Doubleclick button

Steve Lamont (spl@szechuan.ucsd.edu)
Fri, 18 Sep 98 07:22:32 PDT

# To subscribers of the xforms list from spl@szechuan.ucsd.edu (Steve Lamont) :

As I promised, I played around with an initial implementation of a
doubleclick button. Rather than code from scratch, the skeleton of
the DCButton object (as I have dubbed it) is based upon the cross
button described in the manual and provided in the XForms demo code.

There are a lot of things missing from the implementation -- changing
or querying the timeout value, for instance, but they should be
trivially easy to code.

I also didn't rigorously test all of the different button types --
I've tested the FL_NORMAL_BUTTON and FL_RETURN_BUTTON only (though I'm
not sure most of the other types make any particular sense in the
context of a double click button). I am also not as rigorous as I
might wish to be in testing the object type before I attempt to
dereference pointers, etc. I leave this as a programming exercise.

The button responds to either double and single clicks (adding a
tripleclick callback is left as a further exercise for the student),
as well as all clicks, if you wish.

In order to do this, I've added two new callbacks.

I relinquish copyright to this code. Inasmuch as it is derived from
the work of TC Zhao and Mark Overmars, I don't think I can place it in
the public domain but you may use it to the extent that you may use
any of the demo code supplied with XForms. [TC, would you give us
some statement as to the use of derived works?]

The API is described in the file `dcbutton.h':

- - -
/*
* Class DCButton header file
* Pinched from crossbut.h -- Class Crossbutton header file
*/

#ifndef DCBUTTON_H
#define DCBUTTON_H

#define FL_DCBUTTON (FL_USER_CLASS_START + 1)

FL_OBJECT *fl_add_dcbutton(
int type,
FL_Coord x,
FL_Coord y,
FL_Coord w,
FL_Coord h,
const char *label
);

void fl_add_dcbutton_double_click_callback(
FL_OBJECT *ob,
void (*callback)(FL_OBJECT *ob,
long parm),
long parm
);

void fl_add_dcbutton_single_click_callback(
FL_OBJECT *ob,
void (*callback)(FL_OBJECT *ob,
long parm),
long parm
);

FL_OBJECT *fl_create_dcbutton(
int type,
FL_Coord x,
FL_Coord y,
FL_Coord w,
FL_Coord h,
const char *label
);

#endif
- - -

The callbacks are added with fl_add_dcbutton_single_click_callback() and
fl_add_dcbutton_double_click_callback() respectively and have the same
argument lists as the standard fl_add_object_callback(). For
instance:

fl_add_dcbutton_single_click_callback( obj, singleclick_cb, 0 );
fl_add_dcbutton_double_click_callback( obj, doubleclick_cb, 0 );

adds callback actions for single and double click respectively.

Yes, I do tend to like long, descriptive function names. :-)

Note that since I didn't rewrite the Button object from scratch,
XForms will still call the normal object callback if it's registered
or cause fl_do_forms() to return the FL_OBJECT pointer if no object
callback is registered, even if the special callbacks are registered.
I suppose I could have registered a dummy callback in the
fl_create_dcbutton() function to prevent this.

The following is a my quick and dirty implementation of the button:

- - - dcbutton.c - - -
/*
* Routines implementing the "dcbutton" class
* Pinched from crossbut.c -- Routines implementing the "crossbutton" class
*/

#include <stdio.h>
#include <forms.h>

#include "dcbutton.h"
#include "dcbutton_internal.h"

static void single_click_timeout( int timeout_id, void *data )

{

FL_OBJECT *ob = ( FL_OBJECT *) data;
SPEC *spec = ( SPEC *) ob->spec;
DCButtonP dc_button_ptr = ( DCButtonP ) spec->cspecv;

dc_button_ptr->timer_id = 0;
if ( dc_button_ptr->single_click_callback ) {

long parm = dc_button_ptr->single_click_parm;

(*dc_button_ptr->single_click_callback)( ob, parm );

}

}

static void draw_dcbutton( FL_OBJECT *ob )

{

SPEC *spec = ( SPEC * ) ob->spec;

/* if redraw is demanded by FL_ENTER, ignore it */

if( spec->event != FL_ENTER ) {

/* draw the bounding box first */

fl_drw_box( ob->boxtype,
ob->x, ob->y, ob->w, ob->h, ob->col1, ob->bw );

/* if pushed, draw a down box */

if( spec->val )
fl_drw_box( FL_DOWN_BOX, ob->x, ob->y, ob->w, ob->h,
ob->col1, ob->bw );
else
fl_drw_box( FL_UP_BOX, ob->x, ob->y, ob->w, ob->h,
ob->col1, ob->bw );

/* label */

fl_drw_text( ob->align, ob->x, ob->y, ob->w, ob->h,
ob->lcol, ob->lstyle, ob->lsize, ob->label );

if ( ob->type == FL_RETURN_BUTTON )
fl_drw_text( FL_ALIGN_CENTER,
( FL_Coord ) ( ob->x + ob->w - 0.8 * ob->h ),
( FL_Coord ) ( ob->y + 0.2 * ob->h ),
( FL_Coord ) ( 0.6 * ob->h ),
( FL_Coord ) ( 0.6 * ob->h ), ob->lcol, 0, 0,
"@returnarrow" );

}

if ( ( spec->event == FL_RELEASE ) ||
( spec->event == FL_SHORTCUT ) ) {

DCButtonP dc_button_ptr = ( DCButtonP ) spec->cspecv;

if ( dc_button_ptr->timer_id ) {

/*
* Clear the time before it pops on us.
*/

fl_remove_timeout( dc_button_ptr->timer_id );
dc_button_ptr->timer_id = 0;

if ( dc_button_ptr->double_click_callback ) {

long parm = dc_button_ptr->double_click_parm;

(*dc_button_ptr->double_click_callback)( ob, parm );

}

} else if ( dc_button_ptr->timeout > 0 )
dc_button_ptr->timer_id =
fl_add_timeout( dc_button_ptr->timeout,
single_click_timeout, ( void *) ob );

}

}

FL_OBJECT *fl_create_dcbutton( int type, FL_Coord x, FL_Coord y,
FL_Coord w, FL_Coord h, const char *label )

{

FL_OBJECT *ob;
DCButtonP dc_button_ptr;

fl_add_button_class( FL_DCBUTTON, draw_dcbutton, 0 );

ob = fl_create_generic_button( FL_DCBUTTON, type, x, y, w, h, label );

ob->boxtype = FL_NO_BOX;

dc_button_ptr =
( ( FL_BUTTON_SPEC *) ob->spec )->cspecv =
( DCButtonP ) fl_calloc( 1, sizeof( DCButton ) );
dc_button_ptr->timeout = FL_CLICK_TIMEOUT;

return ob;

}

FL_OBJECT *fl_add_dcbutton( int type, FL_Coord x, FL_Coord y, FL_Coord w,
FL_Coord h, const char *label )

{

FL_OBJECT *ob = fl_create_dcbutton( type, x, y, w, h, label );

fl_add_object( fl_current_form, ob );

return ob;

}

void fl_add_dcbutton_single_click_callback( FL_OBJECT *ob,
void (*callback)( FL_OBJECT *ob,
long parm ),
long parm )

{

DCButtonP dcbutton_ptr =
( DCButtonP ) ( ( FL_BUTTON_SPEC *) ob->spec )->cspecv;

dcbutton_ptr->single_click_callback = callback;
dcbutton_ptr->single_click_parm = parm;

}

void fl_add_dcbutton_double_click_callback( FL_OBJECT *ob,
void (*callback)( FL_OBJECT *ob,
long parm ),
long parm )

{

DCButtonP dcbutton_ptr =
( DCButtonP ) ( ( FL_BUTTON_SPEC *) ob->spec )->cspecv;

dcbutton_ptr->double_click_callback = callback;
dcbutton_ptr->double_click_parm = parm;

}
- - - dcbutton_internal.h - - -
typedef struct {

void (*single_click_callback)( FL_OBJECT *ob, long parm );
long single_click_parm;
void (*double_click_callback)( FL_OBJECT *ob, long parm );
long double_click_parm;

int timeout;

int timer_id; /* Internal use */

} DCButton, *DCButtonP;

typedef FL_BUTTON_SPEC SPEC;
- - - try.c - - -
/*
* Demo showing the use of user defined object class: DBBUTTON
* The program runs till killed with Control-C.
*/

#include "forms.h"
#include "dcbutton.h"
#include <stdlib.h>

#ifndef FD_newbut_h_
#define FD_newbut_h

/**** Forms and Objects ****/
typedef struct {
FL_FORM *newbut;
void *vdata;
long ldata;
FL_OBJECT *bexit;
} FD_newbut;

extern FD_newbut *create_form_newbut(void);
#endif /* FD_newbut_h_ */

int main(int argc, char *argv[])

{
FD_newbut *cbform ;

fl_initialize( &argc, argv, "FormDemo", 0, 0 );
cbform = create_form_newbut();
fl_show_form(cbform->newbut, FL_PLACE_CENTER, FL_TRANSIENT, "newbutton");

while( fl_do_forms() )
;

}

static void singleclick_cb( FL_OBJECT *ob, long parm )

{

fprintf( stderr, "called singleclick!\n" );

}

static void doubleclick_cb( FL_OBJECT *ob, long parm )

{

fprintf( stderr, "called doubleclick!\n" );

}

FD_newbut *create_form_newbut( void )

{
FL_OBJECT *obj;
FD_newbut *fdui = (FD_newbut *) fl_calloc(1, sizeof(*fdui));

fdui->newbut = fl_bgn_form(FL_NO_BOX, 320, 250);
obj = fl_add_box(FL_UP_BOX,0,0,320,250,"");

/* obj = fl_add_dcbutton(FL_NORMAL_BUTTON,30,40,250,130,"Button"); */
obj = fl_add_dcbutton(FL_RETURN_BUTTON,30,40,250,130,"Button");

fl_add_dcbutton_single_click_callback( obj, singleclick_cb, 0 );
fl_add_dcbutton_double_click_callback( obj, doubleclick_cb, 0 );
fl_end_form();

return fdui;

}
- - - ENDIT - - -

As usual, I disclaim all warranties and liabilities. Use at your own
risk. If your underwear suddenly all leaps a half a meter to your
left the moment you hit Return, that's your own lookout (though some
of you might enjoy the sensation, I suppose).

spl
_________________________________________________
To unsubscribe, send the message "unsubscribe" to
xforms-request@bob.usuf2.usuhs.mil or see
http://bob.usuf2.usuhs.mil/mailserv/xforms.html
XForms Home Page: http://bragg.phys.uwm.edu/xforms
List Archive: http://bob.usuf2.usuhs.mil/mailserv/list-archives/