CPI

IUP - Portable User Interface

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 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 Programing Interface) was created for this purpose: it was called CPI (Controls Programing 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.

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.)

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.

Iclass *iupCpiCreateNewClass(char *name, char *format);

  • name: Stores the name given to the control. This name allows the control to be used in LED.
  • 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 *)

Next step is to replace, if necessary, some of the control's CPI methods. This is done through function iupCpiSetClassMethod, which receives the control's class as an argument and the pointer to the new method. The Function iupCpiCreateNewClass will set the class with default methods, which provide the control a default behavior.

This initialization function should be named IupXxxOpen, where Xxx is the name of the control. The control will be automatically unregistered, but any other memory allocated in IupXxxOpen should be dealocated in a IupXxxClose.

Example (class creation for a control named Xxx):

#include <iup.h> 
#include <iupcpi.h> 

static Ihandle *XxxCreate(...)
{ 
  ... 
}

void IupXxxOpen(void) 
{ 
  Iclass *new_class = iupCpiCreateNewClass("xxx","n");

  iupCpiSetClassMethod(new_class, ICPI_CREATE, DialCreate); 
}

Control Instances

The new control should make a function available whose name would be IupXxx, where Xxx 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, then 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 the ICPI_CREATE method.

Example 1:

Ihandle* IupXxx(void)
{
  return IupCreate("xxx");
}

Example 2:

Ihandle* IupXxx (Ihandle* first,...)
{
  Ihandle **params = NULL;
  Ihandle *elem;
  va_list arg;

  if (first)
  {
    int i, n = 1;
    va_start (arg, first); 
    while (va_arg(arg, Ihandle *)) n++;
    va_end (arg);

    params = (Ihandle**) malloc (sizeof(Ihandle*) * (n+1));
  
    va_start (arg, first); 
    params[0] = first;

    for (i = 1; i < n; i++)
    {
      params[i] = va_arg(arg, Ihandle *);
    }

    params[n] = NULL;
    va_end (arg);
  }

  elem = IupCreatev("xxx", params);
  if (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 methods 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:

    int iupCpiSetClassMethod(Iclass *ic, char *method, Imethod func);

Method
&
Default Method
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 Iclass for the control instance (represented by the ic parameter). The array parameter is an array with the required attributes, specified in the call to the control creation function. The default function creates an IupCanvas control.

Prototype:
Ihandle *(*create) (Iclass* ic, void** array);

ICPI_DESTROY

iupCpiDefaultDestroy

This method is called by IUP when the control is about to be destroyed. 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 parent the control is a child (This parent can either be a dialog or any other control). If you do not directly map the control to the native system then iupCpiDefaultMap must be called to let IUP map the 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 ICPI_GETSIZE method to compute the natural size of the control. This function must return the same value returned by the ICPI_GETSIZE method.

Prototype:
int (*setnaturalsize) (Ihandle* self);

ICPI_SETCURRENTSIZE

iupCpiDefaultSetCurrentSize

This method is called by IUP for the control to change its current size. For such, this function must call functions iupSetCurrentWidth and iupSetCurrentHeight. Parameters represent the maximum size the control can have in pixels.

Prototype:
void (*setcurrentsize) (Ihandle* self, int max_w, int max_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 stored in the control's attribute environment.

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 the control's attribute environment check also returns null, then the ICPI_GETDEFAULTATTR method is called.

Prototype:
char* (*getattr) (Ihandle* self, char* attr);

ICPI_GETDEFAULTATTR

iupCpiDefaultGetDefaultAttr

This method is called by IUP when verifying an attribute value, when both the call to the ICPI_GETATTR method 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 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.

Prototype:
int (*getsize) (Ihandle *self, int *w, int *h);

ICPI_POPUP

(no default)

This method is called by IUP when wishing the control to show a 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.

Prototype:
int (*popup) (Ihandle *self, int x, int y);