CPI
Introduction
The IUP toolkit was originally designed to support a set of well-defined controls existing in all the destination platforms. With the evolution of native systems (e.g. Windows 95) and new requests from users, IUP needed to evolve with the purpose of making the addition of new interface elements to the toolkit easier.
Thus, to support the development of new controls for IUP, a specific API (Application Program Interface) was created for this purpose: it was called CPI (Control Program Interface). This new API is orthogonal to the original IUP API, that is, its use with a client application does not interfere with the conventional use of IUP. Only a developer wishing to implement a new IUP control is required study this API.
Control Implementation
To create a new CPI control, follow these steps:
- Initialize the control
- Create control instances
- Implement the CPI methods associated to the control
- Make exported information available to the user (function prototypes, definitions, etc.)
General Control Initialization
The initialization function is in charge of passing IUP the necessary information so that the control can be used. Such information is grouped in an Iclass-type structure, which from now on is to be called the class of the control.
The first step is to create the structure. This is done by calling the iupCpiCreateNewClass function. To this function, two pieces of information must be passed: a string identifying the control in a unique way (the "name" of the control), and a string describing the creation parameters when the control is created via LED. The pointer returned by iupCpiCreateNewClass must be stored in a static module variable, as it will be necessary to create new control instances.
Next step is to replace, if necessary, the control's CPI method. This is done through function iupCpiSetClassMethod, which receives the control's class as an argument and the pointer to the new method.
Important: Function iupCpiCreateNewClass fills the class with default methods, which provide the control a default behavior.
This initialization function must be named IupControlOpen, where Control is the name of the control.
Example (class creation for a control named Dial):
#include <iup.h>
#include <iupcpi.h>static Iclass *classe = NULL;
static Ihandle *DialCreate(...) /* método de criação */
{
...
}void IupDialOpen(void)
{
classe = iupCpiCreateNewClass("dial","n");iupCpiSetClassMethod(classe, ICPI_CREATE, DialCreate);
}Creation of Control Instances
The created control must make a function available whose name must be IupControl, where Control is the control name. This function is to be used by the user to create a new control instance, and should not receive arguments. If the control is a container, the the arguments are necessarily its children.
In this creation function, if no parameters are necessary, just call IupCreate with the control's name. If the control allows children, use IupCreatev to pass them forward to IUP. This function will create the control's Ihandle, by means of the function registered by ICPI_CREATE.
Example1:
Ihandle *IupDial() { return IupCreate("dial"); }Example2:Ihandle *IupBox (Ihandle * first,...) { Ihandle **params = NULL; Ihandle *elem = NULL; unsigned int i = 0; va_list arg; if(first) { va_start (arg, first); i = 1; while (va_arg(arg, Ihandle *)) i++; va_end (arg); params = (Ihandle**) malloc (sizeof (Ihandle*) * (i+1)); i = 0; va_start (arg, first); elem = first; while (elem != NULL) { params[i++] = elem; elem = va_arg(arg, Ihandle *); } params[i] = NULL; va_end (arg); } elem = IupCreatev(name, params); free(params); return elem; }CPI Methods
The Iclass structure fields are mostly pointers to functions to be called by IUP in certain moments. These pointers to functions play the same parts as objects in languages such as C++. Following the C++ philosophy, the CPI defines a set of functions which can be used to provide the controls a default behavior. The Iclass structure stores these function pointers, which are defined right after the call to iupCpiCreateNewClass.
In several occasions, the default behavior defined by the CPI is not adequate for the new control's implementation. In this case, a new function must be set, providing the desired implementation for such method. If convenient, this new function can call the function implementing the method's default behavior, either before or after performing the specific treatment of the new control. Generally, a method that will always be redefined to a new control is the one in charge of creating instances of this control. To redefine (replace) a control method, function iupCpiSetClassMethod must be used. It receives as parameters the values described below:
iupCpiSetClassMethod parameter
Default method used
Description
ICPI_CREATE iupCpiDefaultCreate
This method is called by IUP when an instance for such control must be created. When this function is called, IUP already has a registered Ihandle for the control instance (represented by the self parameter). The array parameter is an array with the required attributes, specified in the call to the control creation functionPrototype:
Ihandle *(*create) (Iclass* class, void** array);ICPI_DESTROY iupCpiDefaultDestroy
This method is called by IUP when the IupDestroy function is applied on the control or its dialog. If necessary, this method can be redefined to dispose of structures maintained by the control.Prototype:
void (*destroy) (Ihandle* self);ICPI_MAP iupCpiDefaultMap
This method is called by IUP to map the control in the native system. The parent parameter indicates of which window the control is a child. (This window can either be a dialog or any other control.)Prototype:
void (*map) (Ihandle* self, Ihandle* parent);ICPI_UNMAP iupCpiDefaultUnmap
This method is called by IUP to "destroy" the control's mapping in the native system without removing the control from the control hierarchy of a IUP dialog.Prototype:
void (*unmap) (Ihandle* self);ICPI_SETNATURALSIZE iupCpiDefaultSetNaturalSize
This method is called by IUP for the control to specify its natural size. For such, this function must call functions iupSetNaturalWidth and iupSetNaturalHeight. Its implementation might call the function associated to the getsize field of the Iclass structure (to be described further on) to compute the natural size of the control. This function must return the same value returned by the function associated to the getsize field.Prototype:
int (*setnaturalsize) (Ihandle* self);ICPI_SETCURRENTSIZE iupCpiDefaultSetCurrentSize
This method is called by IUP for the control to specify its current size. For such, this function must call functions iupSetCurrentWidth and iupSetCurrentHeight. Parameters w (width in pixels) and h (height in pixels) represent the maximum values the control dimensions can have.Prototype:
void (*setcurrentsize) (Ihandle* self, int w, int h);ICPI_SETPOSITION iupCpiDefaultSetPosition
This method is called by IUP for the control to define its position inside the parent window. Parameters x and y represent the position in pixels (upper left corner of the control) the control must have, computed by IUP. The default behavior for this method need only be changed if the control has sub-controls.Prototype:
void (*setposition) (Ihandle* self, int x, int y);ICPI_SETATTR
iupCpiDefaultSetAttr
This method is called to provide a new value to a given control attribute. When this method is called, the attribute's value is already updated in the control's attribute environment. The received parameters mean the following: attr is the attribute being changed; value is the new attribute value; ovalue is the previous attribute value.Prototype:
void (*setattr) (Ihandle* self, char* attr, char* value);ICPI_GETATTR iupCpiDefaultGetAttr
This method is called by IUP to verify the value of a control attribute, determined by parameter attr. This method is called before IUP verifies the control's attribute environment. If this method returns null, IUP verifies the control's attribute environment. If this check also returns null, then the method related to the getdefaultattr field is called.ICPI_GETDEFAULTATTR iupCpiDefaultGetDefaultAttr
This method is called by IUP when verifying an attribute value, when both the call to the method related to the getattr field and the verification of the control's attribute environment fail (returned null).Prototype:
char* (*getdefaultattr) (Ihandle* self, char* attr);ICPI_GETSIZE
iupCpiDefaultGetSize
This method is called by IUP when wishing to verify the size the control must have. This function must write to the w and h parameters the control size in pixels, respectively. The return value for this function can only be one of the following:0 - The control size does not vary when the dialog size varies.
1 - The control width may vary when the dialog width varies.
2 - The control height may vary when the dialog height varies.
3 - The control width and height may vary when the dialog size varies.ICPI_POPUP
-
This method is called by IUP when wishing the control to show the popup dialog. The x and y parameters indicate the position the dialog must initially have. This method must return IUP_NOERROR, if no error occurs, or IUP_ERROR if an error occurs.
Header File
For a IUP application to use the new control, the prototypes of the initialization functions and the definitions of the new attributes must be made available. This is done by means of a header file, which must have the same name as the control.
Function Prototypes
The prototypes of all control functions that might be used by the control's client applications must be provided. Usually there are only two prototypes: the initialization function and the instance creation function. It is important to consider that the control might be used in applications both in C and in C++. Therefore, the prototype declaration should be involved by a "C" extern block (see example below).
Attribute Definition
To match the IUP standard, macros must be defined to reference the strings identifying the new attributes used by the control. For example, if a new control has an attribute named MODE used to identify its operation mode, then the following macro must be defined:
#define IUP_MODE "MODE"
Note that the attributes used by the control may have already been defined (in another control's header, for instance). Thus, it is advisable to check if this happens to avoid compilation errors. Also refer to section Attribute Treatment.
Example:
/*
* IupControle.h
*/#ifndef IUPCONTROLE_H
#define IUPCONTROLE_H#ifdef __cplusplus /* necessário quando controle é utilizado em código C++ */
extern "C" {
#endifvoid IupControleOpen(void);
Ihandle *IupControle(...);#ifdef __cplusplus
}
#endif/* Novos atributos */
#ifndef IUP_MODE
#define IUP_MODE "MODE"
#endif#ifndef IUP_LENGTH
#define IUP_LENGTH "LENGTH"
#endif#endif /* IUPCONTROLE_H */
The Iclass Structure
The Iclass structure stores pointers to the control's methods (described in the table above) as well as the following fields:
- char* name :
Stores the name given to the control. This name allows the control to be used in LED.
- char* format :
Format string used to describe the required attributes defined in LED to create a control instance. If this field is null, then the new control type has no required attributes. The format string can be any sequence with the following characters:
Character Meaning n name of a control instance or an action s any string c interface control (Ihandle *) N from this character on, a list of control-instance names or a list of actions will be passed S from this character on, a list of strings will be passed C from this character on, a list of interface controls will be passed (Ihandle *)
Attribute Treatment
By default, a control inherits the same attributes defined for IUP's Canvas element.
The developer of a new control can define new attributes. For such, he/she must redefine the ICPI_SETATTR, ICPI_GETATTR and ICPI_GETDEFATTR methods, if necessary.
If any attribute-manipulation method is redefined, the standard procedure is:
- identify if the given attribute is part of the set of attributes that must receive any special treatment by the new control;
- if the attribute is part of this set, then the adequate treatment must be provided;
- if not, the default method must be called to treat the attribute.
Default Methods
In this section, we present the set of functions corresponding to the default behavior of an interface element's methods. Such functions can either be used to fill an Iclass structure or be called by a new method to execute the default procedure (either before or after the specialized treatment of the new control is executed).
Function
corresponding to the setnaturalsize method:
int ctrsetnaturalsize (Ihandle* self);
Function
corresponding to the setcurrentsize method:
void ctrsetcurrentsize (Ihandle* self, int w, int h);
Functions
corresponding to the getsize method:
int ctrgetsize (Ihandle* self, int* w, int* h); ou
int ctrgetsizevar (Ihandle* self, int* w, int* h);
Note: A control that associates the ctrgetsize function to its getsize method cannot vary in size when its dialog's size varies. On the other hand, a control that uses function ctrgetsizevar can vary in size according to the changes made to its dialog size.
Function
corresponding to the setposition method:
void ctrsetposition (Ihandle* self, int w, int h);
Function
corresponding to the create method:
void ctrcreate (Ihandle* self, void** array);
Function
corresponding to the destroy method:
void ctrdestroy (Ihandle* self);
Function
corresponding to the map method:
void ctrmap (Ihandle* self, Ihandle* parent);
Function
corresponding to the unmap method:
void ctrunmap (Ihandle* self);
Function
corresponding to the setattr method:
void ctrsetattr (Ihandle* self, char* attr, char* value);
Function
corresponding to the getattr method:
char* ctrgetattr (Ihandle* self, char* attr);
Function
corresponding to the getdefaultattr method:
char* ctrgetdefaultattr (Ihandle* self, char* attr);
Extra Functions
Following we provide a list of extra IUP functions. They can be used by programmers wishing to create a new control:
Main control-creation functions
Iclass *iupCpiCreateNewClass(char *name, char
*format);
int iupCpiSetClassMethod(Iclass *ic, char *method, Imethod func);
Functions that call the method in charge for the given action
int iupmethSetNaturalSize( Ihandle* self );
void iupmethSetCurrentSize( Ihandle* self, int w, int h );
int iupmethGetSize( Ihandle* self, int* w, int* h );
void iupmethSetPosition ( Ihandle* self, int w, int h );
void iupmethCreate( Ihandle* self, void** array );
void iupmethDestroy( Ihandle* self );
void iupmethMap( Ihandle* self, Ihandle* parent );
void iupmethUnmap( Ihandle* self );
void iupmethSetAttribute( Ihandle* self, char* attr, char* value);
char* iupmethGetAttribute( Ihandle* self, char* attr );
char* iupmethGetDefaultAttr( Ihandle* self, char* attr );
char* iupmethGetClassName( Ihandle* self );
Functions in charge of manipulating an element's size
void
iupSetCurrentWidth(Ihandle* self, int w);
void iupSetCurrentHeight(Ihandle* self, int h);
int iupGetCurrentWidth(Ihandle* self);
int iupGetCurrentHeight(Ihandle* self);
int iupHorResizable(Ihandle* self);
int iupVertResizable(Ihandle* self);
void iupSetNaturalWidth(Ihandle* self, int w);
void iupSetNaturalHeight(Ihandle* self, int h);
void iupGetCharSize(Ihandle* n, int *w, int *h);
int iupGetSize(Ihandle* e, int* w, int *h);
void iupGetTextSize(Ihandle* h, char* text, int* size);
void iupdrvResizeObjects(Ihandle *n);
Example
The best example possible can be taken from IUP distribution. As an advice, please refer to the iupgauge control.