I guess the frustration just got sufficient for me to do something about it. 
Anyway, I have formed the opinion that our use of the xforms library is 
sufficiently "simple" to make simple wrappers with C++ semantics easy to 
achieve.

Here then is a start. All widgets would derive from XFwidget which 
automatically gives us an emiited signal rather than a global callback. 
Thereafter, various input widgets are very easy.

This would give us very similar code to the gnome and qt2 frontends which is 
nice. However, the real win will come when we combine widgets to create, eg, 

class XFlengthGroup : boost::noncopyable {
public:
        ///
        enum ChoiceType {
                /// Include all available types in the choice
                ALL,
                /// Don't include t%, c%, p%, l%.
                NO_PERCENTAGES
        };
        ///
        XFlengthGroup(XFinput_flt * input, XFchoice * choice, ChoiceType);
        ///
        LyXLength get() const;
        ///
        void set(LyXLength const &);
        /// Activate / Deactivate the widgets
        void enable(bool activate);

private:
        ///
        XFinput_flt * input_;
        ///
        XFchoice * choice_;
        /// The number of items in the choice (depends on ChoiceType).
        lyx::size_type size_;
};

This is the sort of thing that the gnome and qt2 frontends could also do to 
their advantage.

I have this working beautifully in one of my trees here.

Angus

// -*- C++ -*-
/**
 * \file xforms/XFinput.h
 * Copyright 2000-2002 the LyX Team
 * Read the file COPYING
 *
 * \author Angus Leeming, [EMAIL PROTECTED]
 */

#ifndef XFINPUT_H
#define XFINPUT_H

#ifdef __GNUG__
#pragma interface
#endif

#include "XFwidget.h"

/** A wrapper class for the FL_OBJECT, type FL_INPUT with C++ semantics.
 */
class XFinput : public XFwidget {
public:
	/** Set on_change as \c true if the event signal is to be emitted
	 *  after each key press.
	 *  Set it to \c false if it is to be emitted only on Enter.
	 */
	XFinput(flobjs_ *, bool on_change);
	///
	flobjs_ * input() { return ob_; }

protected:
	///
	string const getString() const;
};


class XFinput_str : public XFinput {
public:
	///
	XFinput_str(flobjs_ *, bool on_change);
	///
	void set(string const &);
	///
	string const get() const;
   
};


class XFinput_int : public XFinput {
public:
	///
	XFinput_int(flobjs_ *, bool on_change);
	///
	void set(int);
	///
	int get() const;
   
};


class XFinput_flt : public XFinput {
public:
	///
	XFinput_flt(flobjs_ *, bool on_change);
	///
	void set(float);
	///
	float get() const;
   
};

#endif // XFINPUT_H
/**
 * \file xforms/XFinput.C
 * Copyright 2000-2001 The LyX Team.
 * See the file COPYING.
 *
 * \author Angus Leeming, [EMAIL PROTECTED]
 */

#include <config.h>

#ifdef __GNUG__
#pragma implementation
#endif

#include "XFinput.h"

#include "support/LAssert.h"
#include "support/lstrings.h"

#include FORMS_H_LOCATION

extern "C" {

int C_PrehandlerCB(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);

/** Only allow integer numbers,
    possibly preceeded by a +' or '-' sign */
int fl_int_filter(FL_OBJECT *, char const *, char const *, int);
    /** Only allow integer numbers. No '+' or '-' signs. */
int fl_unsigned_int_filter(FL_OBJECT *, char const *, char const *, int);
    /** Only allow floating point numbers,
	possibly preceeded by a +' or '-' sign */
int fl_float_filter(FL_OBJECT *, char const *, char const *, int);
    /** Only allow floating point numbers. No '+' or '-' signs. */
int fl_unsigned_float_filter(FL_OBJECT *, char const *, char const *, int);

} // extern "C"


XFinput::XFinput(FL_OBJECT * ob, bool on_change)
	: XFwidget(ob)
{
	lyx::Assert(ob_->objclass == FL_INPUT);

	if (on_change)
		fl_set_input_return(ob_, FL_RETURN_CHANGED);
	else
		fl_set_input_return(ob_, FL_RETURN_END);

	fl_set_input(ob_, "");

	// Override the XFwidget prehandler
	fl_set_object_prehandler(ob_, C_PrehandlerCB);
}


string const XFinput::getString() const
{
	char const * tmp = fl_get_input(ob_);

	if (!tmp)
		return string();

	return strip(frontStrip(tmp));
}


XFinput_str::XFinput_str(FL_OBJECT * ob, bool on_change)
	: XFinput(ob, on_change)
{}


void XFinput_str::set(string const & str)
{
	fl_set_input(ob_, str.c_str());
}


string const XFinput_str::get() const
{
	return getString();
}


XFinput_flt::XFinput_flt(FL_OBJECT * ob, bool on_change)
	: XFinput(ob, on_change)
{
	fl_set_input_filter(ob_, fl_float_filter);
}


void XFinput_flt::set(float val)
{
	string const str = tostr(val);
	fl_set_input(ob_, str.c_str());
}


float XFinput_flt::get() const
{
	return strToDbl(getString());
}


XFinput_int::XFinput_int(FL_OBJECT * ob, bool on_change)
	: XFinput(ob, on_change)
{
	fl_set_input_filter(ob_, fl_int_filter);
}


void XFinput_int::set(int val)
{
	string const str = tostr(val);
	fl_set_input(ob_, str.c_str());
}


int XFinput_int::get() const
{
	return strToInt(getString());
}


extern "C" {

int C_PrehandlerCB(FL_OBJECT * ob, int event,
		   FL_Coord, FL_Coord, int key, void *)
{
	// Note that the return value is important in the pre-emptive handler.
	// Don't return anything other than 0.
	lyx::Assert(ob);

	XFinput * ptr = static_cast<XFinput *>(ob->u_vdata);

	if (ptr) {
		switch (event) {
		case FL_PUSH:
			if (key == 2)
				ptr->event.emit();
			break;

		case FL_ENTER:
			ptr->entered.emit(true);
			break;

		case FL_LEAVE:
			ptr->entered.emit(false);
			break;

		default:
			break;
		}
	}
	
	return 0;
}


int fl_int_filter(FL_OBJECT * ob,
		  char const *, char const *, int c)
{
	if (c == 0 /* final test before handing contents to app */
	    || strchr("0123456789+-", c)) {
		if (isStrInt(fl_get_input(ob)))
			return FL_VALID;
	}
	return FL_INVALID|FL_RINGBELL;
}


int fl_unsigned_int_filter(FL_OBJECT * /*ob*/,
			   char const *, char const *, int c)
{
	if (c == 0 /* final test before handing contents to app */
	    || strchr("0123456789", c)) {
		/* since we only accept numerals then it must be valid */
		return FL_VALID;
	}
	return FL_INVALID|FL_RINGBELL;
}


int fl_float_filter(FL_OBJECT * ob,
		    char const *, char const *, int c)
{
	if (c == 0 /* final test before handing contents to app */
	    || strchr("0123456789.+-", c)) {
		if (isStrDbl(fl_get_input(ob)))
			return FL_VALID;
	}
	return FL_INVALID|FL_RINGBELL;
}


int fl_unsigned_float_filter(FL_OBJECT * ob,
			     char const * /*not_used*/,
			     char const * /*unused*/,
			     int c)
{
	if (c == 0 /* final test before handing contents to app */
	    || strchr("0123456789.", c)) {
		if (isStrDbl(fl_get_input(ob)))
			return FL_VALID;
	}
	return FL_INVALID|FL_RINGBELL;
}

} // extern "C"
// -*- C++ -*-
/**
 * \file xforms/XFwidget.h
 * Copyright 2000-2002 the LyX Team
 * Read the file COPYING
 *
 * \author Angus Leeming, [EMAIL PROTECTED]
 */

#ifndef XFWIDGET_H
#define XFWIDGET_H

#ifdef __GNUG__
#pragma interface
#endif

#include "LString.h"
#include <sigc++/signal_system.h>

class flobjs_;

/** A wrapper class for the FL_OBJECT, type FL_INPUT with C++ semantics.
 */
class XFwidget : public SigC::Object {
public:
	///
	XFwidget(flobjs_ *);

	///
	virtual ~XFwidget() {}

	/// Set the string that will be displayed as a tooltip.
	void initTooltip(string const &);

	/// Activate or deactivate the widget.
	void enable(bool);

	/** Signal is emitted when the widget's callback function is called
	 *  by xforms.
	 */
	SigC::Signal0<void> event;

	/// Signal is emitted when the mouse enters or leaves the widget
	SigC::Signal1<void, bool> entered;

protected:
	/// The widget itself
	flobjs_ * ob_;

	///
	string tooltip_;

private:
	/** Connected to the Tooltips::set signal.
	 *  Will, therefore, toggle the tooltip on and off.
	 */
	void setTooltip();

};

#endif // XFWIDGET_H

/**
 * \file xforms/XFinput_string.C
 * Copyright 2000-2001 The LyX Team.
 * See the file COPYING.
 *
 * \author Angus Leeming, [EMAIL PROTECTED]
 */

#include <config.h>

#ifdef __GNUG__
#pragma implementation
#endif

#include "XFwidget.h"

#include "Tooltips.h"

#include "support/LAssert.h"

#include FORMS_H_LOCATION
#include "gettext.h"
#include "xforms_helpers.h" // formatted
	
extern "C" {

// Callback routines invoked by xforms
void C_CB(FL_OBJECT *, long);

int C_PrehandlerCB(FL_OBJECT *, int, FL_Coord, FL_Coord, int, void *);

} // extern "C"


XFwidget::XFwidget(FL_OBJECT * ob)
	: ob_(ob)
{
	lyx::Assert(ob_);

	fl_set_object_prehandler(ob_, C_PrehandlerCB);
	fl_set_object_callback(ob_, C_CB, 0);

	ob_->u_vdata = this;
}


void XFwidget::initTooltip(string const & tooltip)
{
	// Store the tooltip string
	tooltip_ = formatted(_(tooltip), 400);
	Tooltips::set.connect(SigC::slot(this, &XFwidget::setTooltip));
}


void XFwidget::setTooltip()
{
	char const * const c_str = Tooltips::enabled() ? tooltip_.c_str() : 0;
	fl_set_object_helper(ob_, c_str);
}


void XFwidget::enable(bool activate)
{
	if (activate) {
		fl_activate_object(ob_);
		fl_set_object_lcol(ob_, FL_BLACK);
	} else {
		fl_deactivate_object(ob_);
		fl_set_object_lcol(ob_, FL_INACTIVE);
	}
}


extern "C" {

void C_CB(FL_OBJECT * ob, long)
{
	lyx::Assert(ob && ob->u_vdata);

	XFwidget * ptr = static_cast<XFwidget *>(ob->u_vdata);

	if (ptr) {
		ptr->event.emit();
	}
}


int C_PrehandlerCB(FL_OBJECT * ob, int event, FL_Coord, FL_Coord, int, void *)
{
	// Note that the return value is important in the pre-emptive handler.
	// Don't return anything other than 0.
	lyx::Assert(ob);

	XFwidget * ptr = static_cast<XFwidget *>(ob->u_vdata);

	if (ptr) {
		switch (event) {
		case FL_ENTER:
			ptr->entered.emit(true);
			break;

		case FL_LEAVE:
			ptr->entered.emit(false);
			break;

		default:
			break;
		}
	}
	
	return 0;
}

} // extern "C"

Reply via email to