This is xforms.info, produced by makeinfo version 4.13 from xforms.texi. INFO-DIR-SECTION Development START-INFO-DIR-ENTRY * XForms: (xforms). A Graphical user interface toolkit for X END-INFO-DIR-ENTRY  File: xforms.info, Node: XPopup Interaction, Next: Other XPopup Routines, Prev: Creating XPopups, Up: XPopup 23.3.2 XPopup Interaction ------------------------- To select an item, move the mouse to the item to be selected while keeping the mouse button pressed down and then release the mouse button on top of the item to be selected. If you don't want to make a selection release the mouse button somewhere outside the area of the XPopup. If you have a "hanging" XPopup, i.e., a XPopup that's open even though the mouse button isn't pressed anymore you can select by clicking on an item or use the cursor `Up' and `Down' keys to navigate through the items and select by pressing the `' key. The `' and `' keys allow you to jump to the first or last selectable item, respectively. Use `' to close the popup without selecting an item. It is also possible to use convenience functions to bind keyboard keys to items (the "hotkeys") instead of using `%s' with `*note fl_defpup()::': void fl_setpup_shortcut(int popup_id, int item_val, const char *hotkeys); where `item_val' is the value associated with the item (either due to its position or set with `%x') and hotkeys is a string specifying all the hotkey combinations. *Note Shortcuts::, for details. Briefly, within that string `#' and `^' denote the `' and `' keys, respectively. `&n' with `n = 1, 2' etc. can be used to denote the function key numbered `n'. Thus if hotkeys is set to `"#a^A', both `A' and `A' are bound to the item. One additional property of the hotkey is the underlining of corresponding letters in the item string. Again, only the first key in the hotkey string is used. Therefore, the hotkey strings `"Cc"', `"#C"' and `"^C"' will result in the character `C' in the item string `"A Choice"' being underlined, while the the hotkey strings `"cC"' and `"#c"' will not since there's no `c' in the item string. There is a limit of maximum 8 shortcut keys. Two convenience functions are available to set the callback functions for items of a XPopup and the XPopup as a whole (called whenever a selection is made): typedef int (*FL_PUP_CB)(int); FL_PUP_CB fl_setpup_itemcb(int popup_id, int item_val, FL_PUP_CB cb); FL_PUP_CB fl_setpup_menucb(int popup_id, FL_PUP_CB cb); These functions thus allow to change the popup and item callback functions set at creation of the popup with `%F' and `%f'. As usual, `popup_id' is the ID of the XPopup, `item_val' the value associated with the item (position or value set via `%x'), and `cb' is the address of the callback function. Please note that Xpopup objects are a bit special in XForms. Normal objects get returned by e.g., `*note fl_do_forms()::' (or an associated callback gets invoked). But since Xpopup objects are meant to be sub-objects of other objects (like `FL_CHOICE' and `L_MENU' objects) and don't get invoked directly by a call of e.g., `*note fl_do_forms()::' but instead by a call of `*note fl_dopup()::' they can't get returned to the application. Instead the caller of `*note fl_dopup()::' (normally some internal function of a `FL_CHOICE' or `FL_MENU' object) has to deal with the return value. Furthermore, also callback functions can be set that get invoked whenever an item in the XPopup is entered or left, even without a selection being made. The following functions can be used to register these item enter/leave callbacks: typedef void (*FL_PUP_ENTERCB)(int item_val, void *data); typedef void (*FL_PUP_LEAVECB)(int item_val, void *data); FL_PUP_ENTERCB fl_setpup_entercb(int popup_id, FL_PUP_ENTERCB cb, void *data); FL_PUP_LEAVECB fl_setpup_leavecb(int popup_id, FL_PUP_LEAVECB cb, void *data); The function `cb' will be called when the mouse enters or leaves an (non-disabled) item of the XPopup `popup_id'. Two parameters are passed to the callback function. The first parameter is the item number enter/leave applies to and the second parameter is a data pointer. To remove an enter/leave callback, call the functions with the callback function argument `cb' set to `NULL'. There is also a function to associate a XPopup item with a sub-XPopup void fl_setpup_submenu(int popup_id, int item_val, int subpopup_id); If a sub-XPopup is associated with item `item_val' that item can't be selected anymore (releasing the mouse button on this item makes `*note fl_dopup()::' return -1 but instead a new XPopup is opened beside the item and you can now make selections within this sub-XPopup. It is the programmers responsibility to make sure that the item values of the sub-XPopup don't clash with those of the higher-level XPopup or it may be impossible to determine which item was selected.  File: xforms.info, Node: Other XPopup Routines, Next: XPopup Attributes, Prev: XPopup Interaction, Up: XPopup 23.3.3 Other XPopup Routines ---------------------------- Note that most of the setpup/getpup routines are recursive in nature and the function will search the menu and all its submenus for the item. It is possible to modify the display characteristics of a given XPopup item after its creation using the following routine void fl_setpup_mode(int popup_id, int item_val, unsigned mode); As usual `popup_id' is the XPopup ID as returned by `*note fl_newpup()::' or `*note fl_defpup()::' and `item_val' the value of the item. `mode' is one of `FL PUP NONE', `FL PUP GREY', `FL PUP BOX' or `FL PUP RADIO' (one of the later two can be bitwise ORed with `FL_PUP_CHECK', as already discussed above. To obtain the mode of a particular menu item, use the following routine unsigned int fl_getpup_mode(int popup_id, int item_val) This comes in handy to check if a binary or radio item is set if (fl_getpup_mode(popupd, item_val) & FL_PUP_CHECK) /* item is set */ There exists also a routine that can be used to obtain an items text const char *fl_getpup_text(int popup_id, int item_val); In some situations, especially when the popup is activated by non-pointer events (e.g., as a result of a keyboard shortcut), the default placement of popups based on mouse location might not be adequate or appropriate, thus XPopup provides the following routine to override the default placement void fl_setpup_position(int x, int y); where `x' and `y' specify the location where the top-left corner of the popup should be. `x' and `y' must be given in screen coordinates (i.e., relative to the root window) with the origin at the top-left corner of the screen. This routine should be used immediately before invoking `*note fl_dopup()::', the position is not remembered afterwards. If `x' or `y' is negative, the absolute value is taken to mean the desired location relative to the right or bottom corner of the popup (not the screen!). Another function exists for controlling the positon of the popup. When the fuunction void fl_setpup_align_bottom(void); then the pop-up will appear with its lower right hand corner aligned aligned with the mouse position or, if also `*note fl_setpup_position()::' is active, the postion set this way will be interpreted to mean the lower right hand position of the popu-up. A radio item in a group can be initialized to be in "pushed" state by using `%R'. But you can also switch a such a radio item to "pushed state also programmatically using void fl_setpup_selection(int popup_id, int item_val); Of course, other radio items of the XPopup belonging to the same group are reset to "unpushed" state. To obtain the number of items in a popup, use the following routine int fl_getpup_items(int popup_id)  File: xforms.info, Node: XPopup Attributes, Next: XPopup Remarks, Prev: Other XPopup Routines, Up: XPopup 23.3.4 XPopup Attributes ------------------------ The title of a XPopup can be set using the functions void fl_setpup_title(int popup_id, const char *title); void fl_setpup_title_f(int popup_id, const char *fmt, ...); They only differ in the way the new title is passed to the function, the first one accepts a simple string while the second expects a format string as used for `printf()' etc., followed by the appropriate number of (unspecified) arguments. Use the following routines to modify the default popup font style, font size and border width: int fl_setpup_default_fontsize(int size); int fl_setpup_default_fontstyle(int style); int fl_setpup_default_bw(int bw); The functions return the old size, style or border width value, respectively. All XPopups by default use a right arrow cursor. To change the default cursor, use Cursor fl_setpup_default_cursor(int cursor); where you can use for `cursor' any of the standard cursors defined in `' like `XC_watch' etc. The function returns the previously cursor. To change the cursor of a particular XPopup only , use the following routine Cursor fl_setpup_cursor(int popup_id, int cursor); For example, after the following sequence, id = fl_defpup(win, "item1|item2"); fl_setpup_cursor(id, XC_hand2); the popup with ID `id' will use a "hand" instead of the default arrow cursor. In versions before 1.0.91 XPopups were drawn with a heavy shadow around the box. Drawing of this shadow could be controlled via void fl_setpup_shadow(int popup_id, int yes_no); Nowadays this function still exists for backward-compatibility but does nothing. The appearance of XPopups (and their associated sub-popups) can be change by the following routines: void fl_setpup_bw(int popup_id, int bw); void fl_setpup_softedge(int pupup_id, int yes_no); The first sets the border width for a XPopup. Calling `*note fl_setpup_softedge()::' with a true argument for `yes_no' has the same effect as using a negative border width while using a false (0) argument is equivalent to using a positive one (so this function isn't very useful). The background color and text color of a popup can be changed using void fl_setpup_default_color(FL_COLOR bgcolor, FL_COLOR tcolor); By default, the background color `bgcolor' is `FL_COL1' and the text color `tcolor' is `FL_BLACK'. For "binary" or radio items, that have check box associated with them, the "checked" or "pushed" color (default is `FL_BLUE') can be changed with the following routine void fl_setpup_default_checkcolor(FL_COLOR checkcolor); There is by default a limit of 32 XPopups per process. To enlarge the number of XPopups allowed, use the following routine int fl_setpup_maxpups(int new_max); The function returns the previous limit. It is possible to use XPopups as a message facility using the following routines void fl_showpup(int popup_id); void fl_hidepup(int popup_id); No interaction takes place with a XPopup shown by `*note fl_showpup()::' and it can only be removed from the screen programmatically via `*note fl_hidepup()::'.  File: xforms.info, Node: XPopup Remarks, Prev: XPopup Attributes, Up: XPopup 23.3.5 Remarks -------------- Take care to make sure all items, including the items on submenus, of a XPopup have unique values and are positive. XPopups are used indirectly in the demo programs `menu.c', `boxtype.c', `choice.c' and others. For a direct pop-up demo see `popup.c'.  File: xforms.info, Node: Part IV, Next: Part IV Introduction, Prev: Part III Deprecated Objects, Up: Top _Part IV - Designing Object Classes_ ************************************ * Menu: * Part IV Introduction:: * Part IV Global Structure:: * Part IV Events:: * Part IV The Type FL_OBJECT:: * Part IV Drawing Objects:: * Part IV An Example:: * Part IV New Buttons:: * Part IV Using a Pre-emptive Handler::  File: xforms.info, Node: Part IV Introduction, Next: Part IV Global Structure, Prev: Part IV, Up: Top 24 Introduction *************** Earlier chapters discussed ways to build user interfaces by combining suitable objects from the Forms Library, defining a few object callbacks and using Xlib functions. However, there is always a possibility that the built-in objects of the Forms Library might not be enough. Although free objects in principle provide all the flexibility a programmer needs, there can be situations where it is beneficial to create new types of objects, for example switches or joysticks or other types of sliders, etc. In these cases, a programmer can use the architecture defined by the Forms Library to create a new object class that will work smoothly with the built-in or user-created object classes. Creating such new object classes and adding them to the library is simpler than it sounds. In fact it is almost the same as making a free object. This part gives you all the details of how to add new classes. In chapter 24 a global architectural overview is given of how the Forms Library works and how it communicates with the different object classes by means of events (messages). Chapter 25 describes in detail what type of events objects can receive and how they should react to them. Chapter 26 describes in detail the structure of the type `FL_OBJECT' which plays a crucial role, a role equivalent to a superclass (thus all other object classes have `FL_OBJECT' as their parent class) in object-oriented programming. One of the important aspects of an object is how to draw it on the screen. Chapter 27 gives all the details on drawing objects. The Forms Library contains a large number of routines that help you draw objects. In this chapter an overview is given of all of them. Chapter 28 gives an example illustrating on how to create a new object class. Due to the importance of button classes, special routines are provided by the Forms Library to facilitate the creation of this particular class of objects. Chapter 29 illustrates by two examples the procedures of creating new button classes using the special services. One of the examples is taken from the Forms Library itself and the other offers actual usability. Sometimes it might be desirable to alter the behavior of a built-in class slightly. Obviously a full-blown (re)implementation from scratch of the original object class is not warranted. Chapter 30.1 discusses the possibilities of using the pre-emptive handler of an object to implement derived objects.  File: xforms.info, Node: Part IV Global Structure, Next: Part IV Events, Prev: Part IV Introduction, Up: Top 25 Global Structure ******************* The Forms Library defines the basic architecture of an object class. This architecture allows different object classes developed by different programmers to work together without complications. The Forms Library consists of a main module and a number of object class modules. The object class modules are completely independent from the main module. So new object class modules can be added without any change (nor recompilation) of the main module. The main module takes care of all the global bookkeeping and the handling of events. The object class modules have to take care of all the object specific aspects, like drawing the object, reacting to particular types of user actions, etc. For each class there exists a file that contains the object class module. For example, there are files `slider.c', `box.c', `text.c', `button.c', etc. The main module communicates with the object class modules by means of events (messages if you prefer). Each object has to have a handle routine known to the main module so that it can be called whenever something needs to be done. One of the arguments passed to the handle routine is the type of event, e.g., `FL_DRAW', indicating that the object needs to be redrawn. Each object class consists of two components. One component, both its data and functions, is common to all object classes in the Forms Library. The other component is specific to the object class in question and is typically opaque. So for typical object classes, there should be routines provided by the object class to manipulate the object class specific data. Since C lacks inheritance as a language construct, inheritance is implemented in the Forms Library by pointers and the global function `*note fl_make_object()::'(1). It is helpful to understand the global architecture and the object-oriented approach of the Forms Library, it makes reading the C code easier and also adds perspective on why some of the things are implemented the way they are. In this chapter it is assumed that we want to create a new class with the name `NEW'. Creating a new object class mainly consists of writing the handle routine. There also should be a routine that adds an object of the new class to a form and associates the handle routine to it. This routine should have the following basic form: FL_OBJECT *fl_add_NEW(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label); This routine must add an object of class `NEW' to the current form. It receives the parameters `type', indicating the type of the object within the class (see below), `x', `y', `w', and `h', indicating the bounding box of the object in the current active units (mm, point or pixels), and `label' which is the label of the object. This is the routine the programmer uses to add an object of class `NEW' to a form. See below for the precise actions this routine should take. One of the tasks of `fl_add_NEW()' is to bind the event handling routine to the object. For this it will need a routine: static int handle_NEW(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, int key, void *xev); This routine is the same as the handle routine for free objects and should handle particular events for the object. `mx' and `my' contain the current mouse position and `key' the key that was pressed (if this information is related to the event). *Note Events: Part IV Events, for the types of events and the actions that should be taken. `xev' is the XEvent that caused the invocation of the handler. Note that some of the events may have a `NULL' `xev' parameter, so `xev' should be checked before dereferencing it. The routine should return whether the status of the object is changed, i.e., whether the event dispatcher should invoke this object's callback or, if no callback is set for the object, whether the object is to be returned to the application program by `*note fl_do_forms()::' or `*note fl_check_forms()::'. What constitutes a status change is obviously dependent on the specific object class and possibly its types within this class. For example, a mouse push on a radio button is considered a status change while it is not for a normal button where a status change occurs on release. Moreover, most classes have a number of other routines to change settings of the object or get information about it. In particular the following two routines often exist: void fl_set_NEW(FL_OBJECT *obj, ...); that sets particular values for the object and fl_get_NEW(FL_OBJECT *obj, ...); that returns some particular information about the object. See e.g., the routines `*note fl_set_button()::' and `*note fl_get_button()::'. * Menu: * The Routine fl_add_NEW():: ---------- Footnotes ---------- (1) There are other ways to simulate inheritance, such as including a pointer to generic objects as part of the instance specific data.  File: xforms.info, Node: The Routine fl_add_NEW(), Up: Part IV Global Structure 25.1 The Routine `fl_add_NEW()' =============================== `fl_add_NEW()' has to add a new object to the form and bind its handle routine to it. To make it consistent with other object classes and also more flexible, there should in fact be two routines: `fl_create_NEW()' that creates the object and `fl_add_NEW()' that actually adds it to the form. They normally look as follows: typedef struct { /* instance specific record */ } SPEC; FL_OBJECT *fl_create_NEW(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj; /* create a generic object */ obj = fl_make_object(FL_COLBOX, type, x, y, w, h, label, handle_NEW); /* fill in defaults */ obj->boxtype = FL_UP_BOX; /* allocate instance-specific storage and fill it with defaults */ obj->spec_size = sizeof SPEC; obj->spec = fl_calloc(1, obj->spec_size); return obj; } The constant `FL_NEW' will indicate the object class. It should be an integer. The numbers 0 to `FL_USER_CLASS_START - 1' (1000) and `FL_BEGIN_GROUP' (10000) and higher are reserved for the system and should not be used. Also it is preferable to use `fl_malloc()', `fl_calloc()', `fl_realloc()' and `fl_free()' to allocate/free the memory for the instance specific structures. These routines have the same prototypes and work the same way as those in the standard library and may offer additional debugging capabilities in future versions of the Forms Library. Also note that these functions are actually function pointers, and if desired, the application is free to assign these pointers to its own memory allocation routines. There's also a version equivalent to the `strdup()' POSIX function which used `*note fl_malloc()::': char * fl_strdup(const char *s); The object pointer returned by `*note fl_make_object()::' will have all of its fields set to some defaults (*note The Type `FL_OBJECT': Part IV The Type FL_OBJECT.). In other words, the newly created object inherits many attributes of a generic one. Any class specific defaults that are different from the generic one can be changed after `*note fl_make_object()::'. Conversion of units, if different from the default pixel, is performed within `*note fl_make_object()::' and a class module never needs to know what the prevailing unit is. After the object is created, it has to be added to a form: FL_OBJECT *fl_add_NEW(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj; obj = fl_create_NEW(type, x, y, w, h, label); fl_add_object(fl_current_form, obj); return obj; }  File: xforms.info, Node: Part IV Events, Next: Part IV The Type FL_OBJECT, Prev: Part IV Global Structure, Up: Top 26 Events ********* As indicated above, the main module of the Forms Library communicates with the objects by calling the associated handling routine with, as one of the arguments, the particular event for which action must be taken. In the following we assume that `obj' is the object to which the event is sent. The following types of events can be sent to an object: `FL_DRAW' The object has to be (re)drawn. To figure out the actual size of the object you can use the fields `obj->x', `obj->y', `obj->w' and `obj->h'. Many Xlib drawing routines require a window ID, which you can obtain from the object pointer using `FL_ObjWin(obj)'. 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 the following way: The field `obj->belowmouse' tells you whether the object is below the mouse. The field `obj->pushed' indicates whether the object is currently being pushed with the mouse. Finally, `obj->focus' indicate whether input focus is directed towards this object. Note that drawing of the object is the full responsibility of the object class, including the bounding box and the label, which can be found in the field `obj->label'. The Forms Library provides a large number of routines to help you draw object. *Note Drawing Objects: Part IV Drawing Objects, for more details on drawing objects and an overview of all available routines. One important caution about your draw event handling code: none of the high level routines (`*note fl_freeze_form()::', `*note fl_deactivate_form()::') etc. can be used. The only routines allowed to be used are (direct) drawing functions and object internal book keeping routines. Attribute modifying routines, such as `*note fl_set_object_color()::' etc. are not allowed (using them can lead to infinite recursions). In addition, (re)drawing of other objects using `*note fl_redraw_object()::' while handling `*note FL_DRAW::' will also not work. Due to the way double buffering is handled, at the time the `FL_DRAW' event is passed to the handling function (and only then) `FL_ObjWin(obj)' might return a pixmap used as the backbuffer (at least if the object is double buffered). What that means is that `FL_ObjWin(obj)' should not be used when a real window is needed. For a real window you can change the window's cursor or query the mouse position within it. You can't do either of these with the backbuffer pixmap. If there is a need to obtain the real window ID the following routine can be used: Window fl_get_real_object_window(FL_OBJECT *) To summarize: use `FL_ObjWin(obj)' when drawing and use `*note fl_get_real_object_window()::' for cursor or pointer routines. This distinction is important only while handling `FL_DRAW' events, `FL_ObjWin(obj)' should be used anywhere else. `FL_DRAWLABEL' This event typically follows `FL_DRAW' and indicates that the object label needs to be (re)drawn. If the object in question always draws its label inside the bounding box and this is taken care of by handing `FL_DRAW', you can ignore this event. `FL_ENTER' This event is sent when the mouse has entered the bounding box and might require some action. Note also that the field `obj->belowmouse' in the object is being set. If entering an objects area only changes its appearance, redrawing it normally suffices. Don't do this directly! Always redraw the object by calling `*note fl_redraw_object()::'. It will send an `FL_DRAW' event to the object but also does some other things (like setting window IDs and taking care of double buffering etc.). `FL_LEAVE' The mouse has left the bounding box. Again, normally a redraw is enough (or nothing at all). `FL_MOTION' Motion events get sent between `FL_ENTER' and `FL_LEAVE' events when the mouse position changes on the object. The mouse position is given as an argument to the handle routine. `FL_PUSH' The user has pushed a mouse button on the object. Normally this requires some actual action. The number of the mouse button pushed is given in the `key' parameter, having one of the following values: `FL_LEFT_MOUSE, FL_MBUTTON1' Left mouse button was pressed. `FL_MIDDLE_MOUSE, FL_MBUTTON2' Middle mouse button was pressed. `FL_RIGHT_MOUSE, FL_MBUTTON3' Right mouse button was pressed. `FL_SCROLLUP_MOUSE, FL_MBUTTON4' Mouse scroll wheel was rotated in up direction. `FL_SCROLLDOWN_MOUSE, FL_MBUTTON5' Mouse scroll wheel was rotated in down direction. `FL_RELEASE' The user has released the mouse button. This event is only sent if a `*note FL_PUSH::' event was sent before. `*note FL_PUSH::' event. `FL_DBLCLICK' The user has pushed a mouse button twice within a certain time limit (`FL_CLICK_TIMEOUT'), which by default is 400 msec. This event is sent after two `FL_PUSH', `FL_RELEASE' sequence. Note that `FL_DBLCLICK' is only generated for objects that have non-zero `obj->click timeout' fields and it will not be generated for events from the scroll wheel. `FL_TRPLCLICK' The user has pushed a mouse button three times within a certain time window. This event is sent after a `*note FL_DBLCLICK::', `*note FL_PUSH::', `*note FL_RELEASE::' sequence. Set click timeout to none-zero to activate `FL_TRPLCLICK'. `FL_FOCUS' Input got focussed to this object. This type of event and the next two are only sent to objects for which the field `obj->input' is set to 1 (see below). `FL_UNFOCUS' Input is no longer focussed on the object. `FL_KEYPRESS' A key was pressed. The ASCII value (or KeySym if non-ASCII) is passed to the routine via the `key' argument, modifier keys can be retrieved from the `state' member of the XEvent also passed to the function via `xev'. This event only happens between `*note FL_FOCUS::' and `*note FL_UNFOCUS::' events. Not all objects are sent keyboard events, only those that have non-zero value in field `obj->input' or `obj->wantkey'. `FL_SHORTCUT' The user used a keyboard shortcut. The shortcut used is given in the parameter key. See below for more on shortcuts. `FL_STEP' A `FL_STEP' event is sent all the time (typically about 20 times a second but possibly less often because of system delays and other time-consuming tasks) to objects for which the field `obj->automatic' has been set to a non-zero value. The handling routine receives a synthetic `MotionNotify' event as the XEvent. This can be used to make an object change appearance without user action. Clock and timer objects use this type of event. `FL_UPDATE' An `FL_UPDATE' event, like the `*note FL_STEP::' event, also gets send about every 50 msec (but less often under high load) to objects while they are "pushed", i.e., between receiving a `*note FL_PUSH::' and a `*note FL_RELEASE::' event if their `obj->want_update' field is set. Like for the `FL_STEP' event the handling routine receives a synthetic `MotionNotify' event as the XEvent. This is typically used by objects that have to perform tasks at regular time intervals while they are "pushed" (e.g., counters that need to count up or down while the mouse is pushed on one of its buttons). `FL_ATTRIB' An `FL_ATTRIB' event is sent to an object (via calling the handler function each object type must define for this purpose) whenever one of it's properties changes, be it its size, position, box type, border width, colors, label, label color, style or alignment etc. This can e.g., be used by the object to do preparations for later drawing of it or check that what got set is reasonable. It should not use this event to actually draw anything (this is to be done only when an `*note FL_DRAW::' event is received). When the handler function for events is called all the arguments it gets passed are `0'. `FL_FREEMEM' This event is sent when the object is to be freed. All memory allocated for the object internally must be freed when this event is received. `FL_OTHER' Events other than the above. These events currently include ClientMessage, Selection and possibly other window manager events. All information about the event is contained in `xev' parameter and `mx' and `my' may or may not reflect the actual position of the mouse. Many of these events might make it necessary that the object has to be redrawn or partially redrawn. Always do this using the routine `*note fl_redraw_object()::'. * Menu: * Shortcuts::  File: xforms.info, Node: Shortcuts, Up: Part IV Events 26.1 Shortcuts ============== The Forms Library has a mechanism of dealing with keyboard shortcuts. In this way the user can use the keyboard rather than the mouse for particular actions. Obviously, only "active" objects can have shortcuts (i.e., not objects like boxes, texts etc.). The mechanism works as follows. There is a routine void fl_set_object_shortcut(FL_OBJECT *obj, const char *str, int showit); with which one can bind a series of keys to an object. E.g., when `str' is `"acE#d^h"' the keys `'a'', `'c'', `'E'', `d' and `h' are associated with the object. The precise format is as follows: Any character in the string is considered as a shortcut, except `'^'' and `'#'', which stand for combinations with the `' and `' keys. (The case of the key following `'#'' or `'^'' is not important, i.e., no distiction is made between e.g., `"^C"' and `"^c"', both encode the key combination `C' as well as `C'.) The key `'^'' itself can be set as a shortcut key by using `"^^"' in the string defining the shortcut. The key `'#'' can be obtained as a shortcut by using th string `"^#"'. So, e.g., `"#^#"' encodes `#'. The `' key can be given as `"^["'. Another special character not mentioned yet is `'&'', which indicates function and arrow keys. Use a sequence starting with `'&'' and directly followed by a number between 1 and 35 to represent one of the function keys. For example, `"&2"' stands for the `' function key. The four cursors keys (up, down, right, and left) can be given as `"&A"', `"&B"', `"&C"' and `"&D"', respectively. The key `'&'' itself can be obtained as a shortcut by prefixing it with `'^''. The argument `showit' tells whether the shortcut letter in the object label should be underlined if a match exists. Although the entire object label is searched for matches, only the first alphanumerical character in the shortcut string is used. E.g., for the object label `"foobar"' the shortcut `"oO"' would result in a match at the first `o' in `"foobar"' while `"Oo"' would not. However, `"^O"' and `"#O"' would match since for keys used in combination with `' and `' no distiction is made between upper and lower case. To use other special keys not described above as shortcuts, the following routine must be used void fl_set_object_shortcutkey(FL_OBJECT *obj, unsigned int key); where `key' is an X KeySym, for example `XK_Home', `XK_F1' etc. Note that the function `*note fl_set_object_shortcutkey()::' always appends the key specified to the current shortcuts while `*note fl_set_object_shortcut()::' resets the shortcuts. Of course, special keys can't be underlined. Now, whenever the user presses one of these keys, an `*note FL_SHORTCUT::' event is sent to the object. The key pressed is passed to the handle routine (in the argument `key'). Combinations with the `' key are given by adding `*note FL_ALT_MASK::' (currently the 25th bit, i.e., `0x1000000') to the ASCII value of the key. E.g., the key combinations `E' and `e' are passed as `*note FL_ALT_MASK:: + 'E''. The object can now take action accordingly. If you use shortcuts to manipulate class object specific things, you will need to create a routine to communicate with the user, e.g., `fl_set_NEW_shortcut()', and do your own internal bookkeeping to track what keys do what and then call `*note fl_set_object_shortcut()::' to register the shortcut in the event dispatching module. The idea is NOT that the user himself calls `*note fl_set_object_shortcut()::' but that the class provides a routine for this that also keeps track of the required internal bookkeeping. Of course, if there is no internal bookkeeping, a macro to this effect will suffice. For example `*note fl_set_button_shortcut()::' is defined as `*note fl_set_object_shortcut()::'. The order in which keys are handled is as follows: First for a key it is tested whether any object in the form has the key as a shortcut. If yes, the first of those objects gets the shortcut event. Otherwise, the key is checked to see if it is `' or `'. If it is, the `obj->wantkey' field is checked. If the field does not contain `*note FL_KEY_TAB::' bit, input is focussed on the next input field. Otherwise the key is sent to the current input field. This means that input objects only get a `' or `' key sent to them if in the `obj->wantkey' field the `*note FL_KEY_TAB::' bit is set. This is e.g., used in multi-line input fields. If the object wants all cursor keys (including `' etc.), the `obj->wantkey' field must have the `*note FL_KEY_SPECIAL::' bit set. To summarize, the `obj->wantkey' field can take on the following values (or the bit-wise or of them): `FL_KEY_NORMAL' The default. The object receives left and right cursor, `' and `' keys plus all normal keys (0-255) except `' `'. `FL_KEY_TAB' Object receives the `', `' as well as the `' and `' cursor keys. `FL_KEY_SPECIAL' The object receives all keys with a KeySym above 255 which aren't already covered by `FL_KEY_NORMAL' and `FL_KEY_TAB' (e.g., function keys etc.) `FL_KEY_ALL' Object receives all keys. This way it is possible for a non-input object (i.e., if `obj->input' is zero) to obtain special keyboard event by setting `obj->wantkey' to `*note FL_KEY_SPECIAL::'.  File: xforms.info, Node: Part IV The Type FL_OBJECT, Next: Part IV Drawing Objects, Prev: Part IV Events, Up: Top 27 The Type `FL_OBJECT' *********************** Each object has a number of attributes. Some of them are used by the main routine, some have a fixed meaning and should never be altered by the class routines and some are free for the class routines to use. Please always use accessor methods when available instead of using or changing the object's properties directly. Below we consider some of them that are likely to be used in new classes. `int objclass' This indicates the class of the object (e.g., `FL_BUTTON', `FL_SLIDER', `FL_NEW' etc.) The user can query the class of an object using the function `*note fl_get_object_objclass()::'. `int type' This indicates the type of the object within the class. Types are integer constants that should be defined in a header file named after the object class, e.g., `NEW.h'. Their use is completely free. For example, in the slider class the type is used to distinguish between horizontal and vertical sliders. At least one type should exist and the user should always provide it (just for consistency). They should be numbered from 0 upwards. The user can query the type of an object using the function `*note fl_get_object_type()::'. `int boxtype' This is the type of the bounding box for the object. The handling routine for the object, e.g., `handle_NEW()', has to take care that this is actually drawn. Note that there is a routine for drawing boxes, see below. The user can change or query the boxtype of an object with the functions `*note fl_set_object_boxtype()::' and `*note fl_get_object_boxtype()::'. `FL_Coord x, y, w, h' These are the coordinates and sizes that indicate the bounding box of the object. They always have to be provided when adding an object. The system uses them e.g., to determine if the object is below the mouse. The class routines should use them to draw the object in the correct size, etc. Note that these values will change when the user resizes the form window. So never assume anything about their values but always recheck them when drawing the object. The routines `*note fl_get_object_geometry()::', `*note fl_get_object_position()::' and `*note fl_get_object_size()::' should be used to determine position and/or size. To change the position and/or size of an object never change the elements of the structures directly (except in a function like `fl_add_NEW()') but always use `*note fl_set_object_geometry()::', `*note fl_set_object_position()::', `*note fl_set_object_size()::' and `*note fl_move_object()::'! Also note that the `y'-member is always relative to the top of the form the object belongs to, even if the user had called `*note fl_flip_yorigin()::' - this only results in `y'-values passed by and returned to the user when using functions like `*note fl_set_object_position()::' or `*note fl_get_object_position()::' getting "flipped", internally always the normal coordinate system is used. `unsigned int resize' Controls if the object should be resized if the form it is on is resized. The options are `FL_RESIZE_NONE', `FL_RESIZE_X', `FL_RESIZE_Y' and `FL_RESIZE_ALL'. The default is `FL_RESIZE_ALL' which is the bitwise OR of `FL_RESIZE_X' and `FL_RESIZE_Y'. Instead of accessing this element directly better use the functions `*note fl_get_object_resize()::' and `*note fl_set_object_resize()::'. `unsigned int nwgravity, segravity' These two variables control how the object is placed relative to its position prior to resizing. Instead of accessing these elements directly use `*note fl_get_object_gravity()::' and `*note fl_set_object_gravity()::'. `FL_COLOR col1, col2' These are two color indices in the internal color lookup table. The class routines are free to use them or not. The user can change them using the routine `*note fl_set_object_color()::' or inspect the colors with `*note fl_get_object_color()::'. The routine `fl_add_NEW()' should fill in defaults. `char *label' This is a pointer to an allocated text string. This can be used by class routines to provide a label for the object. The class routines may not forget to allocate storage for it when it sets the pointer itself, i.e., doesn't use `*note fl_set_object_label()::' - an empty label should be the empty string and not just a `NULL' pointer. The user can change it using the routines `*note fl_set_object_label()::' and `*note fl_set_object_label_f()::' or ask for it using `*note fl_get_object_label()::'. The label must be drawn by the routine handling the object when it receives a `FL_DRAWLABEL' event (or it could be part of the code for `FL_DRAW' event). For non-offsetted labels, i.e., the alignment is relative to the entire bounding box, simply calling `*note fl_draw_object_label()::' should be enough. `FL_COLOR lcol' The color of the label. The class routines can freely use this. The user can set it with `*note fl_set_object_lcolor()::' and test it with `*note fl_get_object_lcolor()::'. `int lsize' The size of the font used to draw the label. The class routines can freely use this. The user can set it with `*note fl_set_object_lsize()::'. and test it with `*note fl_get_object_lsize()::'. `int lstyle' The style of the font the label os drawn in, i.e., the number of the font in which it should be drawn. The class routines can freely use this. The user can set it with `*note fl_set_object_lstyle()::' and test it with `*note fl_get_object_lstyle()::'. `int align' The alignment of the label with respect to the object. Again it is up to the class routines to do something useful with this. The possible values are `*note FL_ALIGN_LEFT::', `*note FL_ALIGN_RIGHT::', `*note FL_ALIGN_TOP::', `*note FL_ALIGN_BOTTOM::', `*note FL_ALIGN_CENTER::', `*note FL_ALIGN_LEFT_TOP::', `*note FL_ALIGN_RIGHT_TOP::', `*note FL_ALIGN_LEFT_BOTTOM::' and `*note FL_ALIGN_RIGHT_BOTTOM::'. The value should be bitwise ORed with `*note FL_ALIGN_INSIDE::' if the label will be within the bounding box of the object. The user can set this using the routine `*note fl_set_object_lalign()::' and test it with `*note fl_set_object_lalign()::'. `int bw' An integer indicating the border width of the object. Negative values indicate the up box should look "softer" (in which case no black line of 1 pixel width is drawn around the objects box). The user can set a different border width using `*note fl_set_object_bw()::'. `long *shortcut' A pointer to long containing all shortcuts (as keysyms) defined for the object (also see the previous chapter). You should never need them because they are fully handled by the main routines. `void *spec' This is a pointer that points to any class specific information. For example, for sliders it stores the minimum, maximum and current value of the slider. Most classes (except the most simple ones like boxes and texts) will need this. The function for adding a new object (`fl_add_NEW()') has to allocate storage for it. Whenever the object receives the event `FL_FREEMEM' it should free this memory. `int visible' Indicates whether the object is visible. The class routines don't have to do anything with this variable. When the object is not visible the main routine will never try to draw it or send events to it. By default objects are visible. The visisbility of an object can be tested using the `*note fl_object_is_visible()::' function. Note that a this doesn't guarantee that the object is visible on the screen, for this also the form the object belongs to needs to be visible, in which case `*note fl_form_is_visible()::' returns true. `int active' Indicates whether the object is active, i.e., wants to receive events other than `FL_DRAW'. Static objects, such as text and boxes are inactive. This property should be set in the `fl_add_NEW()' routine if required. By default objects are active. This attribute can be changed by using the functions `*note fl_deactivate_object()::' and `*note fl_activate_object()::' and the current state can be determined by calling `*note fl_object_is_active()::'. `int input' Indicates whether this object can receive keyboard input. If not, events related to keyboard input are not sent to the object. The default value of `input' is false. It should be set by `fl_add_NEW()' if required. Note that not all keys are sent (see member `wantkey' below). `int wantkey' An input object normally does not receive `' or `' keystrokes or any other keys except those that have values between 0-255, the `' and `' arrow keys and `' and `' (`' and `' are normally used to switch between input objects). By setting this field to `FL_KEY_TAB' enforces that the object receives also these two keys as well as the `' and `' arrow keys and `' and `' when it has the focus. To receive other special keys (e.g., function keys) `FL_KEY_SPECIAL' must be set in `wantkey'. By setting `wantkey' to `FL_KEY_ALL' all keys are sent to the object. `unsigned int click_timeout' If non-zero this indicates the the maximum elapsed time (in msec) between two mouse clicks to be considered a double click. A zero value disables double/triple click detection. The user can set or query this value using the functions `*note fl_set_object_dblclick()::' and `*note fl_get_object_dblclick()::'. `int automatic' An object is automatic if it automatically (without user actions) has to change its contents. Automatic objects get a `FL_STEP' event about every 50 msec. For example the object class `FL_CLOCK' is automatic. `automatic' by default is false. To set this property use `*note fl_set_object_automatic()::' (don't set the object member directly except from within a function like `fl_add_NEW()', in other contexts some extra work is required) and to test the object for it use `*note fl_object_is_automatic()::'. `int belowmouse' This indicates whether the mouse is on this object. It is set and reset by the main routine. The class routines should never change it but can use it to draw or handle the object differently. `int pushed' This indicates whether the mouse is pushed within the bounding box of the object. It is set and reset by the main routine. Class routines should never change it but can use it to draw or handle objects differently. `int focus' Indicates whether keyboard input is sent to this object. It is set and reset by the main routine. Never change it but you can use its value. `FL_HANDLEPTR handle' This is a pointer to the interaction handling routine for the object. `fl_add_NEW()' sets this by providing the correct handling routine. Normally it is never used (except by the main routine) or changed although there might be situations in which you want to change the interaction handling routine for an object, due to some user action. `FL_OBJECT *next, *prev' `FL_FORM *form' These are pointers to other objects in the form and to the form itself. They are used by the main routines. The class routines should not change them. `void *c_vdata' A void pointer for the class routine. The main module does not reference or modify this field in any way. The object classes, including the built-in ones, may use this field. `char *c_cdata' A char pointer for the class routine. The main module does not reference or modify this field in any way. The object classes, including the built-in ones, may use this field. `long c_ldata' A long variable for the class routine. The main module does not reference or modify this field in any way. The object classes, including the built-in ones, may use this field. `void *u_vdata' A void pointer for the application program. The main module does not reference or modify this field in any way and neither should the class routines. `char *u_cdata' A char pointer for the application program. The main module does not reference or modify this field in any way and neither should the class routines. `long u_ldata' A long variable provided for the application program. `FL_CALLBACKPTR object_callback' The callback routine that the application program assigned to the object and that the system invokes when the user does something with the object. `long argument' The argument to be passed to the callback routine when invoked. `int how_return' Determines under what circumstances the object is returned by e.g., `*note fl_do_forms()::' or the callback function for the object is invoked. This can be either `*note FL_RETURN_NONE::' Object gets never returned or its callback invoked `*note FL_RETURN_CHANGED::' Return object or invoke callback when state of object changed. `*note FL_RETURN_END::' Return object or invoke callback at end of interaction, normally when the mouse key is released or, in the case of input objects, the object has lost focus. `*note FL_RETURN_END_CHANGED::' Return object or invoke callback only when interaction has ended and the state of the object has changed. `*note FL_RETURN_SELECTION::' Return object or invoke callback if e.g., in a browser a line was selected. `*note FL_RETURN_SELECTION::' Return object or invoke callback if e.g., in a browser a line was deselected. `*note FL_RETURN_ALWAYS::' Return object or invoke callback whenever interaction has ended or the state of the object has changed. Never change this element of the structure directly but use the function `*note fl_set_object_return()::' instead! Especially in the case of objects having child objects also the corresponding settings for child objects may need changes and which automatically get adjusted when the above function is used. `int returned' Set to what calling the object handling function did return (and pruned to what the object is supposed to return according to the `how_return' element). Can be either `*note FL_RETURN_NONE::' Handling function did `FL_RETURN_NONE' (i.e., `0'). `*note FL_RETURN_CHANGED::' Handling function detected a change of the objects state. `*note FL_RETURN_END::' Handling function detected end of interaction with object. `*note FL_RETURN_CHANGED::' and `*note FL_RETURN_END::' are bits that can be bitwise ored. If both are set this indicates that the objects state was changed and the interaction ended. The generic object construction routine typedef int (*FL_HANDLEPTR)(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, int key, void *raw_event); FL_OBJECT *fl_make_object(int objclass, int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label, FL_HANDLEPTR handle); allocates a chunk of memory appropriate for all object classes and initializes the newly allocated object to the following state: obj->resize = FL_RESIZE_X | FL_RESIZE_Y; obj->nwgravity = obj->segravity = FL_NoGravity; obj->boxtype = FL_NO_BOX; obj->align = FL_ALIGN_CENTER | FL_ALIGN_INSIDE; obj->lcol = FL_BLACK; obj->lsize = FL_DEFAULT_SIZE; obj->lstyle = FL_NORMAL_STYLE; obj->col1 = FL_COL1; obj->col2 = FL_MCOL; obj->wantkey = FL_KEY_NORMAL; obj->active = 1; obj->visible = 1; obj->bw = borderWidth_resource_set ? resource_val : FL_BOUND_WIDTH; obj->u_ldata = 0; obj->u_vdata = 0; obj->spec = NULL; obj->how_return = FL_RETURN_CHANGED In some situations it can be also useful to make an object a child of another object. An example is the scrollbar object. It has three child objects, a slider and two buttons, which all three are children of the scrollbar object. To make an object `child' a child object of an object named `parent' use the function void fl_add_child(FL_OBJECT *parent, FL_OBJECT *child); When creating a composite object you will typically add callbacks for the child object that handle what happens on events for these child objects (e.g., for the scrollbar the buttons have callbacks that update the internal state for the scrollbar object and result in the slider getting shifted). Within these callback functions the `returned' elements of the parent can be changed to influence if and what gets reported to the application via `*note fl_do_forms()::'. There is rarely any need for the new object class to know how the object is added to a form and how the Forms Library manages the geometry, e.g., does an object have its own window etc. Nonetheless if this information is required, use `*note FL_ObjWin()::' on the object to obtain the window resource ID of the window the object belongs to. Beware that an object window ID may be shared with other objects(1). Always remove an object from the screen with `*note fl_hide_object()::'. The class routine/application may reference the following members of the `FL FORM' structure to obtain information on the status of the form, but should not modify them directly: `int visible' Indicates if the form is visible on the screen (mapped). Never change it directly, use `*note fl_show_form()::' or `*note fl_hide_form()::' instead. `int deactivated' Indicates if the form is deactivated. Never change it directly, use `*note fl_activate_form()::' or `*note fl_deactivate_form()::' instead. `FL OBJECT *focusobj' This pointer points to the object on the form that has the input focus. `FL OBJECT *first' The first object on the form. Pointer to a linked list. `Window window' The forms window. ---------- Footnotes ---------- (1) The only exception is the canvas class where the window ID is guaranteed to be non-shared.  File: xforms.info, Node: Part IV Drawing Objects, Next: Part IV An Example, Prev: Part IV The Type FL_OBJECT, Up: Top 28 Drawing Objects ****************** * Menu: * General Remarks: General Remarks * Color Handling: Color Handling * Mouse Handling: Mouse Handling * Clipping: Clipping * Getting the Size: Getting the Size * Font Handling: Font Handling * Drawing Functions: Drawing Functions  File: xforms.info, Node: General Remarks, Next: Color Handling, Up: Part IV Drawing Objects 28.1 General Remarks ==================== An important aspect of a new object class (or a free object) is how to draw it. As indicated above this should happen when the event `FL_DRAW' is received by the object. The place and size, i.e., the bounding box, of the object are indicated by the object tructure fields `obj->x', `obj->y', `obj->w' and `obj->h'. Forms are drawn in the Forms Library default visual or the user requested visual, which could be any of the X supported visuals. Hence, preferably your classes should run well in all visuals. The Forms Library tries to hide as much as possible the information about graphics mode and, in general, using the built-in drawing routines is the best approach. Here are some details about graphics state in case such information is needed. All state information is kept in a global structure of type `FL_State' and there is a total of six such structures, `fl_state[6]', each for every visual class. The structure contains among others the following members: `XVisualInfo *xvinfo' Many properties of the current visual can be obtained from this member. `int depth' The depth of the visual. Same as what you get from `xvinfo'. `int vclass' The visual class, `PseudoColor', `TrueColor' etc. `Colormap colormap' Current active colormap valid for the current visual for the entire Forms Library (except `FL_CANVAS'). You can allocate colors from this colormap, but you should never free it. `Window trailblazer' This is a valid window resource ID created in the current visual with the colormap mentioned above. This member is useful if you have to call, before the form becomes active (thus does not have a window ID), some Xlib routines that require a valid window. A macro, `fl_default_window()', is defined to return this member and use of the macro is encouraged. `GC gc[16]' A total of 16 GCs appropriate for the current visual and depth. The first (`gc[0]') is the default `GC' used by many internal routines and should be modified with care. It is a good idea to use only the top 8 `GC's (8-15) for your free object so that future Forms Library extensions won't interfere with your program. Since many internal drawing routines use the Forms Library's default `GC' (`gc[0]'), it can change anytime whenever drawing occurs. Therefore, if you are using this GC for some of your own drawing routines make sure to always set the proper value before using it. The currently active visual class (`TrueColor', `PseudoColor' etc.) can be obtained by the following function/macro: int fl_get_form_vclass(FL_FORM *form); int fl_get_vclass(void); The value returned can be used as an index into the array `*note fl_state::' of `*note FL_State::' structures. Note that `*note fl_get_vclass()::' should only be used within a class/new object module where there can be no confusion what the "current" form is. Other information about the graphics mode can be obtained by using visual class as an index into the `fl_state' structure array. For example, to print the current visual depth, code similar to the following can be used: int vmode = fl_get_vclass(); printf("depth: %d\n", fl_state[vmode].depth); Note that `fl_state[]' for indices other than the currently active visual class might not be valid. In almost all Xlib calls, the connection to the X server and current window ID are needed. The Forms Library comes with some utility functions/macros to facilitate easy utilization of Xlib calls. Since the current version of Forms Library only maintains a single connection, the global variable `*note fl_display::' can be used where required. However, it is recommended that you use `fl_get_display()' or `FL_FormDisplay(Form *form)' instead since the function/macro version has the advantage that your program will remain compatible with future (possibly multi-connection) versions of the Forms Library. There are a couple of ways to find out the "current" window ID, defined as the window ID the object receiving dispatcher's messages like `FL_DRAW' etc. belongs to. If the object's address is available, `FL_ObjWin(obj)' will suffice. Otherwise the function `*note fl_winget()::' (see below) can be used. There are other routines that might be useful: FL_FORM *fl_win_to_form(Window win); This function takes a window ID win and returns the form the window belongs to or `None' on failure.  File: xforms.info, Node: Color Handling, Next: Mouse Handling, Prev: General Remarks, Up: Part IV Drawing Objects 28.2 Color Handling =================== As mentioned earlier, Forms Library keeps an internal colormap, initialized to predefined colors. The predefined colors do not correspond to pixel values the server understands but are indexes into the colormap. Therefore, they can't be used in any of the `GC' altering or Xlib routines. To get the actual pixel value the X server understands, use the following routine unsigned long fl_get_pixel(FL_COLOR col); To e.g., get the pixel value of the red color, use unsigned long red_pixel; red_pixel = fl_get_pixel(FL_RED); To change the foreground color in the Forms Library's default `GC' (`gc[0]') use void fl_color(FL_COLOR col); To set the background color in the default `GC' use instead void fl_bk_color(FL_COLOR col); To set foreground or background in `GC's other than the Forms Library's default, the following functions exist: void fl_set_foreground(GC gc, FL_COLOR col); void fl_set_background(GC gc, FL_COLOR col); which is equivalent to the following Xlib calls XSetForeground(fl_get_display(), gc, fl_get_pixel(color)); XSetBackground(fl_get_display(), gc, fl_get_pixel(color)); To free allocated colors from the default colormap, use the following routine void fl_free_colors(FL_COLOR *cols, int n); This function frees the `n' colors stored in the array of colormap indices `cols'. You shouldn't do that for the reserved colors, i.e., colors with indices below `FL_FREE_COL1'. In case the pixel values (instead of the index into the colormap) are known, the following routine can be used to free the colors from the default colormap void fl_free_pixels(unsigned long *pixels, int n); Note that the internal colormap maintained by the Forms Library is not updated. This is in general harmless. To modify or query the internal colormap, use the following routines: unsigned long fl_mapcolor(FL_COLOR col, int red, int green, int blue) long fl_mapcolorname(FL_COLOR col, const char *name); unsigned long fl_getmcolor(FL_COLOR col, int *red, int *green, int *blue); The first function, `*note fl_mapcolor()::' sets a the color indexed by `color' to the color given by the `red', `green' and `blue', returning the colors pixel value. The second function, `*note fl_mapcolorname()::', sets the color in the colormap indexed by `color' to the color named `name', where `name' must be a valid name from the system's color database file `rgb.txt'. It also returns the colors pixel value or -1 on failure. The last function, `*note fl_getmcolor()::', returns the RGB values of the color indexed by `color' in the second to third argument pointers and the pixel value as the return value (or -1, cast to `unsigned long', on failure).  File: xforms.info, Node: Mouse Handling, Next: Clipping, Prev: Color Handling, Up: Part IV Drawing Objects 28.3 Mouse Handling =================== The coordinate system used corresponds directly to that of the screen. But object coordinates are relative to the upper-left corner of the form the object belongs to. To obtain the position of the mouse relative to a certain form or window, use the routines Window fl_get_form_mouse(FL_FORM *form, FL_Coord *x, FL_Coord *y, unsigned *keymask) Window fl_get_win_mouse(Window win, FL_Coord *x, FL_Coord *y, unsigned *keymask); The functions return the ID of the window the mouse is in. Upon return `x' and `y' are set to the mouse position relative to the form or window and `keymask' contains information on modifier keys (same as the the corresponding `XQueryPointer()' argument). A similar routine exists that can be used to obtain the mouse location relative to the root window Window fl_get_mouse(FL_Coord *x, FL_Coord *y, unsigned *keymask); The function returns the ID of the window the mouse is in. To move the mouse to a specific location relative to the root window, use the following routine void fl_set_mouse(FL_Coord x, FL_Coord y); Use this function sparingly, it can be extremely annoying for the user if the mouse position is changed by a program.  File: xforms.info, Node: Clipping, Next: Getting the Size, Prev: Mouse Handling, Up: Part IV Drawing Objects 28.4 Clipping ============= To avoid drawing outside a box the following routine exists: void fl_set_clipping(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h); It sets a clipping region in the Forms Library's default `GC' used for drawing (but not for output of text, see below). `x', `y', `w' and `h' define the area drawing is to restrict to and are relative to the window/form that will be drawn to. In this way you can prevent drawing over other objects. Under some circumstances XForms also does it's own clipping, i.e., while drawing due to a exposure event. This is called "global clipping". Thus the clipping area you have set via a call of `*note fl_set_clipping()::' may get restricted even further due this global clipping. You can check if there's clipping set for the default `GC' using the function int fl_is_clipped(int include_global); which returns `1' if clipping is switched on and `0' otherwise. The `include_global' argument tells the function if global clipping is to be included in the answer or not (i.e., if the argument is `0' only clipping set via `*note fl_set_clipping()::' is reported). The area currently clipped to is returned by the function int fl_get_clipping(int include_global, FL_Coord *x,FL_Coord *y, FL_Coord *width, FL_Coord *height); On return the four pointer arguments are set to the position and size of the clipping rectangle (at least if clipping is switched on) and the qreturn value of this function is the same as that of `*note fl_is_clipped()::'. The `include_global' argument has the same meaning as for `*note fl_is_clipped()::', i.e., it controls if the effects of global clipping is included in the results. When finished with drawing always use void fl_unset_clipping(void); to switch clipping of again. You also can check and obtain the current settings for global clipping using the functions int fl_is_global_clipped(void); int fl_get_global_clipping(FL_Coord *x,FL_Coord *y, FL_Coord *width, FL_Coord *height); Clipping for text is controlled via a different `GC' and thus needs to be set, tested for and unset using a different set of functions: void fl_set_text_clipping(FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h); int fl_is_text_clipped(int include_global); int fl_get_text_clipping(int include_global, FL_Coord *x,FL_Coord *y, FL_Coord *width, FL_Coord *height); void fl_unset_text_clipping(void); Finally, there are functions to set and unset the clipping for a specific `GC': void fl_set_gc_clipping(GC gc, FL_Coord x, FL_Coord y, FL_Coord width, FL_Coord height); void fl_unset_gc_clipping(GC gc); Please note that setting clipping for a `GC' will always further restrict the region to the region of global clipping (if it is on at the moment the function is called) and unsetting clipping will still retain global clipping if this is on at the moment the second function is invoked (if it is currently on can be checked using the `*note fl_is_global_clipped()::').  File: xforms.info, Node: Getting the Size, Next: Font Handling, Prev: Clipping, Up: Part IV Drawing Objects 28.5 Getting the Size ===================== To obtain the bounding box of an object with the label taken into account (in contrast to the result of the `*note fl_get_object_geometry():: function which doesn't include a label that isn't inside the object' the following routine exists: void fl_get_object_bbox(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y, FL_Coord *w, FL_Coord *h); For drawing text at the correct places you will need some information about the sizes of characters and strings. The following routines are provided: int fl_get_char_height(int style, int size, int *ascent, int *descent) int fl_get_char_width(int style, int size); These two routines return the maximum height and width of the font used, where `size' indicates the point size for the font and `style' is the style in which the text is to be drawn. The first function, `*note fl_get_char_height()::', also returns the height above and below the baseline of the font via the `ascent' and `descent' arguments (if they aren't `NULL' pointers). A list of valid styles can be found in Section 3.11.3. To obtain the width and height information for a specific string use the following routines: int fl_get_string_width(int style, int size, const char *str, int len); int fl_get_string_height(int style, int size, const char *str, int len, int *ascent, int *descent); where `len' is the length of the string `str'. The functions return the width and height of the string, respectively. The second function also returns the height above and below the fonts baseline if `ascent' and `descent' aren't `NULL' pointers. Note that the string may not contain newline characters `'\n'' and that the height calculated from the ascent and descent of those characters in the string that extend the most above and below the fonts baseline. It thus may not be suitable for calculating line spacings, for that use the `*note fl_get_char_height()::' or `*note fl_get_string_dimension()::' function. There exists also a routine that returns the width and height of a string in one call. In addition, the string passed can contain embedded newline characters `'\n'' and the routine will make proper adjustment so the values returned are large enough to contain the multiple lines of text. The height of each of the lines is the fonts height. void fl_get_string_dimension(int style, int size, const char *str, int len, int *width, int *height);  File: xforms.info, Node: Font Handling, Next: Drawing Functions, Prev: Getting the Size, Up: Part IV Drawing Objects 28.6 Font Handling ================== Sometimes it can be useful to get the X font structure for a particular size and style as used in the Forms Library. For this purpose, the following routine exists: [const] XFontStruct *fl_get_fontstruct(int style, int size); The structure returned can be used in, say, setting the font in a particular `GC': XFontStruct *xfs = fl_get_fontstruct(FL_TIMESBOLD_STYLE, FL_HUGE_SIZE); XSetFont(fl_get_display(), mygc, xfs->fid); The caller is not allowed to free the structure returned by `*note fl_get_fontstruct()::', it's just a pointer to an internal structure!  File: xforms.info, Node: Drawing Functions, Prev: Font Handling, Up: Part IV Drawing Objects 28.7 Drawing Functions ====================== There are a number of routines that help you draw objects on the screen. All XForms's internal drawing routine draws into the "current window", defined as the window the object that uses the drawing routine belongs to. If that's not what you need, the following routines can be used to set or query the current window: void fl_winset(Window win); Window fl_winget(void); One caveat about `*note fl_winget()::' is that it can return `None' if called outside of an object's event handler, depending on where the mouse is. Thus, the return value of this function should be checked when called outside of an object's event handler. It is important to remember that unless the following drawing commands are issued while handling the `FL_DRAW' or `FL_DRAWLABEL' event (which is not generally recommended), it is the application's responsibility to set the proper drawable using `*note fl_winset()::'. The most basic drawing routines are for drawing rectangles: void fl_rectf(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); void fl_rect(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); Both functions draw a rectangle on the screen in color `col'. While `*note fl_rectf()::' draws a filled rectangle, `*note fl_rect()::' just draws the outline in the given color. To draw a filled (with color `col') rectangle with a black border use void fl_rectbound(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); To draw a rectangle with rounded corners (filled or just the outlined) employ void fl_roundrectf(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); void fl_roundrect(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); To draw a general polygon, use one of the following routines typedef struct { short x, y; } FL_POINT; void fl_polyf(FL_POINT *xpoint, int n, FL_COLOR col); void fl_polyl(FL_POINT *xpoint, int n, FL_COLOR col); void fl_polybound(FL_POINT *xpoint, int n, FL_COLOR col); `*note fl_polyf()::' draws a filled polygon defined by `n' points, `*note fl_polyl()::' the ouline of a polygon and `*note fl_polybound()::' a filled polygon with a black outline. Note: *all* polygon routines require that the array `xpoint' has spaces for *`n+1'* points, i.e., one more than then number of points you intend to draw! To draw an ellipse. either filled, open (with the outline drawn in the given color), or filled with a black border the following routines can be used (use `w' equal to `h' to get a circle): void fl_ovalf(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); void fl_ovall(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); void fl_ovalbound(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); The `x' and `y' arguments are the upper left hand corner of the ellipse, while `w' and `h' are its width and height. Note: `*note fl_ovall()::' (with two 'l') isn't a typo, the trailing 'l' it's meant indicate that only a line will be drawn. And there's also the function void fl_ovalf(int fill, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); which is invoked by both (the macros) `*note fl_ovalf()::' and `*note fl_ovall()::' with the first argument `fill' set to either `1' or `0'. To simplify drawing circles there are three additional functions. The first one draws an (open) circle (with the circumfence in the given color), the second one a filled circle, and the last one a filled circle with a black circumfence: void fl_circ(FL_Coord x, FL_Coord y, FL_Coord r, FL_COLOR col); void fl_circf(FL_Coord x, FL_Coord y, FL_Coord r, FL_COLOR col); void fl_circbound(FL_Coord x, FL_Coord y, FL_Coord r, FL_COLOR col); Here `x' and `y' are the coordinates of the center of the circle, `r' is its radius and `col' the color to be used. To draw circular arcs, either open or filled, the following routines can be used void fl_arc(FL_Coord x, FL_Coord y, FL_Coord radius, int start_theta, int end_theta, FL_COLOR col); void fl_arcf(FL_Coord x, FL_Coord y, FL_Coord radius, int start_theta, int end_theta, FL_COLOR col); `x' and `y' are the coordinates of the center and `r' is the radius. `start_theta' and `end_theta' are the starting and ending angles of the arc in units of tenths of a degree (where 0 stands for a direction of 3 o'clock, i.e., the right-most point of a circle), and `x' and `y' are the center of the arc. If the difference between `theta_end' and `theta_start' is larger than 3600 (360 degrees), drawing is truncated to 360 degrees. To draw elliptical arcs the following routine can be used: void fl_pieslice(int fill, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, int start_theta, int end_theta, FL_COLOR col); `x' and `y' are the upper left hand corner of the box enclosing the ellipse that the pieslice is part of and `w' and `h' the width and height of that box. `start_theta' and `end_theta', to be given in tenth of a degree, specify the starting and ending angles measured from zero degrees (3 o'clock). Depending on circumstance, elliptical arc may be more easily drawn using the following routine void fl_ovalarc(int fill, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, int theta, int dtheta, FL_COLOR col); Here `theta' specifies the starting angle (again measured in tenth of a degree and with 0 at the 3 o'clock position), and `dtheta' specifies both the direction and extent of the arc. If `dtheta' is positive the arc is drawn in counter-clockwise direction from the starting point defined by `theta', otherwise in clockwise direction. If `dtheta' is larger than 3600 it is truncated to 3600. To connect two points with a straight line, use void fl_line(FL_Coord x1, FL_Coord y1, FL_Coord x2, FL_Coord y2, FL_COLOR col); There is also a routine to draw a line along the diagonal of a box (to draw a horizontal line set `h' to 1, not to 0): void fl_diagline(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col); To draw connected line segments between `n' points use void fl_lines(FL_POINT *points, int n, FL_COLOR col); All coordinates in points are relative to the origin of the drawable. There are also routines to draw one or more pixels void fl_point(FL_Coord x, FL_Coord y, FL_COLOR col); void fl_points(FL_POINT *p, int np, FL_COLOR col); As usual, all coordinates are relative to the origin of the drawable. Note that these routines are meant for you to draw a few pixels, not images consisting of tens of thousands of pixels of varying colors. For that kind of drawing `XPutImage(3)' should be used. Or better yet, use the image support in the Forms Library (see *note Images: Part VI Images.). Also it's usually better when drawing multiple points to use fl_points(), even if that means that the application program has to pre-sort and group the pixels of the same color. To change the line width or style, the following convenience functions are available void fl_linewidth(int lw); void fl_linestyle(int style); Set `lw' to 0 to reset the line width to the servers default. Line styles can take on the following values (also see `XChangeGC(3)') `FL SOLID' Solid line. Default and most efficient. `FL DOT' Dotted line. `FL DASH' Dashed line. `FL DOTDASH' Dash-dot-dash line. `FL LONGDASH' Long dashed line. `FL USERDASH' Dashed line, but the dash pattern is user definable via `*note fl_dashedlinestyle()::'. Only the odd numbered segments are drawn with the foreground color. `FL USERDOUBLEDASH' Similar to `FL_LINE_USERDASH' but both even and odd numbered segments are drawn, with the even numbered segments drawn in the background color (as set by `*note fl_bk_color()::'). The following routine can be used to change the dash pattern for `FL_USERDASH' and `FL USERDOUBLEDASH': void fl_dashedlinestyle(const char *dash, int ndashes) Each element of the array `dash' is the length of a segment of the pattern in pixels (0 is not allowed). Dashed lines are drawn as alternating segments, each with the length of an element in `dash'. Thus the overall length of the dash pattern, in pixels, is the sum of all elements of `dash'. When the pattern is used up but the line to draw is longer it used from the start again. The following example code specifies a long dash (9 pixels) to come first, then a skip (3 pixels), a short dash (2 pixels) and then again a skip (3 pixels). After this sequence, the pattern repeats. char ldash_sdash[] = {9, 3, 2, 3}; fl_dashedlinestyle(ldash_sdash, 4); If `dash' is `NULL' or `ndashes' is `0' (or the `dash' array contains an element set to `0') a default pattern of 4 pixels on and 4 fixels off is set. It is important to remember to call `*note fl_dashedlinestyle()::' whenever `FL_USERDASH' is used to set the dash pattern, otherwise whatever the last pattern was will be used. To use the default dash pattern you can pass `NULL' as the dash parameter to `*note fl_dashedlinestyle()::'. By default, all lines are drawn so they overwrite the destination pixel values. It is possible to change the drawing mode so the destination pixel values play a role in the final pixel value. void fl_drawmode(int mode); There are 16 different possible settings for `mode' (see a Xlib programming manual for all the gory details). A of the more useful ones are `GXcopy' Default overwrite mode. Final pixel value = Src `GXxor' Bitwise XOR (exclusive-or) of the pixel value to be drawn with the pixel value already on the screen. Useful for rubber-banding. `GXand' Bitwise AND of the pixel value to be drawn with the pixel value already on the screen. `GXor' Bitwise OR of the pixel value to be drawn with the pixel value already on the screen. `GXinvert' Just invert the pixel values already on the screen. To obtain the current settings of the line drawing attributes use the following routines int fl_get_linewidth(void); int fl_get_linestyle(void); int fl_get_drawmode(void); There are also a number of high-level drawing routines available. To draw boxes the following routine exists. Almost any object class will use it to draw the bounding box of the object. void fl_drw_box(int style, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col, int bw); `style' is the type of the box, e.g., `FL_DOWN_BOX'. `x', `y', `w', and `h' indicate the size of the box. `col' is the color and `bw' is the width of the boundary, which typically should be given the value `obj->bw' or `FL_BOUND_WIDTH'. Note that a negative border width indicates a "softer" up box. See the demo program `borderwidth.c' for the visual effect of different border widths. There is also a routine for drawing a frame: void fl_drw_frame(int style, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col, int bw) All parameters have the usual meaning except that the frame is drawn outside of the bounding box specified. For drawing text there are two routines: void fl_drw_text(int align, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col, int style, int size, const char *str); void fl_drw_text_beside(int align, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col, int style, int size, const char *str); where `align' is the alignment, namely, `FL ALIGN LEFT', `FL ALIGN CENTER' etc. `x', `y', `w' and `h' indicate the bounding box, `col' is the color of the text, `size' is the size of the font to use (in points) and `style' is the font style to be used (see *note Label Attributes and Fonts::, for valid styles). Finally, `str' is the string itself, possibly containing embedded newline characters. `*note fl_drw_text()::' draws the text inside the bounding box according to the alignment requested while `*note fl_drw_text_beside()::' draws the text aligned outside of the box. These two routines interpret a text string starting with the character `@' differently in drawing some symbols instead. Note that `*note fl_drw_text()::' puts a padding of 5 pixels in vertical direction and 4 in horizontal around the text. Thus the bounding box should be 10 pixels wider and 8 pixels higher than required for the text to be drawn. The following routine can also be used to draw text and, in addition, a cursor can optionally be drawn void fl_drw_text_cursor(int align, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, FL_COLOR col, int style, int size, char *str, int FL_COLOR ccol, int pos); where `ccol' is the color of the cursor and `pos' is its position which indicates the index of the character in `str' before which to draw the cursor (-1 means show no cursor). This routine does no interpretion of the special character `@' nor does it add padding around the text. Given a bounding box and the size of an object (e.g., a label) to draw, the following routine can be used to obtain the position of where to draw it with a certain alignment and including padding: void fl_get_align_xy(int align, int x, int y, int w, int h, int obj_xsize, int obj_ysize, int xmargin, int ymargin, int *xpos, int *ypos); This routine works regardless if the object is to be drawn inside or outside of the bounding box specified by `x', `y', `w' and `h'. `obj_xsize' and `obj->ysize' are the width and height of the object to be drawn and `xmargin' and `ymargin' is the additional padding to use. `xpos' and `ypos' return the position to be used for drawing the object. For drawing object labels the following routines might be more convenient: void fl_draw_object_label(FL_OBJECT *obj) void fl_draw_object_label_outside(FL_OBJECT *obj); Both routines assume that the alignment is relative to the full bounding box of the object. The first routine draws the label according to the alignment, which could be inside or outside of the bounding box. The second routine will always draw the label outside of the bounding box. An important aspect of (re)drawing an object is efficiency which can result in flicker and non-responsiveness if not handled with care. For simple objects like buttons or objects that do not have "movable parts", drawing efficiency is not a serious issue although you can never be too fast. For complex objects, especially those that a user can interactively change, special care should be taken. The most important rule for efficient drawing is not to draw if you don't have to, regardless how simple the drawing is. Given the networking nature of X, simple or not depends not only on the host/server speed but also the connection. What this strategy entails is that the drawing should be broken into blocks and depending on the context, draw/update only those parts that need to.  File: xforms.info, Node: Part IV An Example, Next: Part IV New Buttons, Prev: Part IV Drawing Objects, Up: Top 29 An Example ************* Let us work through an example of how to create a simple object class named `colorbox'. Assume that we want a class with the following behavior: it should normally be red. When the user presses the mouse on it it should turn blue. When the user releases the mouse button the object should turn red again and be returned to the application program. Further, the class module should keep a total count how many times the box got pushed. The first thing to do is to define some constants in a file named `colbox.h'. This file should at least contain the class number and one or more types: /* Class number must be between FL_USER_CLASS_START and FL_USER_CLASS_END */ #define FL_COLBOX (FL_USER_CLASS_START + 1) #define FL_NORMAL_COLBOX 0 /* The only type */ Note that the type must start from zero onward. Normally it should also contain some defaults for the boxtype and label alignment etc. The include file also has to declare all the functions available for this object class. I.e., it should contain: extern FL_OBJECT *fl_create_colbox(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); extern FL_OBJECT *fl_add_colbox(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); extern int fl_get_colorbox(FL_OBJECT *); Now we have to write a module `colbox.c' that contains the different routines. First of all we need routines to create an object of the new type and to add it to the current form. We also need to have a counter that keeps track of number of times the colbox is pushed. They would look as follows: typedef struct { int counter; /* no. of times pushed */ } COLBOX_SPEC; FL_OBJECT *fl_create_colbox(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj; /* create a generic object class with an appropriate ID */ obj = fl_make_object(FL_COLBOX, type, x, y, w, h, label, handle_colbox); /* initialize some members */ obj->col1 = FL_RED; obj->col2 = FL_BLUE; /* create class specific structures and initialize */ obj->spec = fl_malloc(sizeof *obj->spec); obj->spec->counter = 0; return obj; } FL_OBJECT *fl_add_colbox(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj = fl_create_colbox(type, x, y, w, h, label); fl_add_object(fl_current_form, obj); return obj; } The fields `col1' and `col2' are used to store the two colors red and blue such that the user can change them when required with the routine `*note fl_set_object_color()::'. What remains is to write the handling routine `handle_colbox()'. It has to react to three types of events: `FL_DRAW', `FL_PUSH' and `FL_RELEASE'. Also, when the box is pushed, the counter should be incremented to keep a total count. Note that whether or not the mouse is pushed on the object is indicated in the field `obj->pushed'. Hence, when pushing and releasing the mouse the only thing that needs to be done is redrawing the object. This leads to the following piece of code: static int handle_colbox(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, int key, void *xev) { switch (event) { case FL_DRAW: /* Draw box */ fl_drw_box(obj->boxtype, obj->x,obj->y, obj->w, obj->h, obj->pushed ? obj->col2 : obj->col1, obj->bw); /* fall through */ case FL_DRAWLABEL: /* Draw label */ fl_draw_object_label(obj); break; case FL_PUSH: ((COLBOX_SPEC *) obj->spec)->counter++; fl_redraw_object(obj); break; case FL_RELEASE: fl_redraw_object(obj); return 1; /* report back to application! */ case FL_FREEMEM: fl_free(obj->spec); break; } return 0; } That is the whole piece of code. Of course, since the `COLBOX_SPEC' structure is invisible outside of `colbox.c', the following routine should be provided to return the total number of times the colbox was pushed: int fl_get_colbox(FL_OBJECT *obj) { if (!obj || obj->objclass != FL_COLBOX) { fprintf(stderr, "fl_get_colbox: Bad argument or wrong type); return -1; } return ((COLBOX_SPEC *) obj->spec)->counter; } To use it, compile it into a file `colbox.o'. An application program that wants to use the new object class simply should include `colbox.h' and link with `colbox.o' when compiling the program. It can then use the routine `fl_add_colbox()' to add objects of the new type to a form.  File: xforms.info, Node: Part IV New Buttons, Next: Part IV Using a Pre-emptive Handler, Prev: Part IV An Example, Up: Top 30 New Buttons ************** Since button-like object is one of the most important, if not _the_ most important, classes in graphical user interfaces, Forms Library provides, in addition to the ones explained earlier, a few more routines that make create new buttons or button-like objects even easier. These routines take care of the communication between the main module and the button handler so all new button classes created using this scheme behave consistently. Within this scheme, the programmer only has to write a drawing function that draws the button. There is no need to handle events or messages from the main module and all types of buttons, radio, pushed or normal are completely taken care of by the generic button class. Further, `*note fl_get_button()::' and `*note fl_set_button()::' work automatically without adding any code for them. Forms Library provides two routines to facilitate the creation of new button object classes. One of the routines is FL_OBJECT *fl_create_generic_button(int objclass, int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label); which can be used to create a generic button that has all the properties of a real button except that this generic button does not know what the real button looks like. The other routine `*note fl_add_button_class()::', discussed below, can be used to register a drawing routine that completes the creation of a new button. All button or button-like objects have the following instance-specific structure, defined in `forms.h', that can be used to obtain information about the current status of the button: typedef struct { Pixmap pixmap; /* for bitmap/pixmap button only */ Pixmap mask; /* for bitmap/pixmap button only */ unsigned int bits_w, /* for bitmap/pixmap button only */ bits_h; int val; /* whether it's pushed */ int mousebut; /* mouse button that caused the push */ int timdel; /* time since last touch (TOUCH buttons)*/ int event; /* what event triggered the redraw */ long cspecl; /* for non-generic class specific data */ void * cspec; /* for non-generic class specific data */ char * file; /* filename for the pixmap/bitmap file */ } FL_BUTTON_STRUCT; Of all its members, only `val' and `mousebut' probably will be consulted by the drawing function. `cspecl' and `cspecv' are useful for keeping track of class status other than those supported by the generic button (e.g., you might want to add a third color to a button for whatever purposes.) These two members are neither referenced nor changed by the generic button class. Making this structure visible somewhat breaks the Forms Library's convention of hiding the instance specific data but the convenience and consistency gained by this far outweights the compromise on data hiding. The basic procedures in creating a new button-like object are as follows. First, just like creating any other object classes, you have to decide on a class ID, an integer between `FL_USER_CLASS_START' (1001) and `FL_USER_CLASS_END' (9999) inclusive. Then write a header file so that application programs can use this new class. The header file should include the class ID definition and function prototypes specific to this new class. After the header file is created, you will have to write C functions that create and draw the button. You also will need an interface routine to place the newly created button onto a form. After creating the generic button, the new button class should be made known to the button driver via the following function void fl_add_button_class(int objclass, void (*draw)(FL_OBJECT *), void (*cleanup)(FL_BUTTON_SPEC *)); where `objclass' is the class ID, and `draw' is a function that will be called to draw the button. `cleanup' is a function that will be called prior to destroying the button. You need a cleanup function only if the drawing routine uses the `cspecv' field of `FL_BUTTON_STRUCT' to hold memory allocated dynamically by the new button. We use two examples to show how new buttons are created. The first example is taken from the button class in the Forms Library, i.e., its real working source code that implements the button class. To illustrate the entire process of creating this class, let us call this button class `FL_NBUTTON'. First we create a header file to be included in an application program that uses this button class: #ifndef NBUTTON_H_ #define NBUTTON_H_ #define FL_NBUTTON FL_USER_CLASS_START extern FL_OBJECT *fl_create_nbutton(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); extern FL_OBJECT *fl_add_nbutton(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); #endif Now to the drawing function. We use `obj->col1' for the normal color of the box and `obj->col2' for the color of the box when pushed. We also add an extra property so that when mouse moves over the button box, the box changes color. The following is the full source code that implements this: static void draw_nbutton(FL_OBJECT *obj) { FL_COLOR col; /* box color. If pushed we use obj->col2, otherwise use obj->col1 */ col = ((FL_BUTTON_STRUCT *) obj->spec)->val ? obj->col2 : obj->col1; /* if mouse is on top of the button, we change the color of * the button to a different color. However we only do this * if the * box has the default color. */ if (obj->belowmouse && col == FL_COL1) col = FL_MCOL; /* If original button is an up_box and it is being pushed, * we draw a down_box. Otherwise, don't have to change * the boxtype */ if ( obj->boxtype == FL_UP_BOX && ((FL_BUTTON_STRUCT *) obj->spec)->val) fl_drw_box(FL_DOWN_BOX, obj->x, obj->y, obj->w, obj->h, col, obj->bw); else fl_drw_box(obj->boxtype, obj->x, obj->y, obj->w, obj->h, col, obj->bw); /* draw the button label */ fl_drw_object_label(obj); /* if the button is a return button, draw the return symbol. * Note that size and style are 0 as they are not used when * drawing symbols */ if (obj->type == FL_RETURN_BUTTON) fl_drw_text(FL_ALIGN_CENTER, obj->x + obj->w - 0.8 * obj->h - 1, obj->y + 0.2 * obj->h, 0.6 * obj->h, 0.6 * obj->h, obj->lcol, 0, 0, "@returnarrow"); } Note that when drawing symbols, the style and size are irrelevent and set to zero in `*note fl_drw_text()::' above. Since we don't use the `cspecv' field to point to dynamically allocated memory we don't have to write a clean-up function. Next, following the standard procedures of the Forms Library, we code a separate routine that creates the new button(1) FL_OBJECT *fl_create_nbutton(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj; obj = fl_create_generic_button(FL_NBUTTON, type, x, y, w, h, label); fl_add_button_class(FL_NBUTTON, draw_nbutton, NULL); obj->col1 = FL_COL1; /* normal color */ obj->col2 = FL_MCOL; /* pushed color */ obj->align = FL_ALIGN_CENTER; /* button label placement */ return obj; } You will also need a routine that adds the newly created button to a form FL_OBJECT *fl_add_nbutton(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj = fl_create_nbutton(type, x, y, w, h, label); fl_add_object(fl_current_form, obj); return obj; } This concludes the creation of button class `FL_NBUTTON'. The next example implements a button that might be added to the Forms Library in the future. We call this button a crossbutton. Normally, this button shows a small up box with a label on the right. When pushed, the up box becomes a down box and a small cross appears on top of it. This kind of button obviously is best used as a push button or a radio button. However, the Forms Library does not enforce this. It can be enforced, however, by the application program or by the object class developers. [image src="xforms_images/crossbutton.png"] We choose to use `obj->col1' as the color of the box and `obj->col2' as the color of the cross (remember these two colors are changeable by the application program via `*note fl_set_object_color()::'). Note that this decision on color use is somewhat arbitrary, we could have easily made `obj->col2' the color of the button when pushed and use `obj->spec->cspecl' for the cross color (another routine named e.g., `fl_set_crossbutton_crosscol()' should be provided to change the cross color in this case). We start by defining the class ID and declaring the utility routine prototypes in the header file `crossbut.h': #ifndef CROSSBUTTON_H_ #define CROSSBUTTON_H_ #define FL_CROSSBUTTON (FL_USER_CLASS_START + 2) extern FL_OBJECT *fl_add_crossbutton(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); extern FL_OBJECT *fl_create_crossbutton(int, FL_Coord, FL_Coord, FL_Coord, FL_Coord, const char *); #endif Next we write the actual code that implements crossbutton class and put it into `crossbut.c': /* routines implementing the "crossbutton" class */ #include #include "crossbut.h" /** How to draw it */ static void draw_crossbutton(FL_OBJECT *obj) { FL_Coord xx, yy, ww, hh; FL_BUTTON_STRUCT *sp = obj->spec; /* There is no visual change when mouse enters/leaves the box */ if (sp->event == FL_ENTER || sp->event == FL_LEAVE) return; /* draw the bounding box first */ fl_drw_box(obj->boxtype, obj->x, obj->y, obj->w, obj->h, obj->col1, obj->bw); /* Draw the box that contains the cross */ ww = hh = (0.5 * FL_min(obj->w, obj->h)) - 1; xx = obj->x + FL_abs(obj->bw); yy = obj->y + (obj->h - hh) / 2; /* If pushed, draw a down box with the cross */ if (sp->val) { fl_drw_box(FL_DOWN_BOX, xx, yy, ww, hh, obj->col1, obj->bw); fl_drw_text(FL_ALIGN_CENTER, xx - 2, yy - 2, ww + 4, hh + 4, obj->col2, 0, 0, "@9plus"); } else fl_drw_box(FL_UP_BOX, xx, yy, ww, hh, obj->col1, obj->bw); /* Draw the label */ if (obj->align == FL_ALIGN_CENTER) fl_drw_text(FL_ALIGN_LEFT, xx + ww + 2, obj->y, 0, obj->h, obj->lcol, obj->lstyle, obj->lsize, obj->label); else fl_draw_object_label_outside(obj); if (obj->type == FL_RETURN_BUTTON) fl_drw_text(FL_ALIGN_CENTER, obj->x + obj->w - 0.8 * obj->h, obj->y + 0.2 * obj->h, 0.6 * obj->h, 0.6 * obj->h, obj->lcol, 0, 0, "@returnarrow"); } This button class is somewhat different from the normal button class (`FL_BUTTON') in that we enforce the appearance of a crossbutton so that an un-pushed crossbutton always has an upbox and a pushed one always has a downbox. Note that the box that contains the cross is not the bounding box of a crossbutton although it can be if the drawing function is coded so. The rest of the code simply takes care of interfaces: /* creation routine */ FL_OBJECT * fl_create_crossbutton(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj; fl_add_button_class(FL_CROSSBUTTON, draw_crossbutton, NULL); /* if you want to make cross button only available for * push or radio buttons, do it here as follows: if (type != FL_PUSH_BUTTON && type != FL_RADIO_BUTTON) type = FL_PUSH_BUTTON; */ obj = fl_create_generic_button(FL_CROSSBUTTON, type, x, y, w, h, label); obj->boxtype = FL_NO_BOX; obj->col2 = FL_BLACK; /* cross color */ return obj; } /* interface routine to add a crossbutton to a form */ FL_OBJECT *fl_add_crossbutton(int type, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h, const char *label) { FL_OBJECT *obj = fl_create_crossbutton(type, x, y, w, h, label); fl_add_object(fl_current_form, obj); return obj; } The actual code is in the demo directory, see the files `crossbut.c' and `crossbut.h'. An application program only needs to include the header file `crossbut.h' and link with `crossbut.o' to use this new object class. There is no need to change or re-compile the Forms Library. Of course, if you really like the new object class, you can modify the system header file `forms.h' to include your new class header file automatically (either through inclusion at compile time or by including the actual header). You can also place the object file (`crossbut.o') in `libforms.a' and `libforms.so' if you wish. Note however that this will make your application programs dependent on your personal version of the library. Since the current version of Form Designer does not support any new object classes developed as outlined above, the best approach is to use another object class as stubs when creating a form, for example, you might want to use checkbutton as stubs for the crossbutton. Once the position and size are satisfactory, generate the C-code and then manually change checkbutton to crossbutton. You probably can automate this with some scripts. Finally there is a demo program utilizing this new button class. The program is `newbutton.c'. ---------- Footnotes ---------- (1) A separate creation routine is useful for integration into the Form Designer.  File: xforms.info, Node: Part IV Using a Pre-emptive Handler, Next: Part V, Prev: Part IV New Buttons, Up: Top 31 Using a Pre-emptive Handler ****************************** Pre-emptive handlers came into being due to reasons not related to developing new classes. They are provided for the application programs to have access to the current state or event of a particular object. However, with some care, this preemptive handler can be used to override parts of the original built-in handler thus yielding a new class of objects. As mentioned earlier, an object module communicates with the main module via events. Central part of the module is the event handler, which determines how an object responds to various events such as mouse clicks or a key presses. Now a pre-emptive handler is a function which, if installed, gets called first by the main module when an event for the object occurs. The pre-emptive handler has the option to override the built-in handler by informing the main module not to call the built-in handler (and a possibly also installed post handler), thus altering the behavior of the object. A post handler, on the other hand, is called when the object handler has finished its tasks and thus does not offer the capability of overriding the built-in handler. It is much safer, however. The API to install a pre- or post-handler for an object is as follows typedef int (*FL_HANDLEPTR)(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, int key, void *raw_event); void fl_set_object_prehandler(FL_OBJECT *obj, FL_HANDLEPTR pre_handler); void fl_set_object_posthandler(FL_OBJECT *obj, FL_HANDLEPTR post_handler); `event' is a generic event of the Forms Library, that is, `*note FL_DRAW::', `*note FL_ENTER::' etc. Parameters `mx' and `my' are the mouse position and `key' is the key pressed. The last parameter `raw_event' is a pointer to the XEvent (cast to a void pointer due to the different types of Xevents) that caused the invocation of the pre- or post-handler. But note: not all events of the Form Library have a corresponding Xevent and thus dereferencing of `xev' should only be done after making sure it is not `NULL'. The pre- and post-handler have the same function prototype as the built-in handler. Actually they are called with exactly the same parameters by the event dispatcher. The pre-handler should return `FL_PREEMPT' to prevent the dispatcher from calling the normal object handler for events and `!FL_PREEMPT' if the objects handler for is to be invoked next. The post-handler may return whatever it wants since the return value is not used. Note that a post-handler will receive all events even if the object the post-handler is registered for does not. For example, a post-handler for a box (a static object that only receives `*note FL_DRAW::') receives all events. Note that when an object has been de-activated using `*note fl_deactivate_object()::' (or the whole form the object belongs to is de-activated via calls of `*note fl_deactivate_form()::' or `*note fl_deactivate_all_forms()::') also pre-emptive and post-handlers won't get invoked for the object. See the demo programs `preemptive.c' and `xyplotall.c' for examples. Bear in mind that modifying the built-in behavior is in general not a good idea. Using a pre-emptive handler for the purpose of "peeking", however, is quite legitimate and can be useful in some situations.  File: xforms.info, Node: Part V, Next: Part V Overview of Main Functions, Prev: Part IV Using a Pre-emptive Handler, Up: Top _Part V - General Informations_ ******************************* * Menu: * Part V Overview of Main Functions:: * Part V Some Useful Functions:: * Part V Resources for Forms Library:: * Part V Dirty Tricks:: * Part V Trouble Shooting::  File: xforms.info, Node: Part V Overview of Main Functions, Next: Part V Some Useful Functions, Prev: Part V, Up: Top 32 Overview of Main Functions ***************************** In this chapter we give a brief overview of all the main functions that are available. For an overview of all routines related to specific object classes see Part III. * Menu: * Version Information:: * Initialization:: * Creating Forms:: * Object Attributes:: * Doing Interaction:: * Signals:: * Idle Callbacks and Timeouts:: * Global Variables::  File: xforms.info, Node: Version Information, Next: Initialization, Up: Part V Overview of Main Functions 32.1 Version Information ======================== The header file `forms.h' defines three symbolic constants which you can use to conditionally compile your application. They are `FL_VERSION' The major version number. `FL_REVISION' Revision number. `FL_INCLUDE_VERSION' `1000 * FL_VERSION + FL_REVISION' There is also a routine that can be used to obtain the library version at run time: int fl_library_version(int *version, int *revision) The function returns a consolidated version information, computed as `1000 * version + revision'. For example, for library version 1 revision 21 (1.21), the function returns a value of 1021 with `version' and `revision' (if not `NULL') set to 1 and 21, respectively. It is always a good idea to check if the header and the run time library are of the same version and take appropriate actions when they are not. This is especially important for versions less than 1. To obtain the version number of the library used in an executable, run the command with `-flversion' option, which will print the complete version information.  File: xforms.info, Node: Initialization, Next: Creating Forms, Prev: Version Information, Up: Part V Overview of Main Functions 32.2 Initialization =================== The routine Display *fl_initialize(int *argc, char *argv[], const char *appclass, XrmOptionDescList app_opt, int n_app_opt); initializes the Forms Library and returns a pointer to the `Display' structure if a connection could be made, otherwise `NULL'. This function must be called before any other calls to the Forms Library (except `*note fl_set_defaults()::' and a few other functions that alter some of the defaults of the library). The meaning of the arguments is as follows `argc, argv' Number and array of the command line arguments the application was started with. The application name is derived from `argv[0]' by stripping leading path names and trailing period and extension, if any. Due to the way the X resources (and command line argument parsing) work, the executable name should not contain a dot `.' or a star `*'. `appclass' The application class name, which typically is the generic name for all instances of this application. If no meaningful class name exists, it is typically given (or converted to if non given) as the application name with the first letter capitalized (second if the first letter is an X). `app_opt' Specifies how to parse the application-specific resources. `n_app_opt' Number of entries in the option list. The `*note fl_initialize()::' function builds the resource database, calls the Xlib `XrmParseCommand()' function to parse the command line arguments and performs other per display initialization. After the creation of the database, it is associated with the display via `XrmSetDatabase()', so the application can get at it if necessary. All recognized options are removed from the argument list and their corresponding values set. The XForms library provides appropriate defaults for all options. The following are recognized by the library: *Option* *Type* *Meaning* *Default* `-fldebug' level int Print debug information 0 (off) `-name' appname string Change application name none `-flversion' Print version of the library `-sync' Synchronous X11 mode (debug) false `-display' string Set (remote) host `$DISPLAY' host:dpy `-visual' class string TrueColor, PseudoColor... best `-depth' depth int Set prefered visual depth best `-vid' id long Set prefered visual ID 0 `-private' Force use of private colormap false `-shared' Force use of shared colormap false `-stdcmap' Force use of standard colormap false `-double' Enable double buffering for false forms `-bw' width int Set object border width 1 `-rgamma' gamma float Set red gamma 1.0 `-ggamma' gamma float Set green gamma 1.0 `-bgamma' gamma float Set blue gamma 1.0 In the above table "best" means the visual that has the most colors, which may or may not be the server's default. There is a special command option `-visual Default' that sets both the visual and depth to the X servers default. If a visual ID is requested, it overrides depth or visual if specified. The visual ID can also be requested programmatically (before `*note fl_initialize()::' is called) via the function void fl_set_visualID(long id); Note that all command line options can be abbreviated, thus if the application program uses single character options, they might clash with the built-ins. For example, if you use `-g' as a command line option to indicate geometry, it might not work as `-g' matches `-ggamma' in the absence of `-ggamma'. Thus you should avoid using single character command line options. If the border width is set to a negative number, all objects appear to have a softer appearance. Older version of the library used a larger default for the border width of 3. As mentioned the `*note fl_initialize()::' function removes all the above listed values from the command line arguments, leaving you with a cleaned-up list. To get again at the complete list you can use the function char **fl_get_cmdline_args( int *arg_cnt ); returning a copy to the values from the original list and their number via the `arg_cnt' argument. Depending on your application XForms defaults may or may not be appropriate. E.g., on machines capable of 24 bits visuals, Forms Library always selects the deeper 24 bits visual. If your application only uses a limited number of colors, it might be faster if a visual other than 24 bits is selected. There are a couple of ways to override the default settings. You can provide an application specific resource database distributed with your program. The easiest way, however, is to set up your own program defaults programmatically without affecting the users' ability to override them with command line options. For this, you can use the following routine before calling `*note fl_initialize()::': void fl_set_defaults(unsigned long mask, FL_IOPT *flopt); In addition to setting a preferred visual, this function can also be used to set other program defaults, such as label font size, unit of measure for form sizes etc. The following table lists the fields, masks and their meanings of `*note FL_IOPT::': Structure Mask Name Meaning --------------------------------------------------------------------------- `typedef struct {' `int debug;' `FL_PDDebug' Debug level (0-5) `int depth;' `FL_PDDepth' Preferred visual depth `int vclass;' `FL_PDVisual' Prefered visual, `TrueColor' etc. `int `FL_PDDouble' Simulate double buffering doubleBuffer;' `int `FL_PDButtonFontSize' Default button label font size buttonFontSize;' `int `FL_PDMenuFontSize' Menu label font size menuFontSize;' `int `FL_PDChoiceFontSize' Choice label and choice text choiceFontSize;' font size `int `FL_PDBrowserFontSize' Browser label and text font size browserFontSize;' `int `FL_PDInputFontSize' Input label and text font size inputFontSize;' `int `FL_PDLabelFontSize' Label font size for all other labelFontSize;' objects (box, pixmap etc.) `int pupFontSize;' `FL_PDPupFontSize' Font size for pop-ups `int `FL_PDPrivateMap' Select private colormap if privateColormap;' appropriate `int `FL_PDSharedMap' Force use of shared colormap sharedColormap;' `int `FL_PDStandardMap' Force use of standard colormap standardColormap;' `int `FL_PDScrollbarType' Scrollbar type to use for scrollbarType;' browser and input `int ulThickness;' `FL_PDULThickness' Underline thickness `int ulPropWidth;' `FL_PDULPropWidth' Underline width, 0 for const. width fonts `int `FL_PDBS' Turn BackingStore on or off backingStore;' `int coordUnit;' `FL_PDCoordUnit' Unit of measure: pixel, mm, point `int borderWidth;' `FL_PDBorderWidth' Default border width `} FL IOPT;' A special visual designation, `FL_DefaultVisual' and a command line option equivalent, `-visual Default' are provided to set the program default to the server's default visual class and depth. If you set up your resource specifications to use class names instead of instance names, users can then list instance resources under an arbitrary name that is specified with the `-name' option. Coordinate units can be in pixels, points (1/72 inch), mm (millimeters), cp (centi-point, i.e., 1/100 of a point) or cmm (centi-millimeter). The the type of unit in use can be queried or set via the functions int fl_get_coordunit(void); void fl_set_coordunit(int coordUnit); `coordUnit' can have the following values: `FL_COORD_PIXEL', `FL_COORD_POINT', `FL_COORD_MM', `FL_COORD_centiPOINT' and `FL_COORD_centiMM'. The unit in use can be changed anytime, but typically you would do this prior to creating a form, presumably to make the size of the form screen resolution independent. The basic steps in doing this may look something like the following: int oldcoordUnit = fl_get_coordunit(); fl_set_coordunit(FL_COORD_POINT); fl_bgn_form(...); /* add more objects */ fl_end_form(); fl_set_coordunit(oldcoordunit); Some of the defaults are "magic" in that their exact values depend on the context or platform. For example, the underline thickness by default is 1 for normal fonts and 2 for bold fonts. There exists a convenience function to set the application default border width void fl_set_border_width(int border_width) which is equivalent to FL_IOPT fl_cntl; fl_cntl.borderWidth = border_width; fl_set_defaults(FL_PDBorderWidth, &fl_cntl); Typically this function, if used, should appear before `*note fl_initialize()::' is called so the user has the option to override the default via resource or command line options. The cirrent setting of the borderwidth can also tested via int fl_get_border_width(void); To change the default scrollbar type (which is `THIN_SCROLLBAR') used in browser and input object, the following convenience function can be used: void fl_set_scrollbar_type(int type); where `type' can be one of the following `FL_NORMAL_SCROLLBAR' Basic scrollbar `FL_THIN_SCROLLBAR' Thin scrollbar `FL_NICE_SCROLLBAR' Nice scrollbar `FL_PLAIN_SCROLLBAR' Similar to thin scrollbar, but not as fancy Setting the scrollbar type before calling `*note fl_initialize()::' is equivalent to FL_IOPT fl_cntl; fl_cntl.scrollbarType = type; fl_set_defaults(FL_PDScrollbarType, &fl_cntl); It is recommended that this function be used before `*note fl_initialize()::' so the user has the option to override the default through application resources. Prior to version 0.80 the origin of XForms' coordinate system was at the lower left-hand corner of the form. The new Form Designer will convert the form definition file to the new coordinate system, i.e., with the origin at the upper left-hand corner, so no manual intervention is required. To help those who lost the `.fd' files or otherwise can't use a newer version of `fdesign', a compatibility function is provided void fl_flip_yorigin(void); Note however that this function must be called prior to `*note fl_initialize()::' and is a no-op after that. If this function has been called functions like `*note fl_get_object_position()::' or `*note fl_get_object_bbox()::', reporting an objects positions and bounding box, will return `y'-coordinates in the old-fashioned coordinate system with the origin at the left bottom corner of the form. Similarly, the functions for setting or changing an objects position (`*note fl_set_object_position()::' and `*note fl_move_object()::') then expect to receive arguments for the `y'-coordinates in this system. The `y'-coordinate stored in the object itself (i.e., `obj->y') is always for the normal coordinate system with the origin at the top left corner. For proportional font, substituting tabs with spaces is not always appropriate because this most likely will fail to align text properly. Instead, a tab is treated as an absolute measure of distance, in pixels, and a tab stop will always end at multiples of this distance. Application program can adjust this distance by setting the tab stops using the following routine void fl_set_tabstop(const char *s); where `s' is a string whose width in pixels is to be used as the tab length. The font used to calculate the width is the same font that is used to render the string in which the tab is embedded. The default `"aaaaaaaa"', i.e., eight `'a''s. Before we proceed further, some comments about double buffering are in order. Since Xlib does not support double buffering, Forms Library simulates this functionality with pixmap bit-bliting. In practice, the effect is hardly distinguishable from double buffering and performance is on par with multi-buffering extensions (It is slower than drawing into a window directly on most workstations however). Bear in mind that a pixmap can be resource hungry, so use this option with discretion. In addition to using double buffering throughout an application, it is also possible to use double buffering on a per-form or per-object basis by using the following routines: void fl_set_form_dblbuffer(FL_FORM *form, int yes_no); void fl_set_object_dblbuffer(FL_OBJECT *obj, int yes_no); Currently double buffering for objects having a non-rectangular box might not work well. A nonrectangular box means that there are regions within the bounding box that should not be painted, which is not easily done without complex and expensive clipping and unacceptable inefficiency. XForms gets around this by painting these regions with the form's backface color. In most cases, this should prove to be adequate. If needed, you can modify the background of the pixmap by changing `obj->dbl_background' after switching to double buffer. Normally the Forms Library reports errors to `stderr'. This can be avoided or modified by registering an error handling function void fl_set_error_handler(void (*user_handler)(const char *where, const char *fmt,...)); The library will call the `user_handler' function with a string indicating in which function an error occured and a formatting string (see `sprintf()') followed by zero or more arguments. To restore the default handler, call the function again with `user_handler' set to `NULL'. You can call this function anytime and as many times as you wish. You can also instruct the default message handler to log the error to a file instead of printing to `stderr' void fl_set_error_logfp(FILE *fp); For example fl_set_error_logfp(fopen("/dev/null","w")); redirects all error messages to `/dev/null', effectively turning off the default error reporting to `stderr'. In XForms versions older than 1.0.01 for some error messages, in addition to being printed to stderr, a dialog box were shown that requires actions from the user. This could be turned off and on with the function void fl_show_errors(int show); where `show' indicates whether to show (1) or not show (0) the errors. With newer versions of the Forms Library this function has no effect. The fonts used in all forms can be changed using the routines int fl_set_font_name(int n, const char *name); int fl_set_font_name_f(int n, const char *fmt, ,,,); The first function just accepts a simple string while the second constructs the font name from a format string just as it's used for `printf()' etc. and the following arguments. The first argument, `n', must be a number between 0 and `FL_MAXFONTS-1'. The function returns `0' on success, `1' if called before proper initialization of the library and `-1' for either invalid arguments (`name' or the result of the expansion of the format string doesn't name an available font, `n' negative or not less than `FL_MAXFONTS'). *Note Label Attributes and Fonts::, for details. A redraw of all forms is required to actually see the change for visible forms. Since the dimension of an object is typically given in pixels, depending on the server resolution and the font used, this can lead to unsatisfactory user interfaces. For example, a button designed to (just) contain a label in a 10 pt font on a 75 DPI monitor will have the label overflow the button on a 100 DPI monitor. This comes about because a character of a 10 pt font when rendered with `75 DPI' resolution may have 10 pixels while the same character in the same 10 pt font with 100 DPI resolution may have 14 pixels. Thus, when designing the interfaces, leave a few extra pixels for the object. Or use a resolution independent unit, such as point, or centi-point etc. Using a resolution independent unit for the object size should solve the font problems, theoretically. In practice, this approach may still prove to be vulnerable. The reason is the discreteness of both the font resolution and the monitor/server resolutions. The standard X fonts only come in two discrete resolutions, 75 DPI and 100 DPI. Due to the variations in monitor resolutions, the theoretically identical sized font, say a 10 pt font, can vary in sizes (pixels) by up to 30%, depending on the server (rendering a font on a 80 DPI monitor will cause errors in sizes regardless if a 75 DPI or 100 DPI font is used.) This has not even taken into account the fact that a surprising number of systems have wrong font paths (e.g., a 90 DPI monitor using 75 DPI fonts etc.). With the theoretical and practical problems associated with X fonts, it is not practical for XForms to hard-code default font resolution and it is not practical to use the resolution information obtained from the server either as information obtained from the server regarding monitor resolution is highly unreliable. Thus, XForms does not insist on using fonts with specific resolutions and instead it leaves the freedom to select the default fonts of appropriate resolutions to the system administrators. Given all these uncertainties regarding fonts, as a workaround, XForms provides a function that can be used to adjust the object size dynamically according to the actual fonts loaded: double fl_adjust_form_size(FL_FORM *form); This function works by computing the size (in pixels) of every object on the form that has an inside label and compares it to the size of the object. Scaling factors are computed for all object labels that don't fit. The maximum scaling factor found is then used to scale the form so every object label fits inside the object. It will never shrink a form. The function returns the resulting scaling factor. In scaling the aspect ratio of the form is left unmodified and all object gravity specifications are ignored. Since this function is meant to compensate for font size and server display resolution variations, scaling is limited to 125% per invocation. The best place to use this function is right after the creation of the forms. If the forms are properly designed this function should be a no-op on the machine the forms were designed on. Form Designer has a special option `-compensate' and resource `compensate' to request the emission of this function automatically for every form created. It is likely that this will become the default once the usefulness of it has been established. There is a similar function that works the same way, but on an object-by-object basis and further allows explicit margin specifications: void fl_fit_object_label(FL_OBJECT *obj, FL_Coord hm, FL_Coord vm); where `hm' and `vm' are the horizontal and vertical margins to leave on each side of the object, respectively. This function works by computing the object labels size and comparing it to the object size. If the label does not fit inside the object with the given margin, the entire form the object is on is scaled so the object label fits. In scaling the form, all gravity specification is ignored but the aspect ratio of the form (and thus of all objects) is kept. This function will not shrink a form. You can use this function on as many objects as you choose. Of course the object has to have a label inside the object for this function to work. All colors with indices smaller than `FL_FREE_COL1' are used (or can potentially be used) by the Forms Library. If you wish they can be changed using the following function prior to `*note fl_initialize()::': void fl_set_icm_color(FL_COLOR index, int r, int g, int b); Using this function you can actually change all entries in the internal colormap (with `index' going up to `FL_MAX_COLORS-1'). You may also inspect the internal colormap using void fl_get_icm_color(FL_COLOR index, int *r, int *g, int *b); In some situations Forms Library may modify some of the server defaults. All modified defaults are restored as early as possible by the main loop and in general, when the application exits, all server defaults are restored. The only exception is when exiting from a callback that is activated by shortcuts. Thus it is recommended that the cleanup routine `*note fl_finish()::' is called prior to exiting an application or register it via `atexit()'. void fl_finish(void); In addition to restoring all server defaults, `*note fl_finish()::' also shuts down the connection and frees dynamically allocated memory.  File: xforms.info, Node: Creating Forms, Next: Object Attributes, Prev: Initialization, Up: Part V Overview of Main Functions 32.3 Creating Forms =================== To start the definition of a form call FL_FORM *fl_bgn_form(int type, FL_Coord w, FL_Coord h); When the form is created it automatically acquires one object, a box object covering the full area of the form, which is used as the background of the form. The `type' argument is the type of this box object, so you can "style" the look of your forms (but don't use any non-rectangular box types). `w' and `h' are the width and height of the new form. The function returns a pointer to the new form. Note: if you look at the code generated by `fdesign' for the creation of a form you may notice that the type of this automatically assigned box is `*note FL_NO_BOX::' (which is invisible) and that for the background another box of the same size but a different (visible) type is added. This is because in `fdesign' the very first object can't be accessed and thus its properties can not be adjusted (like the box type or its color that then becomes the background color of the form). By using an extra box, which can be accessed from within `fdesign', that problem is circumvented. There also exist functions for setting and requesting the background color of a form void fl_set_form_background_color(FL_FORM *form, FL_COLOR col); FL_COLOR fl_get_form_background_color(FL_FORM *form); These functions use the color of the very first object of the form, or, if this is a box of type `*note FL_NO_BOX::' as it is the case with forms created via code generated by `fdesign', the color of the second object. If these object(s) don't exist the function can't work properly. Once all objects required have been added to a form call void fl_end_form(void); Between these two calls objects and groups of objects are added to the form with functions like `*note fl_add_button()::'. To start a new group of objects use FL_OBJECT *fl_bgn_group(void); The function returns a pointer to the group (actually to an invisible pseudo-object of class `FL_BEGIN_GROUP'). Groups can't be nested. When all objects that are supposed to belong to the group are added call void fl_end_group(void); Also this function creates an (invisible) pseudo-object, belonging to class `FL_END_GROUP', but since it can't be used its address isn ot returned. Groups are useful for two reasons. First of all, it is possible to hide or deactivate groups of objects with a single function call. This is often very handy to dynamically change the appearance of a form depending on the context or selected options. In addition it can also be used as a shortcut to set some particular attributes of several objects. It is not uncommon that you want several objects to maintain their relative positioning upon form resizing. This requires to set the gravity for each object. If these objects are placed inside a group, setting the gravity attributes of the group will suffice. The second reason for use of groups is radio buttons. Radio buttons are considered related only if they belong to the same group. Using groups is the only way to place unrelated groups of radio buttons on a single form without interference from each other. Both forms and groups that have been ended by `*note fl_end_form()::' or `*note fl_end_group()::' can be "reopened" by using FL_FORM *fl_addto_form(FL_FORM *form) FL_OBJECT *fl_addto_group(FL_OBJECT *group); Both functions return their argument on success and `NULL' on failure (e.g., because a different group or form is still open). On success further objects can be appended to the form or group. To remove an object from a form use void fl_delete_object(FL_OBJECT *obj); This does not yet destroy the object, it just breaks its connection to the form it did belong to, so it can still be referenced and added to the same form again or some other form using void fl_add_object(FL_FORM *form, FL_OBJECT *obj); even without "reopening" the form using `*note fl_addto_form()::'. To finally destroy an object use void fl_free_object(FL_OBJECT *obj); If `*note fl_delete_object()::' hadn't been called for the object this will happen now. The object receives a final event of type `*note FL_FREEMEM::' to allow it to free memory it did allocate and do whatever other clean-up required. Finally all memory allocated for the object is freed. After being freed an object can not be referenced anymore. A form as a whole, together with all the objects it contains can be deleted by calling void fl_free_form(FL_FORM *form); This will first hide the form (emitting warning if this is necessary), then free all of its objects and finally release memory allocated for the form.  File: xforms.info, Node: Object Attributes, Next: Doing Interaction, Prev: Creating Forms, Up: Part V Overview of Main Functions 32.4 Object Attributes ====================== A number of general routines are available for setting and querying attributes. Unless stated otherwise, all attributes altering routines affect the appearance or geometry of the object immediately if the object is visible. Since the object class and type of an object can't be changed anymore once an object has been created there are only functions for querying these attributes: int fl_get_object_objclass(FL_OBJECT *obj); int fl_get_object_type(FL_OBJECT *obj); Receiving a negative value indicates that a `NULL' pointer was passed to the functions. To set the two colors that influence the appearance of the object use void fl_set_object_color(FL_OBJECT *obj, FL_COLOR col1, FL_COLOR col2); and to find out about the colors of an object use void fl_get_object_color(FL_OBJECT *obj, FL_COLOR *col1, FL_COLOR *col2); void fl_set_object_boxtype(FL_OBJECT *obj, int boxtype); Changes the shape of the box of the object. Please note that not all possible boxtypes are suitable for all types of objects, see the documentation for the different objects for limitations. To find out the current boxtype of an object use int fl_get_object_boxtype(FL_OBJECT *obj); Receiving a negative value indicates that a `NULL' pointer was passed to the function. There are also functions to change or query the border width of an object: void fl_set_object_bw(FL_OBJECT *obj, int bw); void fl_get_object_bw(FL_OBJECT *obj, int *bw); If the requested border width is 0, -1 is used. To change or inquire the objects position (relative to the form it belongs to) the functions void fl_set_object_position(FL_OBJECT *obj, FL_Coord x, FL_Coord y); void fl_get_object_position(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y); exist. If the object is visible it's redrawn at the new position. An object can also be moved relative to its current position using the function void fl_move_object(FL_OBJECT *obj, FL_Coord dx, FL_Coord dy); where `dx' and `dy' are the amounts by which the object is moved to the right and down. To change or inquire about the size of an object use void fl_set_object_size(FL_OBJECT *obj, FL_Coord w, FL_Coord h); void fl_get_object_size(FL_OBJECT *obj, FL_Coord *w, FL_Coord *h); When changing the size of the object the position of its upper left hand corner remains unchanged. To set or query both the position and the size of an object the functions void fl_set_object_geometry(FL_OBJECT *obj, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h); void fl_get_object_geometry(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y, FL_Coord (*w, FL_Coord *h); can be used. Please note: always use one of the above functions to change the position and/or size of an object and don't try to change the information stored in the object directly. There's some double bookkeeping going on under the hood that makes sure that the objects position and size won't change due to rounding errors when the whole form gets resized and changing the internal information kept in the objects structure would interfere with this. There's a second function for calculation an objects geometry: void fl_get_object_bbox(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y, FL_Coord *w, FL_Coord *h); The difference between this functions and `*note fl_get_object_geometry()::' is that `*note fl_get_object_bbox()::' returns the bounding box size that has the label, which could be drawn outside of the object figured in. Some objects in the library are composite objects that consist of other objects. For example, the scrollbar object is made of a slider and two scroll buttons. To get a handle to one of the components of the composite object, the following routine is available: FL_OBJECT *fl_get_object_component(FL_OBJECT *obj, int objclass, int type, int number); where `obj' is the composite object, `objclass' and `type' are the component object's class ID and type; and `number' is the sequence number of the desired object in case the composite has more than one object of the same class and type. You can use a constant -1 for `type' to indicate any type of class `objclass'. The function returns the object handle if the requested object is found, otherwise `NULL'. For example to obtain the object handle to the horizontal scrollbar in a browser, code similiar to the following can be used hscrollbar = fl_get_object_component(browser, FL_SCROLLBAR, FL_HOR_THIN_SCROLLBAR, 0) To influence change the color, font size, font style, alignment and text of the label of an object use void fl_set_object_lcolor(FL_OBJECT *obj, FL_COLOR lcol); void fl_set_object_lsize(FL_OBJECT *obj, int lsize); void fl_set_object_lstyle(FL_OBJECT *obj, int lstyle); void fl_set_object_lalign(FL_OBJECT *obj, int align); void fl_set_object_label(FL_OBJECT *obj, const char *label); void fl_set_object_label(FL_OBJECT *obj, const char *fmt, ...); To find out about the object labels color, font size, style, alignment and the string itself use FL_COLOR fl_get_object_lcolor(FL_OBJECT *obj); int fl_get_object_lsize(FL_OBJECT *obj); int fl_get_object_lstyle(FL_OBJECT *obj); int fl_get_object_lalign(FL_OBJECT *obj); const char * fl_get_object_label(FL_OBJECT *obj); To set a tool-tip text for an object use the following routines void fl_set_object_helper(FL_OBJECT *obj, const char *helpmsg); void fl_set_object_helper_f(FL_OBJECT *obj, const char *fmt, ...); where `helpmsg' is a text string (with possible embedded newlines in it) that will be shown when the mouse hovers over the object for nore than about 600 msec. A copy of the string is made internally. The second functions accepts instead of a simple string a format string just as it's used for `printf()' etc., followed by as many further arguments as the format string contains format specifiers. The boxtype, color and font for the tool-tip message displayed can be customized further using the following routines: void fl_set_tooltip_boxtype(int boxtype); void fl_set_tooltip_color(FL_COLOR textcolor, FL_COLOR background); void fl_set_tooltip_font(int style, int size); where `boxtype' is the backface of the form that displays the text. The default is `*note FL_BORDER_BOX::'. `textcolor' and `background' specify the color of the text and the color of the backface. The defaults for these are `FL_BLACK' and `FL_YELLOW'. `style' and `size' are the font style and size of the text. There are four function for controlling how an object reacts to resizing the form it belongs to or to find out what its current settings are: void fl_set_object_resize(FL_OBJECT *obj, unsigned int howresize); void fl_get_object_resize(FL_OBJECT *obj, unsigned int *howresize); void fl_set_object_gravity(FL_OBJECT *obj, unsigned int NWgravity, unsigned int SEgravity); void fl_get_object_gravity(FL_OBJECT *obj, unsigned int *NWgravity, unsigned int *SEgravity); *Note Doing Interaction: Part I Doing Interaction, for more details on the resizing behaviour of objects. If you change many attributes of a single object or many objects in a visible form the changed object is redrawn after each change. To avoid this put the changes between calls of the two functions void fl_freeze_form(FL_FORM *form); void fl_unfreeze_form(FL_FORM *form); The form is automatically redrawn once it is "unfrozen", so a call of `*note fl_redraw_form()::' isn't required (and, while the form is "frozen", calling this function as well as `*note fl_redraw_object()::' has no effects). You may also freeze and unfreeze all forms at once by using void fl_freeze_all_forms(void); void fl_unfreeze_all_forms(void); There are also routines that influence the way events are dispatched. These routines are provided mainly to facilitate the development of (unusual) new objects where attributes need to be changed on the fly. These routines should not be used on the built-in ones. To enable or disable an object to receive the `*note FL_STEP::' event, use the following routine void fl_set_object_automatic(FL_OBJECT *obj, int yes_no); To determine if an object receives `*note FL_STEP::' events use int fl_object_is_automatic(FL_OBJECT *obj); To enable or disable an object to receive the `*note FL_DBLCLICK::' event use the following routine void fl_set_object_dblclick(FL_OBJECT *obj, unsigned long timeout); where `timeout' specifies the maximum time interval (in msec) between two clicks for them to be considered a double-click (using 0 disables double-click detection). To determine the current setting of the timeout use unsigned fl_get_object_dblclick(FL_OBJECT *obj); To make an object or a group invisible or visible use the following two functions void fl_hide_object(FL_OBJECT *obj); void fl_show_object(FL_OBJECT *obj); `obj' can be the pseudo-object returned by `*note fl_bgn_group()::' and then allows to hide or show whole groups of objects. To determine if an object is visible (given that the form it belongs to is also visible) use int fl_object_is_visible(FL_OBJECT *obj); void fl_trigger_object(FL_OBJECT *obj); returns `obj' to the application program after calling its callback if one exists. void fl_set_focus_object(FL_FORM *form, FL_OBJECT *obj); sets the input focus in form `form' to object `obj'. Note however, if this routine is used as a response to an `*note FL_UNFOCUS::' event, i.e., as an attempt to override the focus assignment by the main loop from within an objects event handler, this routine will not work as the main loop assigns a new focus object upon return from the object event handler, which undoes the focus change inside the event handler. To override the `*note FL_UNFOCUS::' event the following routine should be used: void fl_reset_focus_object(FL_OBJECT *obj); Use the following routine to obtain the object that has the focus on a form FL_OBJECT *fl_get_focus_object(FL_FORM *form); The routine void fl_set_object_callback(FL_OBJECT *obj, void (*callback)(FL_OBJECT *, long), long argument); binds a callback routine to an object. To invoke the callback manually (as opposed to invocation by the main loop), use the following function void fl_call_object_callback(FL_OBJECT *obj); If the object `obj' does not have a callback associated with it, this call has not effect. void fl_set_form_callback(FL_FORM *form, void (*callback)(FL_OBJECT *, void *), void *data); binds a callback routine to an entire form. It is sometimes useful to obtain the last X event from within a callback function, e.g., to implement different functionalities depending on which button triggers the callback. For this, the following routine can be used from within a callback function. const XEvent *fl_last_event(void); In other rare circumstances one might not be interested not in the X event but instead the internal XForms event resulting in the invocation of an object or form callback. This information can be obtained by calling int fl_current_event(void); A callback invocation resulting from a call of `*note fl_call_object_callback()::' will return `FL_TRIGGER'. For other possible return value see *note the chapter about XForms internal events: Part IV Events. Calling this function is only useful while within an object or form callback, at all other times it returns just `FL_NOEVENT'. Also in objects callback it might be of interest to find out if the mouse is on top of a certain letter of the (inside) label (one trivial use of this can be found in the program `demo/strange_button.c'. To find out about this use int fl_get_label_char_at_mouse(FL_OBJECT *obj); The function returns the index of the character in the label of the object the mouse is on or `-1' if it's not over the label. Note that this function has some limitations: it can only be used on labels inside of the object and the label string may not contain underline characters (and the label can't be a symbol) - if you try to use it on labels that don't satisfy these requirements `-1' is returned. Sometimes, it may be desirable to obtain hardcopies of some objects in a what-you-see-is-what-you-get (WYSISYG) way, especially those that are dynamic and of vector-graphics in nature. To this end, the following routine exists: int fl_object_ps_dump(FL_OBJECT *obj, const char *fname); The function will output the specified object in PostScript. If `fname' is `NULL', a file selector will be shown to ask the user for a file name. The function returns a negative number if no output is generated due to errors. At the moment, only the `FL_XYPLOT' object is supported. Nothe that this function isn't part of the statndard XForms library (`libforms') but the XForms image library (`libflimage' discussed in *note Part VI Images::. The object must be visible at the time of the function call. The hardcopy should mostly be WYSIWYG and centered on the printed page. The orientation is determined such that a balanced margin results, i.e., if the width of the object is larger than the height, landscape mode will be used. Further, if the object is too big to fit on the printed page, a scale factor will be applied so the object fits. The box underneath the object is by default not drawn and in the default black&white mode, all curves are drawn in black. See demo program `xyplotover.c' for an example output. It is possible to customize the output by changing the PostScript output control parameters via the function FLPS_CONTROL *flps_init(void); A typical use is to call this routine to obtain a handle to the PostScript output control structure and change the control structure members to suit your needs before calling `*note fl_object_ps_dump()::'. You should not free the returned buffer. The control structure has the following members `int ps_color' The choices are full color (`FLPS_COLOR'), grayscale (`FLPS_GRAYSCALE') and black&white (`FLPS_BW'). The default for xyplot is black and white. In this mode, all drawings are black, on a white background. If `drawbox' (see below) is true, the drawing color can be either white or black depending on the specified color. `int orientation' Valid choices are `FLPS_AUTO', `FLPS_PORTRAIT' and `FLPS_LANDSCAPE'. The default is `FLPS_AUTO'. `auto_fit' By default, this is true so the object always fits the printed page. Set it to false (0) to turn off auto-scaling. `int eps' Set this to 1 if output in EPS format is required. `int drawbox' Set this to 1 if the box of the object is to be drawn. `float xdpi, ydpi' These two are the screen resolution. The default is to use the actual resolution of the display. Note by setting a dpi number smaller or larger than the actual resolution, the output object is in effect being enlarged or shrunken. `float paper_w' The paper width in inches. The default is 8.5 in. `float paper_h' The paper height in inches. The default is 11 in. To generate a PostScript output of a form or forms, use the `fd2ps' program documented in *note Part II Generating Hardcopies::.  File: xforms.info, Node: Doing Interaction, Next: Signals, Prev: Object Attributes, Up: Part V Overview of Main Functions 32.5 Doing Interaction ====================== To display the form `form' on the screen use one of Window fl_show_form(FL_FORM *form, int place, int border, const char *title); Window fl_show_form(FL_FORM *form, int place, int border, const char *fmt, ...); `place' controls the position and size of the form. `border' indicates whether a border (window manager's decoration) should be drawn around the form. If a border is to be drawn `title' is the name of the window (and its associated icon). The routine returns the window identifier of the form. For resource and identification purposes, the form name is taken to be the title with spaces removed and the first character lower-cased. E.g., if a form has a title `"Foo Bar' the forms name is derived as `"fooBar"'. The only difference between the two functions is that the first one accepts a simple string for the title while the second expects a format string like `printf()', followed by the appropriate number of arguments. For the the location and size of the window controlled by `place' the following possibilities exist: `FL_PLACE_SIZE' The user can control the position but the size is fixed. Interactive resizing is not allowed once the form becomes visible. `FL_PLACE_POSITION' Initial position used will be the one set via `*note fl_set_form_position()::'. Interactive resizing is allowed. `FL_PLACE GEOMETRY' Place at the latest position and size (see also below) or the geometry set via `*note fl_set_form_geometry()::' etc. A form so shown will have a fixed size and interactive resizing is not allowed. `FL_PLACE_ASPECT' Allows interactive resizing but any new size will have the aspect ratio as that of the initial size. `FL_PLACE_MOUSE' The form is placed centered below the mouse. Interactive resizing will not be allowed unless this option is accompanied by `*note FL_FREE_SIZE::' as in `*note FL_PLACE_MOUSE::|*note FL_FREE_SIZE::'. `FL_PLACE_CENTER' The form is placed in the center of the screen. If `*note FL_FREE_SIZE::' is also specified, interactive resizing will be allowed. `FL_PLACE_FULLSCREEN' The form is scaled to cover the full screen. If `*note FL_FREE_SIZE::' is also specified, interative resizing will be allowed. `FL_PLACE_FREE' Both the position and size are completely free. The initial size used is the designed size. Initial position, if set via `*note fl_set_form_position()::', will be used, otherwise interactive positioning may be possible if the window manager allows it. `FL_PLACE_HOTSPOT' The form is so placed that mouse is on the "hotspot". If `*note FL_FREE_SIZE::' is also specified, interactive resizing will be allowed. `FL_PLACE_CENTERFREE' Same as `*note FL_PLACE_CENTER::|*note FL_FREE_SIZE::', i.e., place the form at the center of the screen and allow resizing. `FL_PLACE ICONIC' The form is shown initially iconified. The size and location used are the window manager's default. If no size is specified, the designed (or later scaled) size will be used. Note that the initial position is dependent upon the window manager used. Some window managers will allow interactive placement of the windows and some will not. There are three values that can be passed for `border': `FL_FULLBORDER' Draw full border with title `FL_TRANSIENT' Draw borders with possibly less decoration (depends on the window managers behaviour) `FL_NOBORDER' Draw no border at all Since multiple forms can be displayed at the same time note that using `FL_NOBORDER' might have adverse effect on keyboard focus and is not very friendly to other applications (it is close to impossible to move a form that has no border). Thus use this feature with discretion. The only situation where `FL_NOBORDER' is appropriate is for automated demonstration suites or when the application program must obtain an input or a mouse click from the user, and even then all other forms should be deactivated while a borderless form is active. For almost all situations where the application must demand an action from the user `FL_TRANSIENT' is preferable. Also note that you can't iconify a form that has no borders and under most window managers forms displayed with `FL_TRANSIENT' can't be iconified either. One additional property (under almost all window managers) of a transient window is that it will stay on top of the main form, which the application program can designate using void fl_set_app_mainform(FL_FORM *form); By default, the main form is set automatically by the library to the first full-bordered form shown. To obtain the current main form, use the following routine FL_FORM *fl_get_app_mainform(void); In some situations, either because the concept of an application main form does not apply (for example, an application might have multiple full-bordered windows), or under some (buggy) window managers, the designation of a main form may cause stacking order problems. To workaround these, the following routine can be used to disable the designation of a main form (must be called before any full-bordered form is shown): void fl_set_app_nomainform(int yes_no); with a true flag. All visible forms will have the properties `WM_CLASS', `WM_CLIENT_MACHINE' and `WM_NAME' set. In addition, the first full-bordered form will have the `WM_COMMAND' property set and is by default the applications main form. Sometimes it is necessary to have access to the window resource ID before the window is mapped (shown). For this, the following routines can be used Window fl_prepare_form_window(FL_FORM *form, int place, int border, const char *name); Window fl_prepare_form_window_f(FL_FORM *form, int place, int border, const char *fmt, ...); These routines create a window that obeys any and all constraints just as `*note fl_show_form()::' does but remains unmapped. The only difference between the two functions is that the first one takes a simple string for the forms name while the second expects a format string like `printf()', followed by the appropriate number of further arguments. To map such a window, the following must be used Window fl_show_form_window(FL_FORM *form); Between these two calls, the application program has full access to the window and can set all attributes, such as icon pixmaps etc., that are not set by `*note fl_show_form()::'. The application program can raise a form to the top of the screen so no other forms obscures it by calling void fl_raise_form(FL_FORM *form); To instead lower a form to the bottom of the stack use void fl_lower_form(FL_FORM *form); When placing a form on the screen using `FL_PLACE_GEOMETRY' for the `place' argument to `*note fl_show_form()::' the position and size can be set before by using the routines void fl_set_form_position(FL_FORM *form, FL_Coord x, FL_Coord y); void fl_set_form_size(FL_FORM *form, FL_Coord w, FL_Coord h); void fl_set_form_geometry(FL_FORM form*, FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h); void fl_scale_form(FL_FORM *form, double xsc, double ysc); where `*note fl_set_form_geometry()::' combines the functionality of `*note fl_set_form_position()::' and `*note fl_set_form_size()::' and the last routine, `*note fl_scale_form()::', scales the form in horizontal and vertical direction by the factors passed to the function. These routines can also be used when the form is visible. Sometimes it is desirable to know how large the decoration are the window manager puts around a forms window. They can be obtained by a call of void fl_get_decoration_sizes(FL_FORM *form, int *top, int *right, int *bottom, int *left); This is especially useful if it is necessary to open a window at some previously stored position since in that case one needs the position of of the window, which deviates from the position reported for the form by the window manager's decorations. Obviously, the above function can't be used for forms that are embedded into another form. The function int fl_form_is_iconified(FL_FORM *form); allows to test if the (visible) window of a form is in iconified state. If interactive resizing is allowed (e.g., by showing the form with `*note FL_PLACE_POSITION::') it can be useful to limit the range of the size of a form can take. To this end, the following functions are available void fl_set_form_minsize(FL_FORM *form, FL_Coord minw, FL_Coord minh); void fl_set_form_maxsize(FL_FORM *form, FL_Coord maxw, FL_Coord maxh); Although these two routines can be used before or after a form becomes visible, not all window managers honor such requests once the window is visible. Also note that the constraints only apply to the next call of `*note fl_show_form()::' for the form. To set or change the icon shown when a form is iconified use the following routine void fl_set_form_icon(FL_FORM *form, Pixmap icon, Pixmap mask); where `icon' can be any valid pixmap ID. (*note Pixmap Object:: for some of the routines that can be used to create pixmaps.) Note that a previously set icon if not freed or modified in anyway. If, for any reason, you would like to change the form title after the form has been made visible, the following calls can be used (they will also change the icon title) void fl_set_form_title(FL_FORM *form, const char *name); void fl_set_form_title_f(FL_FORM *form, const char *fmt, ...); (While the first function expects a simple string, the second has to be called with a format string as `printf()' etc., followed by the corresponding number of arguments.) The routine void fl_hide_form(FL_FORM *form); hides the particular form, i.e., closes its window and all subwindows. To check if a form is visible or not, the following function can be used int fl_form_is_visible(FL_FORM *form)' The function can return that the form is visible (`*note FL_VISIBLE::'), is invisible (`*note FL_INVISIBLE::') or is in the processing of becoming invisible (`*note FL_BEING_HIDDEN::'). The most important function for doing the actual interaction with forms is FL_OBJECT *fl_do_forms(void); It starts the main loop of the program and returns only when either the state of an object changes that has no callback bound to it or `*note fl_finish()::' is called in a callback. In the first case the address of the object is returned, in the latter `NULL'. A second way of doing interaction with the currently displayed forms is using FL_OBJECT *fl_check_forms(void); This routine returns `NULL' immediately unless the state of one of the object (without a callback bound to it) changed. In that case a pointer to this object gets returned. `NULL' also gets returned after a call of `*note fl_finish()::'. Then there are two more functions: FL_OBJECT *fl_do_only_forms(void); FL_OBJECT *fl_check_only_forms(void); Both functions do the same as `*note fl_do_forms()::' and `*note fl_check_forms()::' except that they do not handle user events generated by application windows opened via `*note fl_winopen()::' or similar routines. To activate or deactivate a form for user interaction you can use void fl_activate_form(FL_FORM *form); void fl_deactivate_form(FL_FORM *form); The same can also be done for all forms at once using void fl_deactivate_all_forms(void) void fl_activate_all_forms(void) To find out if a form is currently active call int fl_form_is_activated(FL_FORM *form); A return value of 0 tells you that the form is currently deactivated. You can also register callbacks for a form that are invoked whenever the activation status of the form is changed: typedef void (*FL_FORM_ATACTIVATE)(FL_FORM *, void *); FL_FORM_ACTIVATE fl_set_form_atactivate(FL_FORM *form, FL_FORM_ATACTIVATE callback, void *data); typedef void (*FL_FORM_ATDEACTIVATE)(FL_FORM *, void *); FL_FORM_ACTIVATE fl_set_form_atdeactivate(FL_FORM *form, FL_FORM_ATACTIVATE callback, void *data); Also individual objects (or groups of objects if the argument of the function is an object returned by `*note fl_bgn_group()::') can be activated and deactivated to enable or disable user interaction: void fl_activate_object(FL_OBJECT *obj); void fl_deactivate_object(FL_OBJECT *obj); It is normally useful to give the user a visual clue when an object gets deactivated, e.g., by graying out its label etc. To find out if an object is active use int fl_object_is_active(FL_OBJECT *obj); void fl_redraw_object(FL_OBJECT *obj); This routine redraws the particular object. If `obj' is a group it redraws the complete group. Normally you should never need this routine because all library routines take care of redrawing objects when necessary, but there might be situations in which an explicit redraw is required. To redraw an entire form use void fl_redraw_form(FL_FORM *form); For non-form windows, i.e., those created with `*note fl_winopen()::' or similar routines by the application program, the following means of interaction are provided (note that these do not work on form windows, for which a different set of functions exist, *note Windowing Support:: for details.) You may set up a callback routine (of type `FL_APPEVENT_CB' for all user events using typedef int (*FL_APPEVENT_CB)(XEvent *, void *); FL_APPEVENT_CB fl_set_event_callback(FL_APPEVENT_CB callback, void *data); The function returns the previously set callback (or `NULL'). It is also possible to set up callback functions on a per window/event basis using the following routines: typedef int (*FL_APPEVENT_CB)(XEvent *xev, void *user_data); FL_APPEVENT_CB fl_add_event_callback(Window win, int xevent_type, FL_APPEVENT_CB callback, void *user_data); void fl_remove_event_callback(Window win, int xevent_type); These functions manipulate the event callback functions for the window specified, which will be called when an event of type `xevent_type' is pending for the window. If `xevent_type' is 0 it signifies a callback for all event for window `win'. Note that the Forms Library does not solicit any event for the caller, i.e., the Forms Library assumes the caller opens the window and solicits all events before calling these routines. To let the Forms Library handle event solicitation, the following function may be used void fl_activate_event_callbacks(Window win);  File: xforms.info, Node: Signals, Next: Idle Callbacks and Timeouts, Prev: Doing Interaction, Up: Part V Overview of Main Functions 32.6 Signals ============ Typically, when a signal is delivered, the application does not know what state the application is in, thus limiting the tasks a signal handler can do. In a GUI system and with a main loop inside the library, it's even harder to know what's safe or unsafe to do in a signal handler. Given all these difficulties, the Forms Library's main loop is made to be aware of signal activities and invoke signal handlers only when it's appropriate to do so, thus removing most limitations on what a signal handler can do. The application program can elect to handle the receipt of a signal by registering a callback function that gets called when a signal is caught typedef void (*FL_SIGNAL_HANDLER)(int, void *); void fl_add_signal_callback(int signal, FL_SIGNAL_HANDLER sh, void *data); Only one callback per signal is permitted. By default, `*note fl_add_signal_callback()::' will store the callback function and initiate a mechanism for the OS to deliver the signal when it occurs. When the signal is received by the library, the main loop will invoke the registered callback function when it is appropriate to do so. The callback function can make use of all of XForms's functions as well as Xlib functions as if they were reentrant. Further, a signal callback registered his way is persistent and will cease to function only when explicitly removed. It is very simple to use this routine. For example, to prevent a program from exiting prematurely due to signals, a code fragment similar to the following can be used: void clean_up(int signum, void *data) { /* clean up, of course */ } /* call this somewhere after fl_initialize() */ fl_add_signal_callback(SIGINT, clean_up, &mydata); After this, whenever a `SIGINT' signal is received, `clean_up()' is called. To remove a signal callback, the following routine should be used void fl_remove_signal_callback(int signal); Although very easy to use, there are limitations with the default behavior outlined above. For example on some platforms there is no blocking of signals of any kind while handling a signal. In addition, use of `*note fl_add_signal_callback()::' prevents the application program from using any, potentially more flexible, system signal handling routines on some platforms. Also there might be perceptible delays from the time a signal is delivered by the OS and the time its callback is invoked by XForms' main loop. This delay can be particular troublesome for timing sensitive tasks (playing music for example). In light of these limitations, provisions are made so an application program may choose to take over the initial signal handling setup and receipt via various system dependent methods (`sigaction()' for example). To change the default behavior of the built-in signal facilities, the following routine should be called prior to any use of `fl_add_signal_callback(') with a true value for `flag': void fl_app_signal_direct(int flag); After this call `*note fl_add_signal_callback()::' will not initiate any actions to receive a signal. The application program should handle the receipt and blocking of signals (via e.g., `signal(2)', `sigaction(2)', `sigprocmask(2') etc.) When the signal is received by the application program, it should call the following routine to inform the main loop of the delivery of the signal `signum', possibly after performing some timing sensitive tasks: void fl_signal_caught(int signum); This routine is the only one in the library that can be safely called from within a direct application signal handler. If multiple invocations of `*note fl_signal_caught()::' occur before the main loop is able to call the registered callback, the callback is called only once. The following example illustrates how to handle a timing critical situation (for most application, idle callback, timeouts or `FL_TIMER' object should be sufficient). First, you need to define the function that will handle the timing critical tasks. The function will be registered with the OS to be invoked directly by it. There are limitations on what you can do within a (OS) signal handler, in particular, GUI activity is not safe. void timing_critical_task(int sig) { /* handle timing critical tasks that does not involve GUI */ ... /* Now tell the library the signal has been delivered by the OS. * The library will invoke the xforms signal handler when it's * appropriate to do so */ fl_signal_caught(sig); } Now define a (XForms) signal handler that will be responsible for handling the response of the GUI upon receipt of the signal void gui_signal_handler(int sig, void *data) { /* within an XForms signal handler, there is no limitation * on GUI activitity */ fl_set_object_color(....); ... } To make all this work, a set-up similar to the following can be used /* setup the signal */ fl_app_signal_direct(1); setitimer(ITIMER_REAL, interval); /* setup the OS signal handler */ signal(SIGALRM, timing_critical_tasks); /* setup the XForms signal handler */ fl_add_signal_callback(SIGALRM, gui_signal_handler, &myData);  File: xforms.info, Node: Idle Callbacks and Timeouts, Next: Global Variables, Prev: Signals, Up: Part V Overview of Main Functions 32.7 Idle Callbacks and Timeouts ================================ For application programs that need to perform some light, but semi-continuous or periodic tasks, idle callback and timeouts (also `FL_TIMER' objects) can be utilized. To register an idle callback with the system, use the following routine typedef int (*FL_APPEVENT_CB)(XEvent *, void *); FL_APPEVENT_CB fl_set_idle_callback(FL_APPEVENT_CB callback, void *user_data); where `callback' is the function that will get called whenever the main loop is idle. The time interval between invocations of the idle callback can vary considerably depending on interface activity and other factors. A range between 50 and 300 msec should be expected. While the idle callback is executed it won't be called again (i.e., no call of any XForms function from within the idle callback function will call the idle callback function), so it does not need to be reentrant. It is possible to change what the library considers to be "idle" with the following function: void fl_set_idle_delta(long msec); Here `msec' is the minimum time interval of inactivity after which the main loop is considered to be in an idle state. However it should be noted that under some conditions an idle callback can be called sooner than the minimum interval. If the timing of the idle callback is of concern, timeouts should be used. Timeouts are similar to idle callbacks but with the property that the user can specify a minimum time interval that must elapse before the callback is called. The precision of timeouts tends to be quite a bit better than that of idle callbacks since they internally get prefered treatent. To register a timeout callback, the following routine can be used typedef void (*FL_TIMEOUT_CALLBACK)(int, void *); int fl_add_timeout(long msec, FL_TIMEOUT_CALLBACK callback, void *data); The function returns the timeout ID (note: the function will not return 0 and -1, so the application can use these values to mark invalid or expired timeouts). When the time interval specified by the `msec' argument (in milli-second) is elapsed, the timeout is removed and the callback function is called with the timeout ID as the first argument. Although a timeout offers some control over the timing, due to performance and CPU load compromises, while the resolution can be better than 10 ms under favourable conditions, it can also be much worse, occasionally up to 150 ms. To remove a timeout before it triggers, use the following routine void fl_remove_timeout(int id); where `id' is the timeout ID returned by `*note fl_add_timeout()::'. *Note Timer Object::, for the usage of `FL_TIMER' object. For tasks that need more accurate timing the use of signal should be considered.  File: xforms.info, Node: Global Variables, Prev: Idle Callbacks and Timeouts, Up: Part V Overview of Main Functions 32.8 Global Variables ===================== For convenience the library exports a number of global variables. These are: `FL_OBJECT *FL_EVENT' This is a special object returned by `*note fl_do_forms()::' etc. when an X event is received that isn't coming from a form under the control of the library, e.g., for a window that was opened directly via Xlib functions. Upon receiving this special event the application program can and must remove the pending event from the queue using `*note fl_XNextEvent()::'. `FL_FORM *fl_current_form' This variable is always set to the currently active form. `Display *fl_display' This variable is set to the display (X server) the program is connected to and is needed as an argument for many Xlib functions. It's recommended not to use this global variable but instead either the function `*note fl_get_display()::' or `*note FL_FormDisplay()::' (the latter accepts a form pointer as its argument and will also be safe in future versions of the library that may support multiple connections). `int fl_screen' This variable is set to the default screen of the display connection. `Window fl_root' This variable is set to the root window. `Window fl_vroot' Some window managers have problems with obtaining the corrent root window and applications don't work with the normal root windows. In this case `fl_vroot' can be used instead. `int fl_scrw, fl_scrh' These variables contain the screens width and height. `int fl_mode' The variable contains the visual mode in use, it should be one of the Xlib constants `PseudoColor', `TrueColor', `DirectColor', `StaticColor', `GrayScale or `StaticGray'. Alternatively, the functions `*note fl_get_vclass()::' or `fl_get_form_vclass()'' can be used (the latter accepts a form pointer as its argument and is thus also safe for future versions that may allow multiple connections). `FL_State fl_state[ 6 ]' This array of structure of type `*note FL_State::' contains a lot of information about the graphics mode, where each structure has the information for each of the cisual modes. Interesting is only the entry for the visual mode used, `*note fl_vmode::'. `char *fl_ul_magic_char' This variable points to the character used to indicate underlining in labels and other texts. If it appears as the very first character of a string all characters in that string are underlined, otherwise the character direct in front of it. Per default it's set to `'\b''.  File: xforms.info, Node: Part V Some Useful Functions, Next: Part V Resources for Forms Library, Prev: Part V Overview of Main Functions, Up: Top 33 Some Useful Functions ************************ * Menu: * Misc. Functions:: * Windowing Support:: * Cursors:: * Clipboard::  File: xforms.info, Node: Misc. Functions, Next: Windowing Support, Up: Part V Some Useful Functions 33.1 Misc. Functions ==================== The following routine can be used to sound the keyboard bell (if capable): void fl_ringbell(int percent); where `percent' can range from -100 to 100 with 0 being the default volume setting of the keyboard. A value of 100 indicates maximum volume and a value of -100 minimum volume (off). Note that not all keyboards support volume variations. To get the user name who's running the application you can use the routine const char *fl_whoami(void); To get a string form of the current date and time, the following routine is available: const char *fl_now(void); The format of the string is of the form `"Wed Jun 30 21:49:08 1993"'. The following time related routine might come in handy void fl_gettime(unsigned long *sec, unsigned long *usec); Upon function return `sec' and `usec' are set to the current time, expressed in seconds and microseconds since 00:00 GMT January, 1970. This function is most useful for computing time differences. Th function int fl_mode_capable(int mode, int warn); allows to determine the visual classes the system is capable of. `mode' must be one of `GrayScale', `StaticGray', `PseudoColor', `StaticColor', `DirectColor' and `TrueColor' and the function returns 1 if the system is capable of displaying in this visual class and 0 otherwise. If `warn' is set a warning is printed out in case the capability asked for isn't available. Finally int fl_msleep(usigned long msec); allows to wait for a number of milli-seconds (with the best resolution possible on your system).  File: xforms.info, Node: Windowing Support, Next: Cursors, Prev: Misc. Functions, Up: Part V Some Useful Functions 33.2 Windowing Support ====================== Some of the following routines are also used internally by the Forms Library as an attempt to localize window system dependencies and may be of some general use. Be warned that these routines may be subject to changes, both in their API and/or functionality. You can create and show a window with the following routines Window fl_wincreate(const char *name); Window fl_winshow(Window win); where the parameter `win' of `*note fl_winshow()::' is the window ID returned by `*note fl_wincreate()::'. The title of the window is set by the `name' argument. Between the creation and showing of the window other attributes of the window can be set. Note that a window opened this way is always a top level window and uses all the Forms Library's defaults (visual, depth etc.). Another thing about `*note fl_winshow()::' is that it will wait for and gobble up the first `Expose' event and you can draw into the window immediately after the function returns. It is sometimes more convenient to create and show a window in a single call using Window fl_winopen(const char *name); This will open a (top-level) window with the title `name'. A window so opened can be drawn into as soon as the function returns, i.e., `*note fl_winopen()::' waits until the window is ready to be drawn to. The newly opened window will have the following default attributes `event_mask' `ExposureMask', `KeyPressMask', `KeyReleaseMask', `ButtonPressMask', `ButtonReleaseMask', `OwnerGrabButtonMask', `ButtonMotionMask', `PointerMotionMask', `PointerMotionHintMask', `StructureNotifyMask' `backing_store' as set by `fl_cntl.backingStore' `class' `InputOutput' `visual' same as Forms Library's default `colormap' same as Forms Library's default To make a top-level window a sub-window of another window use the following routine int fl_winreparent(Window win, Window new_parent); The origin of the window `win' will be at the origin of the parent window `new_parent'. At the time of the function call, both the window and the parent window must be valid windows. By default, a newly opened window will have a size of 320 by 200 pixels and no other constraints. You can modify the default or constraints using the following routines prior to calling `*note fl_winopen()::': void fl_initial_winsize(FL_Coord w, FL_Coord h); void fl_winsize(FL_Coord w, FL_Coord h); These two routines set the preferred window size. `w' and `h' are the width and height of the window in pixels. `*note fl_winsize()::' in addition will make the window non-resizeable (but you can still resize the window programmatically) by setting the minimum and maximum window size to the requested size via `WMHints'. The effect of a window having this property is that it can't be interactively resized (provided the window manager cooperates). Also the state of the window when opening it can be influenced by the function void fl_initial_winstate(int state); where `state' is on of the XLib constants `NormalState' (the default) or `IconicState', which will result in the opened window being iconified. The third possible constant, `WithdrawnState', doesn't make much sense in this context. It is sometimes desirable to have a window that is resizeable but only within a useful range. To set such a constraint use the following functions: void fl_winminsize(Window window, FL_Coord minw, FL_Coord minh); void fl_winmaxsize(Window window, FL_Coord maxw, FL_Coord maxh); These two routines can also be used after a window has become visible. For windows still to be created/opened, use `None' for the window parameter. For example, if we want to open a window of 640 by 480 pixels and have it remain resizeable but within a permitted range, code similar to the following can be used: fl_initial_winsize(640, 480); fl_winminsize(None, 100,100); fl_winmaxsize(None, 1024,768) win = fl_winopen("MyWin"); In addition to the window size preference you can also set the preferred position of a window to be opened: void fl_winposition(FL_Coord x, FL_Coord y); where `x' and `y' are the coordinates of the upper-left corner of the window relative to the root window. Alternatively, you can set the geometry (position and size) in a single function call: void fl_initial_wingeometry(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h); void fl_wingeometry(FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h); Again, windows for which `*note fl_wingeometry()::' had been created will not allow interactive resizing later on. There are further routines that can be used to change other aspects of the window to be created: void fl_winaspect(Window win, FL_Coord x, FL_Coord y); This will set the aspect ratio of the window for later interactive resizing. To change the window title (and its associated icon title) use void fl_wintitle(Window win, const char *title); void fl_wintitle_f(Window win, const char *fmt, ...); While the first function only accepts a simple string for the window title the second one allows to pass a format string just like the one used for `printf()' etc. and an appropriate number of further arguments which are used to construct the title. To change the icon title only use the routines void fl_winicontitle(Window win, const char *title); void fl_winicontitle_f(Window win, const char *fmt, ...); To install an icon for the window use void fl_winicon(Window win, Pixmap icon, Pixmap mask); You can suppress the window manager's decoration or make a window a transient one by using the following routines prior to creating the window void fl_noborder(void); void fl_transient(void); You can also set the background of the window to a certain color using the following call void fl_winbackground(Window win, unsigned long pixel); It is possible to set the steps by which the size of a window can be changed by using void fl_winstepsize(Window win, int xunit, int yunit); where `xunit' and `yunit' are the number of pixels of changes per unit in x- and y- directions, respectively. Changes to the window size will be multiples of these units after this call. Note that this only applies to interactive resizing. To change constraints (size and aspect ratio) on an active window, you can use the following routine void fl_reset_winconstraints(Window win); The following routines are available to get information about an active window win: void fl_get_winsize(Window win, FL_Coord *w, FL_Coord *h); void fl_get_winorigin(Window win, FL_Coord *x, FL_Coord *y); void fl_get_wingeometry(Window win, FL_Coord *x, FL_Coord *y, FL_Coord *w, FL_Coord *h); All values returned are in pixels. The origin of a window is measured from the upper left hand corner of the root window. To change the size of a window programmatically the following function is available: void fl_winresize(Window win, FL_Coord neww, FL_Coord newh); Resizing will not change the origin of the window (relative to the root window). While the window gets resized originally set restraints will remain unchanged. E.g., if a window was not permitted to be resized interactively it will continue to remain unresizeable by the user. To move a window without resizing it use the following function: void fl_winmove(Window win, FL_Coord newx, FL_Coord newy); To move and resize a window, use the following routine void fl_winreshape(Window win, FL_Coord newx, FL_Coord newy, FL_Coord neww, FL_Coord newh); The following routine is available to iconify a window int fl_iconify(Window win); The return value is nonzero when the message, asking for iconification of the window, was send successfully to the window manager, otherwise zero (but this may not be taken as a sure sign that the window was really iconified). To make a window invisible use void fl_winhide(Window win); A window hidden this way can be shown again later using `*note fl_winshow()::'. To hide and destroy a window, use the following calls void fl_winclose(Window win); There will be no events generated from `*note fl_winclose()::', i.e., the function waits and gobbles up all events for window `win'. In addition, this routine also removes all callbacks associated with the closed window. The following routine can be used to check if a window ID is valid or not int fl_winisvalid(Window win); Note that excessive use of this function may negatively impact performance. Usually an X application should work with window managers and accepts the keyboard focus assignment. In some special situations, explicit override of the keyboard focus might be warranted. To this end, the following routine exists: void fl_winfocus(Window win); After this call keyboard input is directed to window `win'.  File: xforms.info, Node: Cursors, Next: Clipboard, Prev: Windowing Support, Up: Part V Some Useful Functions 33.3 Cursors ============ XForms provides a convenience function to change the cursor shapes: void fl_set_cursor(Window win, int name); where `win' must be a valid window identifier and `name' is one of the symbolic cursor names (shapes) defined by standard X or the integer values returned by `*note fl_create_bitmap_cursor()::' or one of the Forms Library's pre-defined symbolic names. The X standard symbolic cursor names (all starts with `XC_') are defined in `' (you don't need to explicitly include this as `' already does this for you). For example, to set a watch-shaped cursor for form `form' (after the form is shown), the following call may be made fl_set_cursor(form->window, XC_watch); The Forms Library defines a special symbolic constants, `FL_INVISIBLE_CURSOR' that can be used to hide the cursor for window `win': fl_set_cursor(win, FL_INVISIBLE_CURSOR); Depending on the structure of the application program, a call of `XFlush(fl_get_display());' may be required following `*note fl_set_cursor()::'. To reset the cursor to the XForms's default (an arrow pointing northwest), use the following routine void fl_reset_cursor(Window win); To change the color of a cursor use the following routine void fl_set_cursor_color(int name, FL_COLOR fg, FL_COLOR bg); where `fg' and `bg' are the foreground and background color of the cursor, respectively. If the cursor is being displayed, the color change is visible immediately. It is possible to use cursors other than those defined by the standard cursor font by creating a bitmap cursor with int fl_create_bitmap_cursor(const char *source, const char *mask, int w, int h, int hotx, int hoty); where `source' and `mask' are two (x)bitmaps. The mask defines the shape of the cursor. The pixels set to 1 in the mask define which source pixels are displayed. If `mask' is `NULL' all bits in `source' are displayed. `hotx' and `hoty' are the hotspot of the cursor (relative to the source's origin). The function returns the cursor ID which can be used in calls of `*note fl_set_cursor()::' and `*note fl_set_cursor_color()::' etc. Finally, there is a routine to create animated cursors where several cursors are displayed one after another: int fl_create_animated_cursor(int *cur_names, int interval); The function returns the cursor name (ID) that can be shown later via `*note fl_set_cursor()::'. In the function call `cur_names' is an array of cursor names (either X standard cursors or cursor names returned by `*note fl_create_bitmap_cursor()::'), terminated by -1. Parameter `interval' indicates the time each cursor is displayed before it is replaced by the next in the array. An interval about 150 msec is a good value for typical uses. Note that there is currently a limit of 24 cursors per animation sequence. Internally animated cursor works by utilizing the timeout callback. This means that if the application blocks (thus the main loop has no chance of servicing the timeouts), the animation will stop. See demo program `cursor.c' for an example use of the cursor routines.  File: xforms.info, Node: Clipboard, Prev: Cursors, Up: Part V Some Useful Functions 33.4 Clipboard ============== Clipboard is implemented in the Forms Library using the X selection mechanism, more specifically the `XA_PRIMARY' selection. X selection is a general and flexible way of sharing arbitrary data among applications on the same server (the applications are of course not necessarily running on the same machine). The basic (and over-simplified) concept of the X selection can be summarized as follows: the X Server is the central point of the selection mechanism and all applications running on the server communicate with other applications through the server. The X selection is asynchronous in nature. Every selection has an owner (an application represented by a window) and every application can become owner of the selection or lose the ownership. The clipboard in Forms Library is a lot simpler than the full-fledged X selection mechanism. The simplicity is achieved by hiding and handling some of the details and events that are of no interests to the application program. In general terms, you can think of a clipboard as a read-write buffer shared by all applications running on the server. The major functionality you want with a clipboard is the ability to post data onto the clipboard and request the content of the clipboard. To post data onto the clipboard, use the following routine typedef int (*FL_LOSE_SELECTION_CB)(FL_OBJECT *obj, long type); int fl_stuff_clipboard(FL_OBJECT *obj, long type, const void *data, long size, FL_LOSE_SELECTION_CB callback); where `size' is the size (in bytes) of the content pointed to by `data'. If successful, the function returns a positive value and the data will have been copied onto the clipboard. The callback is the function that will be called when another application takes ownership of the clipboard. For textual content the application that loses the clipboard should typically undo the visual cues about the selection. If no action is required when losing the ownership a `NULL'q callback can be passed. The `obj' argument is used to obtain the window (owner) of the selection. `type' is currently unused. At the moment the return value of `lose_selection_callback()' is also unused. The data posted onto the clipboard are available to all applications that manipulate `XA_PRIMARY', such as xterm etc. To request the current clipboard content use the following routine typedef int (*FL_SELECTION_CB)(FL_OBJECT *obj, long type, const void * data, long size); int fl_request_clipboard(FL_OBJECT *obj, long type, FL_SELECTION_CB callback); where `callback' is the callback function that gets called when the clipboard content is obtained. The content `data' passed to the callback function should not be modified. One thing to remember is that the operation of the clipboard is asynchronous. Requesting the content of the clipboard merely asks the owner of the content for it and you will not have the content immediately (unless the asking object happens to own the selection). XForms main event loop takes care of the communication between the requesting object and the owner of the clipboard and breaks up and re-assembles the content if it exceeds the maximum protocol request size (which has a guaranteed minimum of 16 kB, but typically is larger). If the content of the clipboard is successfully obtained the main loop invokes the lose selection callback of the prior owner and then the requesting object's callback function. The function returns a positive number if the requesting object owns the selection (i.e., the callback could beinvoked before the function returned) and 0 otherwise. If there is no selection the selection callback is called with an empty buffer and the length of the buffer is set to 0. In that case `*note fl_request_clipboard()::' returns -1.  File: xforms.info, Node: Part V Resources for Forms Library, Next: Part V Dirty Tricks, Prev: Part V Some Useful Functions, Up: Top 34 Resources for Forms Library ****************************** Managing resources is an important part of programming with X. Typical X programs use extensive resource database/management to customize their appearances. With the help of the Form Designer there is little or no need to specify any resources for the default appearance of an application written using the Forms Library. Because of this, complete resource support is a somewhat low-priority task and currently only minimal support is available. Nevertheless, more complete and useful resource management system specific to the Forms Library can be implemented using the services provided by the XForms. * Menu: * Current Support:: * Going Further::  File: xforms.info, Node: Current Support, Next: Going Further, Up: Part V Resources for Forms Library 34.1 Current Support ==================== At the moment all built-in XForms resources have a top level class name `XForm' and a resource name `xform'. Because of this incomplete specification most of the current resources are "global", in the sense that they affect all form windows. Eventually all resources will be fully resolved, e.g., to specify attribute `foo' of form `formName', the resource name can be `appName.formName.foo' instead of (the current incomplete) `appName.xform.foo'. The argument `app_opt' passed to `*note fl_initialize()::' is a table of structures listing your applications command line options. The structure is defined as follows typedef struct { char * option; char * specifier; XrmOptionKind argKind; void * value; } XrmOptionDescList, FL_CMD_OPT; See `XrmGetResource()' for details. After the initialization routine is called all command line arguments, both XForms built-in and application specific ones, are removed from `argc' and `argv' and parsed into a standard XResources database. To read your application specific options follow `*note fl_initialize()::' with the following routine void fl_get_app_resources(FL_RESOURCE *resource, int nresources); Here `resource' is a table containing application specific resources in the following format: typedef struct { char * res_name; /* resource name without application name */ char * res_class; /* resource class */ FL_RTYPE type; /* C type of the variable */ void * var /* variable that will hold the value */ char * defval; /* default value in string form */ int nbytes; /* buffer size for string var. */ } FL_RESOURCE; and the resource type `FL_RTYPE' type is one of the following `FL_SHORT' for short variable `FL_BOOL' for boolean variable (int) `FL_INT' for int variable `FL_LONG' for long variable `FL_FLOAT' for float variable `FL_STRING' for char[] variable `FL_NONE' for variables not to be used (or not available) Note that the variable for `FL_BOOL' must be of type int. It differs from `FL_INT' only in the way the resources are converted, not in the way their values are stored. A boolean variable is considered to be true (1) if any one of `True', `true', `Yes', `yes', `On', `on', or 1 is specified as its value. For string variables, the length for the destination buffer must be specified. `*note fl_get_app_resources()::' simply looks up all entries specified in the `FL_RESOURCE' structure in all databases after prefixing the resource name with the application name, which can be the new name introduced by the `-name' command line option. Summarized below are the currently recognized Forms Library built-in resources: Resource Name Class Type Default values ---------------------------------------------------------------------------- rgamma Gamma float 1.0 ggamma Gamma float 1.0 bgamma Gamma float 1.0 visual Visual string best depth Depth int best doubleBuffer DoubleBuffer bool true privateColormap PrivateColormap bool false standardColormap StandardColormap bool false sharedColormap SharedColormap bool false pupFontSize PupFontSize int 12pt buttonFontSize FontSize int 10pt sliderFontSize FontSize int 10pt inputFontSize FontSize int 10pt browserFontSize FontSize int 10pt menuFontSize FontSize int 10pt choiceFontSize FontSize int 10pt ulPropWidth ULPropWidth bool true ulThickness ULThickness int 1 scrollbarType ScrollbarType string thin coordUnit CoordUnit string pixel borderWidth BorderWidth int 1 Again, "best" means that the Forms Library by default selects a visual that has the most depth. By default, resource files are read and merged in the order as suggested by X11 R5 as follows: * `/usr/lib/X11/app-defaults/' * `$XAPPRLESDIR/' * `RESOURCE_MANAGER' property as set using `xrdb' if `RESOURCE_MANAGER' is empty, `~/.Xdefaults' * `$XENVIRONMENT' if `$XENVIORONMENT' is empty, `~/.Xdefaults-hostname' * ommand line options All options set via resources may not be the final values used because resource settings are applied at the time an object/form is created, thus any modifications after that override the resource settings. For example `buttonLabelSize', if set, is applied at the time the button is created (`*note fl_add_button()::'). Thus altering the size after the button is created via `*note fl_set_object_lsize()::' overrides whatever is set by the resource database. To run your application in `PseudoColor' with a depth of 8 and a thicker underline, specify the following resources appname*visual: PseudoColor appname*depth: 8 appname*ulThickness: 2 Since resources on a form by form basis are yet to be implemented, there is no point specifying anything more specific although also `appname.XForm.depth' etc. would work correctly. * Menu: * Resources Example::  File: xforms.info, Node: Resources Example, Up: Current Support 34.1.1 Resources Example ------------------------ Let us assume that you have an application named `myapp' and it accepts the options `-foo' _level_ and `-bar' plus a filename. The proper way to initialize the Forms Library is as follows FL_CMD_OPT cmdopt[] = { {"-foo", "*.foo", XrmoptionSepArg, 0 }, {"-bar", ".bar", XrmoptionNoArg, "True"} }; int foolevel, ifbar; int deftrue; /* can only be set thru resources */ FL_resource res[] = { {"foo", "FooCLASS", FL_INT, &foolevel, "0"}, {"bar", "BarCLASS", FL_BOOL, &ifbar, "0"}, {"deftrue", "Whatever", FL_BOOL, &deftrue, "1"} }; int main(int argc, char *argv[]) { fl_initialize(&argc, argv ,"MyappClass", cmdopt, 2); fl_get_app_resources(res, 3); if (argc == 1) /* missing filename */ fprintf(stderr, "Usage %s: [-foo level][-bar] " "filename\n","myapp"); /* rest of the program */ } After this both variables `foolevel' and `ifbar' are set either through resource files or command line options, with the command line options overriding those set in the resource files. In case neither the command line nor the resource files specified the options, the default value string is converted. There is another routine, a resource routine of the lowest level in XForms, which might be useful if a quick-and-dirty option needs to be read: const char *fl_get_resource(const char *res_name, const char *res_class, FL_RTYPE type, char *defval, void *val, int nbytes); `res_name' and `res_class' must be complete resource specifications (minus the application name) and should not contain wildcards of any kind. The resource will be converted according to the type and result stored in `type', which is an integer of type `*note FL_RTYPE::'. `nbytes' is used only if the resource type is `*note FL_STRING::'. The function returns the string representation of the resource value. If a value of `*note FL_NONE::' is passed for `type' the resource is not converted and the pointer `val' is not dereferenced. There is also a routine that allows the application program to set resources programmatically: void fl_set_resource(const char *string, const char *value); where `string' and `value' are a resource-value pair. The string can be a fully qualified resource name (minus the application name) or a resource class. Routines `*note fl_set_resource()::' and `*note fl_get_resource()::' can be used to store and retrieve arbitrary strings and values and may be useful to pass data around.  File: xforms.info, Node: Going Further, Prev: Current Support, Up: Part V Resources for Forms Library 34.2 Going Further ================== It is possible to implement your own form/object specific resources management system using the services mentioned above. For example, to implement a user-configurable form size, code similar to the following can be used, assuming the form is named `"myform"': struct fsize { int width, height; } myformsize; FL_RESOURCE res[] = { {"myform.width", "XForm.width", FL_INT, &myform.width, "150"}, {"myform.height","XForm.height", FL_INT, &myform.height, "150"} }; fl_initialize(&argc, argv, app_class, 0, 0); fl_get_app_resources(res, 2); /* create the forms */ myform = fl_bgn_form(myformsize.width, myformsize.height,.....); Or (more realistically) you create the form first using `fdesign' and then scale it before it is shown: fl_initialize(&argc, argv, app_class, 0, 0); fl_get_app_resources(res, 2); /*create_all_forms here */ fl_set_form_size(myform, mysformsize.width, myformsize.height); fl_show_form(myform, ...); Since eventually form geometry and other things might be done via XForms internal routines it is recommended that you name your form to be the form title with all spaces removed and the first letter lower-cased, i.e., if a form is shown with a label `Foo Bar', the name of the form should be `fooBar'.  File: xforms.info, Node: Part V Dirty Tricks, Next: Part V Trouble Shooting, Prev: Part V Resources for Forms Library, Up: Top 35 Dirty Tricks *************** This chapter describes some of the routines that may be used in special situations where more power or flexibility from Forms Library is needed. These routines are classified as "dirty tricks" either because they can easily mess up the normal operation of Forms Library or they depend on internal information that might change in the future, or they rely too much on the underlying window systems. Thus whenever possible, try not to use these routines. * Menu: * Interaction:: * Other::  File: xforms.info, Node: Interaction, Next: Other, Up: Part V Dirty Tricks 35.1 Interaction ================ * Menu: * Form Events:: * Object Events::  File: xforms.info, Node: Form Events, Next: Object Events, Up: Interaction 35.1.1 Form Events ------------------ It is possible to by-pass the form event processing entirely by setting a "raw callback" that sits between the event reading and dispatching stage, thus a sneak preview can be implemented and optionally the event can even be consumed before the libraries internal form processing machinery gets to it. Use the following routines to register such a preemptive processing routine typedef int (*FL_RAW_CALLBACK)(FL_FORM *, void *xevent); FL_RAW_CALL_BACK fl_register_raw_callback(FL_FORM *form, unsigned long mask, FL_RAW_CALLBACK callback); where `mask' is the event mask you are interested in (same as the XEvent mask). The function returns the old handler for the event. Currently only handlers for the following events are supported * `KeyPressMask' and `KeyReleaseMask' * `ButtonPressMask' and `ButtonReleaseMask' * `EnterWindowMask' and `LeaveWindowMask' * `ButtonMotionMask' and `PointerMotionMask' * `FL ALL EVENT' (see below) Further, there is only one handler for each event pair, (e.g., `ButtonPress' and `ButtonRelease'), thus you can't have two separate handlers for each pair although it is possible to register a handler only for one of them (but almost always a mistake) if you know what you're doing. If you register a single handler for more than one pair of events, e.g., setting mask to `KeyPressMask|ButtonPressMask', the returned old handler is random. A special constant, `FL_ALL_EVENT', is defined so that the handler registered will received all events that are selected. To select events, use `*note fl_addto_selected_xevent()::'. Once an event handler is registered and the event is detected, then instead of doing the default processing by the dispatcher, the registered handler function is invoked. The handler function must return either `FL_PREEMPT' if the event is consumed) and `0' otherwise so that the internal processing of the event can continue. See the demo program `minput2.c' for an example. Since these kind of handlers work on a rather low level there's a chance that they interfere with some mechanisms of the library. Consider the case of setting a raw callback handler for mouse press and release events, in which the handler returns `0' for mouse press events but `FL_PREEMPT' on relese events. In that case the mouse press event results in the normal processing and e.g., a button below the mouse will receive it (and be drawn correspondingly). To be drawn again in its normal way it also needs to receive the release event (even if the mouse isn't on top of it anymore when the mouse button is released). But when the handler function doesn't also let the release event propagate to the normal handling of events then the button will never receive the expected release event and will stay drawn in the way as if the release event never happened. Thus one should avoid having different return values from the handler for pairs of related events.  File: xforms.info, Node: Object Events, Prev: Form Events, Up: Interaction 35.1.2 Object Events -------------------- Just as you can by-pass the internal event processing for a particular form, you can also do so for an object. Unlike in raw callbacks, you can not select individual events. The mechanism provided is via the registration of a pre-handler for an object. The pre-handler will be called before the built-in object handler. By electing to handle some of the events, a pre-handler can, in effect, replace part of the built-in handler. In *note the chapter about pre-emptive handlers: Part IV Using a Pre-emptive Handler. the API was already discussed in detail, so here we just repeat the discussion for completeness as any use of pre-emptive handler is considered "dirty tricks". To register a pre-handler, use the following routine typedef int (*FL_HANDLEPTR)(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, int key, void *raw_event); void fl_set_object_prehandler(FL_OBJECT *, FL_HANDLEPTR prehandler); where `event' is the generic event in the Forms Library, that is, `FL DRAW', `FL ENTER' etc. The arguments `mx' and `my' are the mouse position and `key' is the key pressed. The last parameter, `raw_event' is a pointer to the XEvent that caused the invocation of the pre-handler. cast to a void pointer. Notice that the pre-handler has the same function prototype as the built-in handler. Actually they are called with the exact same parameters by the event dispatcher. The prehandler should return `0' if the processing by the built-in handler should continue. A return value of `FL PREEMPT' will prevent the dispatcher from calling the built-in handler. See demo program `preemptive.c' for an example. A similar mechanism exists for registering a post-handler, i.e., a handler invoked after the built-in handler is finished, by using void fl_set_object_posthandler(FL_OBJECT *, FL_HANDLEPTR prehandler); Whenever possible a post-handler should be used instead of a pre-handler.  File: xforms.info, Node: Other, Prev: Interaction, Up: Part V Dirty Tricks 35.2 Other ========== As stated earlier, `*note fl_set_defaults()::' can be used to modify the Forms Library's defaults prior to calling `*note fl_initialize()::'. Actually, this routine can also be used after `*note fl_initialize()::' to override the values set on the command line or in the application databases. However, overriding users' preferences should be done with discretion. Further, setting `privateColormap' after `*note fl_initialize()::' has no effect.  File: xforms.info, Node: Part V Trouble Shooting, Next: Part VI, Prev: Part V Dirty Tricks, Up: Top 36 Trouble Shooting ******************* This chapter deals with a number of (common) problems encountered by people using the Forms Library. Ways of avoiding them are presented. `fl show form()' only draws the form partially This only happens if immediately following `*note fl_show_form()::' the application program blocks the execution (e.g., waiting for a socket connection, starting a new process via `fork()' etc.). To fix this problem, you can flush the X buffer manually using `fl_update_display(1)' before blocking occurs or use an idle callback to check the status of the blocking device or let the main loop handle it for you via `*note fl_add_io_callback()::'. I updated the value of a slider/counter/label, but it does not change This only happens if the update is followed by a blockage of execution or a long task without involving the main loop of Forms Library. You can force a screen update using `fl_update_display(1)'. I found a bug in XForms, What do I do? Please consider subscribing to the XForms mailing list at `http://lists.nongnu.org/mailman/listinfo/xforms-development' and sending an email with information about the bug you found. Please try to post information about the version of the Forms Library you're using and your OS beside a description of the bug. Some sample code that exhibits the erratic behavior would help greatly. If, for some reasons, you don't want subscribe to the mailing list you may also send an email to one of the maintainers. At the moment you probably should first contact Jens Thoms Toerring, <>.  File: xforms.info, Node: Part VI, Next: Part VI Images, Prev: Part V Trouble Shooting, Up: Top _Part VI - Image Support API_ ***************************** * Menu: * Part VI Images::  File: xforms.info, Node: Part VI Images, Next: Index of Functions, Prev: Part VI, Up: Top 37 Images ********* Although images are not typically a part of the GUI, they are often part of an application. For this reason and others, image support is part of Forms Library. It is somewhat not unexpected that the users of a graphical user interface want some graphics support. The most important reason to have image support in the library is the amount of questions/requests on the mailing list of the Forms Library about images. It convinced us that having image support will make many Forms Library users life easier. The second reason has something to do with image support in X, which at best is cumbersome to use as the API reflects the underlying hardware, which, at the level of Xlib, is quite appropriate, but not quite what an application programmer wants to deal with. Image support in Forms Library for the large part is hardware independent. This is possible because xforms makes distinction between the real image it keeps and the image being displayed. At the expense of some flexibility and memory requirement, the high-level image support API should prove to be useful for most situations. The third reason is that image support as it is now in the library is well isolated and is only linked into an application when it is actually being used. This is not a trivial point in the consideration to include image support in the library proper. * Menu: * The Basic Image Support API:: * The FL_IMAGE Structure:: * Supported image types:: * Creating Images:: * Supported Image Formats:: * Setup and Configuration:: * Simple Image Processing:: * Utilities::  File: xforms.info, Node: The Basic Image Support API, Next: The FL_IMAGE Structure, Up: Part VI Images 37.1 The Basic Image Support API ================================ Reading and displaying images are quite easy. It can be as simple as a couple of lines of code: FL_IMAGE *image; if ((image = flimage_load("imagefilename")) image->display(image, win); In this example, an image is created from a file, then the image is displayed in a window, `win'. For most casual uses, this is really what is needed to load and display an image. As you may have guessed, an image in Forms Library is represented by a structure of type `FL_IMAGE'. In addition to the pixels in the image, it also keeps a variety of information about the image such as its type, dimension, lookup tables etc. Further, if the image can not be displayed directly on the display hardware (for example, the image is 24 bits, while the display is only capable of 8 bits), a separate displayable image is created and displayed. Any manipulation of the image is always performed on the original high-resolution image, and a new displayable image will be created if necessary. Writing an image is just as simple if (flimage_dump(image, "filename", "jpeg") < 0) fprintf(stderr,"image write failed"); In this code snippet, an image in memory is written to a file in JPEG format. As you might have noticed by now, all image routines start with flimage. The exact APIs for reading and writing an image are as follows FL_IMAGE *flimage_load(const char *filename); int flimage_dump(FL_IMAGE *im, const char *filename, const char *fmt); The function `*note flimage_load()::' takes a filename and attempts to read it. If successful, an image (or multiple images) is created and returned. If for any reason the image can't be created (no permission to read, unknown file format, out of memory etc), a null pointer is returned. As will be documented later, error reporting and progress report can be configured so these tasks are performed inside the library. The function `*note flimage_dump()::' takes an image, either returned by `*note flimage_load()::' (possibly after some processing) or created on the fly by the application, attempts to create a file to store the image. The image format written is controlled by the third parameter `fmt'q, which should be either the formal name or the short name of one of the supported formats (such as jpeg, ppm, gif, bmp etc., see section 23.3) or some other formats the application knows how to write. If this parameter is `NULL', the original format the image was in is used. If the image is successfully written, a non-negative number is returned, otherwise a negative number. Depending on how the image support is configured, error reporting may have already occurred before the function returns. Given these two routines, a file converter (i.e., changing the image file format) is simple if ((image = flimage_load("inputfile")) flimage_dump(image, "outfile", "newformat"); See the demo program `iconvert.c' for a flexible and usable image converter. To free an image, use the following routine void flimage_free(FL_IMAGE *image); The function first frees all memory allocated for the image, then the image structure itself. After the function returns, the image should not be referenced. The following routines are available to display an image in a window int flimage_display(FL_IMAGE *image, FL_WINDOW win); int flimage_sdisplay(FL_IMAGE *image, FL_WINDOW win); where `win' is a window ID. If the image(s) is successfully displayed, a non-negative integer is returned, a negative integer otherwise. The difference between the two display routines is that `*note flimage_sdisplay()::' only displays a single image while `*note flimage_display()::', built on top of `flimage_sdisplay()', can display single or multiple images. For typical use, `*note flimage_display()::' or `image->display' should be used. `*note flimage_sdisplay()::' is useful only if you're coding your own multi-image display routine. For example, `*note flimage_display()::' is built roughly like the following int flimage_display(FL_IMAGE *im, FL_WINDOW win) { int err; for (err = 0; err >=0 && im; im = im->next) { err = flimage_sdisplay(im, win); fl_update_display(0); fl_msleep(im->setup->delay); } return err; } And you can build your own multi-frame image display routine to suit your application's needs. Despite the display routine's simple look, this function performs tasks that involve the details of dealing with different hardware capabilities, a daunting task for beginners. For PseudoColor displays (i.e., using color maps or color lookup tables), a color quantization or dithering step may be performed by the function to reduce the number of colors in the image (of course, the colorreduced image is kept only for display, the original image is untouched so future processing is carried out on the original full resolution image, rather than the displayed, an approximate of the original image). In general, when the information in an image is reduced in order to display it, the original image is not altered in any way. For example, this function can display a 24bit image on a 1bit display without losing any information on the original 24bit image. By default, the entire image is displayed at the top-left corner of the window. To display the image at other locations within the window (perhaps to center it), use the `image->wx' and `image->wy' fields of the `FL_IMAGE' structure. These two fields specify where in the window the origin of the image should be. By repeatedly changing `image->wx' and `image->wy' and displaying, image panning can be implemented. It is also possible to display a subimage by specifying non-zero value for `(image->sx,image->sy)' and `(image->sw, image->sh)'. You can view the image as a 2D space with the origin at the top left corner. The positive y axis of the image space is pointing downward. `(image->sx,image->sy)' specify the subimage offset into the image (they must be non-negative) and `(image->sw,image->sh)' specify the width and height of the subimage. Taken the window offset and the subimage together, the more accurate statement of the functionality of the the function `*note flimage_display()::' is that it displays a subimage specified by `(image->sx,image->sy)' and `(image->sw,image->sh)' starting at `(image->wx, image->wy)'. You can also use clipping to display a subimage by utilizing the following functions and `image->gc' fl_set_gc_clipping(image->gc, x, y, w, h); fl_unset_gc_clipping(image->gc); where the coordinates are window coordinates. Of course, by manipulating `image->gc' directly, more interesting clipping or masking can be achieved. Since the GC is visual dependent, a newly created image before displaying may not yet have a valid GC assoiated with it. If you must set some clipping before displaying, you can set the `image->gc' yourself beforehand. Note that you if you free the GC, make sure you reset it to `None'. To display an image in a canvas, the following can be used flimage_display(image, FL_ObjWin(canvas)); Since this function only knows about window IDs, and writes to the window directly, it may not be sensitive to the status of the form the canvas is on, e.g., a frozen form. In your application, you should check the status of the form before calling this function. Sometimes it may be useful to find out if a specific file is an image file before attempting to read it (for example, as a file filter). To this end, the following routine exists int flimage_is_supported(const char *file); The function returns true if the specified file is a known image file. If the file is not a known image or not readable for any reason, the function return 0.  File: xforms.info, Node: The FL_IMAGE Structure, Next: Supported image types, Prev: The Basic Image Support API, Up: Part VI Images 37.2 The `FL_IMAGE' Structure ============================= Before we go into more details on image support, some comments on the image structure are in order. The image structure contains the following basic fields that describe fully the image in question and how it should be displayed. typedef unsigned char FL_PCTYPE; /* primary color type */ #define FL_PCBITS 8 /* primary color bits */ #define FL_PCMAX ((1<map len'. Although `alpha lut' is always allocated for a color index image, it's currently not used by the Forms Library. `map_len' The length of the colormap (lookup table). `app_background' A packed RGB value indicating the preferred color to use for the background of an image (also known as transparent color). This field is initialized to an illegal value. Since there is no portable way to obtain the window background the application has to set this field if transparency is to be achieved. In future versions of image support, other means of doing transparency will be explored and implemented. `wx, wy' The window offset to use to display the image. `sx, sy, sw, sh' The subimage to display. `comments' This is typically set by the loading routines to convey some information about the image. The application is free to choose how to display the comment, which may have embedded newlines in it. `io_spec' This field is meant for the reading/writing routine to place format specific state information that otherwise needs to be static or global. `spec_size' This field should be set to the number of bytes `io_spec' contains. `display' A function you can use to display an image. The image loading routine sets this function. `next' This is a link to the next image. This is how `*note flimage_load()::' chains multiple image together. `double_buffer' If true, the display function will double-buffer the image by using a pixmap. For typical image display it's not necessary to enable double-buffering as it is very expensive (memory and speed). Double-buffering may be useful in image editing. `pixmap' The backbuffer pixmap if double-buffered. Although it is generally not necessary for an application to access individual pixels, the need to do so may arise. In doing so, it is important to consult the `image->type' field before dereferencing any of the pixel field. That is, you should access `image->ci' only if you know that the image type is `FL_IMAGE_CI' or `FL_IMAGE_MONO'.  File: xforms.info, Node: Supported image types, Next: Creating Images, Prev: The FL_IMAGE Structure, Up: Part VI Images 37.3 Supported image types ========================== Forms Library supports all common and not-so-common image types. For example, the supported images range from the simple 1 bit bitmap to full 24 bit RGB images. 12 bit gray scale images (common in medical imaging) are also supported. The supported image types are denoted using the following constants, all of them (except `FL_IMAGE_FLEX') using a different bit, so they can be bitwise ORed together: FL_IMAGE_MONO, /* 1 bit bitmaps */ FL_IMAGE_GRAY, /* gray-scale image (8 bit) */ FL_IMAGE_GRAY16, /* gray-scale image (9 to 16 bit) */ FL_IMAGE_CI, /* generic color index image */ FL_IMAGE_RGB, /* 24 bit RGB(A) image */ FL_IMAGE_PACKED, /* 24 bit RGB(A) image. Packed storage */ FL_IMAGE_FLEX, /* All of the above */ For the 24 bit variety another 8 bit (`image->alpha' and the top-most byte of the packed integer) is available for the application, perhaps storing the alpha values into it. The Forms Library does not modify or reference this extra byte. Mono (b&w) images are stored as a colormap image with a lut of length 2. The `FL_IMAGE_FLEX' type is mainly for the reading and loading routines to indicate the types they are capable of handling. For example, if you're coding an output routine, you use `FL_IMAGE_FLEX' to indicate that the output routine can take any type the image. Otherwise the driver will convert the image type before handing the image over to the actual output routine. In displaying an image of type `FL_IMAGE_GRAY16', window leveling, a technique to visualize specific ranges of the data, is employed. Basically, you specify a window level (`level') and a window width (`wwidth') and the display function will map all pixels that fall within `level-width/2' and `level+width/2' linearly to the whole dynamic range of the intensities the hardware is capable of displaying. For example, if the display device can only display 256 shades of gray, `level-width/2' is mapped to 0 and `level+width/2' is mapped to 255, and pixels values between `level-width/2' and `level+width/2' are linearly mapped to values between 0 and 255. Pixel values that fall below `level-width/2' are mapped to zero and those that larger than `level+width/2' are mapped to 255. Use the following routine to set the window level int flimage_windowlevel(FL_IMAGE *im, int level, int wwidth); The function returns 1 if window level parameters are modified, otherwise 0 is returned. Setting `wwidth' to zero disables window leveling. Note that if `im' points to a multiple image, window level parameters are changed for all images. To obtain the image type name in string format, e.g., for reporting purposes, use the following routine const char *flimage_type_name(int type); To convert between different types of images, the following routine is available int flimage_convert(FL_IMAGE *image, int newtype, int ncolors); The parameter `newtype' should be one of the supported image types mentioned earlier in this section. Parameter `ncolors' is meaningful only if `newtype' is `FL_IMAGE_CI'. In this case, it specifies the number of colors to generate, most likely from a color quantization process. If the conversion is successful a non-negative integer is returned, otherwise a negative integaer. Depending on which quantization function is used, the number of quantized colors may not be more than 256. To keep information loss to a minimum, `*note flimage_convert()::' may elect to keep the original image in memory even if the conversion is successful. For example, converting a full color image (24 bit) into a 8 bit image and then converting back can lose much information of the image if the converting function does not keep the original image. What this means is that the following sequence gets back the original image /* the current image is RGB. Now we reduce the full color image to 8 bit color index image. The conversion routine will keep the 24 bit color. */ flimage_convert(image, FL_IMAGE_CI, 256); /* Now convert back to RGB for image processing. The con- version routine will notice that the input image was originally converted from a 24bit image. Instead of doing the conversion, it simply retrieves the saved image and returns. */ flimage_convert(image, FL_IMAGE_RGB, 0); This behavior might not always be what the application wants. To override it, you can set `image->force_convert' to 1 before calling the conversion routine. Upon function return the flag is reset to zero.  File: xforms.info, Node: Creating Images, Next: Supported Image Formats, Prev: Supported image types, Up: Part VI Images 37.4 Creating Images ==================== With the basic fields in the image structure and image types explained, we're now in a position to tackle the problem of creating images on the fly. The data may have come from some simulations or some other means, the task now is to create an image from the data and try to display/visualize it. The first task involved in creating an image is to create an image structure that is properly initialized. To this end, the following routine is available FL_IMAGE *flimage_alloc(void); The function returns a pointer to a piece of dynamically allocated memory that's properly initialized. The task next is to put the existing data into the structure. This involves several steps. The first step is to figure out what type of image to create. For scalar data, there are two logical choices, either a gray-scale intensity image or a color index image with the data being interpreted as indices into some lookup table. Both of these may be useful. Gray-scale imagse are straight forward to create and the meaning of the pixel values is well defined and understood. On the other hand with color-mapped image you can selectively enhance the data range you want to visualize by choosing appropriate color-maps. For vector data, RGB image probably makes most sense. In any case it's strictly application's decision. All that is needed to make it work with Forms Library is to set the `image->type' field to a valid value. Of course the image dimension (width and height) also needs to be set. Once this is done, we need to copy the data into the image structure. Before we copy the data we create the destination storage using one of the following routines void *fl_get_matrix(int nrows, int ncols, unsigned int elem_size); int flimage_getmem(FL_IMAGE *image); The `*note fl_get_matrix()::' function creates a 2-dimensional array of entities of size `elem_size'. The array is of `nrows' by `ncols' in size. The 2D array can be passed as a pointer to pointer and indexed as a real 2D arrays. The `*note flimage_getmem()::' routine allocates the proper amount of memory appropriate for the image type, including colormaps when needed. After the destination storage is allocated, copying the data into it is simple image->type = FL_IMAGE_GRAY; image->w = data_columns; image->h = data_row; flimage_getmem(image); /* or you can use the instead im->gray = fl_get_matrix(im->h, im->w, sizeof **im->gray); */ for (row = 0; row < image->h; row++) for (col = 0; col < image->w; col++) image->gray[row][col] = data_at_row_and_col; Of course, if data is stored row-by-row, a `memcpy(3)' instead of a loop over columns may be more efficient. Also if your data are stored in a single array, `*note fl_make_matrix()::' might be a lot faster as it does not copy the data. If the created image is a color index image, in addition to copying the data to `image->ci', you also need to set the lookup table length `image->map_len', which should reflect the dynamic range of the data: image->type = FL_IMAGE_CI; image->w = A; image->h = B; image->map_len = X; flimage_getmem(image); /* this will allocate ci and lut */ for (row = 0; row < image->h; row++) for (col = 0; col < image->w; col++) image->ci[row][col] = data; for (i = 0; i < image->map_len; i++) { image->red_lut[i] = some_value_less_than_FL_PCMAX; image->green_lut[i] = some_value_less_than_FL_PCMAX; image->blue_lut[i] = some_value_less_than_FL_PCMAX; } If the type is `FL_IMAGE_GRAY16', you also need to set `image->gray_maxval' to the maximum value in the data. Now we're ready to display the image flimage_display(image, win); As mentioned before, the display routine may create a buffered, display hardware specific and potentially lower-resolution image than the original image. If for any reason, you need to modify the image, either the pixels or the lookup tables, you need to inform the library to invalidate the buffered image: image->modified = 1;  File: xforms.info, Node: Supported Image Formats, Next: Setup and Configuration, Prev: Creating Images, Up: Part VI Images 37.5 Supported Image Formats ============================ There are many file formats for image storage. The popularity, flexibility and cleanness of the different formats varies. Forms Library supports several popular ones, but these are not the only ones that are popular. Toward the end of this section, it will be outlined how to extend the image support in the Forms Library so more image file can be read by `*note flimage_load()::'. * Menu: * Built-in support:: * Adding New Formats:: * Queries::  File: xforms.info, Node: Built-in support, Next: Adding New Formats, Up: Supported Image Formats 37.5.1 Built-in support ----------------------- Each image file format in Forms Library is identified by any one of three pieces of information, the formal name, the short name, and the file extension. For example, for the GIF format, the formal name is `"CompuServe GIF"'(1), the short name is `"GIF"', and file extension is `"gif"'. This information is used to specify the output format for `*note flimage_dump()::'. The following table summarizes the supported file formats with comments FormalName ShortName Extension Comments ------------------------------------------------------------------------------- Portable Pixmap ppm ppm Portable Graymap pgm pgm Portable Bitmap pbm pbm CompuServe GIF gif gif Windows/OS2 BMP file bmp bmp JPEG/JFIF format jpeg jpg X Window Bitmap xbm xbm X Window Dump xwd xwd X PixMap xpm xpm XPM3 only NASA/NOST FITS fits fits Standard FITS and IMAGE extension Portable Network Graphics png png needs netpbm SGI RGB format iris rgb need pbmplus/netpbm package PostScript format ps ps needs gs for reading Tagged Image File Format tiff tif no compression support To avoid executable bloating with unnecessary code, only ppm, pgm, pbm and compression filters (gzip and compress) are enabled by default. To enable other formats, call `flimage_enable_xxx()' once anywhere after `*note fl_initialize()::', where `xxx' is the short name for the format. For example, to enable BMP format, `flimage_enable_bmp()' should be called. Further, if you enable GIF support, you're responsible for any copyright/patent and intellectual property dispute arising from it. Under no circumstance should the authors of the Forms Library be liable for the use or misuse of the GIF format. Usually there are choices on how the image should be read and written. The following is a rundown of the built-in options that control some aspects of image support. Note that these options are persistent in nature and once set they remain in force until reset. typedef struct { int quality; int smoothing; } FLIMAGE_JPEG_OPTIONS; void flimage_jpeg_output_options(FLIMAGE_JPEG_OPTIONS *option); The default quality factor for JPEG output is 75. In general, the higher the quality factor rhe better the image is, but the file size gets larger. The default smoothing factor is 0. void flimage_pnm_output_options(int raw_format); For PNM (ppm, pgm, and pbm) output, two variants are supported, the binary (raw) and ASCII format. The raw format is the default. If the output image is of type `FL_IMAGE_GRAY16', ASCII format is always output. void flimage_gif_output_options(int interlace); If `interlace' is true, an interlaced output is generated. Transparency, comments, and text are controlled, respectively, by `image->tran_rgb', `image->comments' and `image->text'. PostScript options affect both reading and writing. FLIMAGE_PS_OPTION *flimage_ps_options(void); where the control structure has the following members `int orientation' The orientation of the generated image on paper. Valid options are `FLPS_AUTO', `FLPS_PORTRAIT' and `FLPS_LANDSCAPE'. The default is `FLPS_AUTO'. `int auto_fit' By default, the output image is scaled to fit the paper if necessary. Set it to false (0) to turn auto-scaling off. `float xdpi, ydpi' These two are the screen resolution. Typical screens these days have resolutions about 80 dpi. The settings of these affect both reading and writing. `float paper_w' The paper width, in inches. The default is 8.5 in. `float paper_h' The paper height, in inches. The default is 11.0 in `char* tmpdir' A directory name where temporary working files go. The default is `/tmp'. `float hm, vm' Horizontal and vertical margins, in inches, to leave when writing images. The default is 0.4 in (about 1 cm). `float xscale' Default is 1.0. `float yscale' Default is 1.0. `int first_page_only' If set, only the first page of the document will be loaded even if the document is multi-paged. The default setting is false. To change an option, simply call `*note flimage_ps_options()::' and change the field from the pointer returned by the function: void SetMyPageSize(float w, float h) { FLIMAGE_PS_OPTION *options = flimage_ps_options(); options->paper_w = w; options->paper_h = h; } All these option setting routines can be used either as a configuration routine or an image-by-image basis by always calling one of these routines before `*note flimage_dump()::'. For example, flimage_jpeg_output_options(option_for_this_image); flimage_dump(im, "file","jpeg"); You can also utilize the `image->pre_write' function to set the options. This function, if set, is always called inside `*note flimage_dump()::' before the actual output begins. ---------- Footnotes ---------- (1) The Graphics Interchange Format (c) is the Copyright property of CompuServe Incorporated. GIF(sm) is a Service Mark property of CompuServe Incorporated.  File: xforms.info, Node: Adding New Formats, Next: Queries, Prev: Built-in support, Up: Supported Image Formats 37.5.2 Adding New Formats ------------------------- It is possible for application to add new formats to the library so `*note flimage_load()::' and `*note flimage_dump()::' know how to handle them. Basically, the application program tells the library how to identify the image format, and the image dimension, and how to read and write pixels. The API for doing so is the following typedef int (*FLIMAGE_Identify) (FILE *); typedef int (*FLIMAGE_Description) (FL_IMAGE *); typedef int (*FLIMAGE_Read_Pixels) (FL_IMAGE *); typedef int (*FLIMAGE_Write_Image) (FL_IMAGE *); int flimage_add_format(const char *formal_name, const char *short_name, const char *extension, int type, FLIMAGE_Identify identify, FLIMAGE_Description description, FLIMAGE_Read_Pixels read_pixels, FLIMAGE_Write_Image write_image); where we have `formal_name' The formal name of the image format `short_name' An abbreviated name for the image format `extension' File extension, if this field is `NULL', `short_name' will be substituted `type' The image type. This field generally is one of the supported image types (e.g., `FL_IMAGE_RGB'), but it does not have to. For image file formats that are capable of holding more than one type of images, this field can be set to indicate this by ORing the supported types together (e.g., `FL_IMAGE_RGB|FL_IMAGE_GRAY'). However, when description returns, the image type should be set to the actual type in the file. `identify' This function should return 1 if the file pointed to by the file pointer passed in is the expected image format (by checking signature etc.). It should return a negative number if the file is not recognized. The decision if the file pointer should be rewound or not is between this function and the description function. `description' This function in general should set the image dimension and type fields (and colormap length for color index images) if successful, so the driver can allocate the necessary memory for read pixel. Of course, if `read_pixels' elects to allocate memory itself, the `description' function does not have to set any fields. However, if reading should continue, the function should return 1 otherwise a negative number. The function should read from input file stream `image->fpin'. It is likely that some information obtained in this function needs to be passed to the actual pixel reading routine. The easiest way is, of course, to make these information static within the file, but if a GUI system is in place, all the reading routines should try to be reentrant. The method to avoid static variables is to use the `image->io_spec' field to keep these information. If this field points to some dynamically allocated memory, you do not need to free it after `read_pixels' function finishes. However, if you free it or this field points to static memory, you should set to this field to `NULL' when finished. The following is a short example showing how this field may be utilized. typedef struct { int bits_per_pixel; int other_stuff; } SPEC; static int description(FL_IMAGE *im) { SPEC *sp = fl_calloc(1, sizeof *sp); im->io_spec = sp; im->spec_size = sizeof *sp; sp->bits_per_pixel = read_from_file(im->fpin); return 0; } static int read_pixels(FL_IMAGE *im) { SPEC *sp = im->io_spec; int bits_per_pixel = sp->bits_per_pixel; read_file_based_on_bits_per_pixel(im->fpin); /* You don't have to free im->io_spec, but if you do remember to set it to NULL before returning */ return 0; } `read_pixels' This function reads the pixels from the file and fills one of the pixel matrix in the image structure depending on the type. If reading is successful, a non-negative number should be returned otherwise a negative number should be returned. Upon entry, `image->completed' is set to zero. The function should not close the file. `write_image' This function takes an image structure and should write the image out in a format it knows. Prior to calling this routine, the driver will have already converted the image type to the type it wants. The function should return 1 on success and a negative number otherwise. If only reading of the image format is supported this parameter can be set to `NULL'. The function should write to file stream `image->fpout'. By calling `*note flimage_add_format()::' the newly specified image format is added to a "recognized image format" pool in the library. When `*note flimage_load()::' is called the library, after verifying that the file is readable, loops over each of the formats and calls the `identify' routine until a format is identified or the pool exhausted. If the file is recognized as one of the supported formats the `description' routine is called to obtain the image dimension and type. Upon its return the library allocates all memory needed, then calls `read_pixels'. If the image format pool is exhausted before the file is recognized `*note flimage_load()::' fails. On output, when `*note flimage_dump()::' is called, the requested format name is used to look up the output routine from the image format pool. Once an output routine for the requested format is found, the library looks the image type the output is capable of writing. If the current image type is not among the types supported by the format the library converts image to the type needed prior to calling the output routine `write_image()'. So what `*note flimage_dump()::' does is int flimage_dump(FL_IMAGE *im, const char *filename, const char *formatName) { format = search_image_format_pool(formatName); if (!format) return -1; im->fpout = fopen(filename); if (im->pre_write) im->pre_write(im); convert image type if necessary(im); format->write_pixels(im); ... } If the name of the image format supplied by `*note flimage_add_format()::' is identical to one that is already supported, the new routines replace those that are in the pool. This way, the application can override the built-in supports. For a non-trivial example of adding a new format, see file `flimage_jpeg.c'. Another way of adding image formats is through external filters that convert an unsupported format into one that is. All you need to do is inform the library what external filter to use. `pbmplus' or `netpbm' are excellent packages for this purpose. The library has two functions that deal with external filters int flimage_description_via_filter(FL_IMAGE * im, char *const *cmds, const char *what, int verbose); int flimage_write_via_filter(FL_IMAGE *im, char *const *cmds, char *const formats[], int verbose); where `cmds' are a list of shell commands (filters) that convert the format in question into one of the supported formats. Parameter `what' is for reporting purposes and parameter `verbose' controls if some information and error messages should be printed. This is mainly for debugging purposes. Let us go through one example to show how this filter facility can be used. In this example, we support SGI's rgb format via the `netpbm' package. As with regular image format, we first define a function that identifies the image format: static int IRIS_identify(FILE *fp) { char buf[2]; fread(buf, 1, 2, fp); return (buf[0] == '\001' && buf[1] == '\332') || (buf[0] == '\332' && buf[1] == '\001'); } Then we need to define the filter(s) that can convert a RGB file into one that's supported. Here we use `sgitopnm', but you can use diferent filters if available. Function `*note flimage_description_via_filter()::' will try all the filters specified until one of them succeeds. If none does an error code is returned: static int IRIS_description(FL_IMAGE *im) { static char *cmds[] = {"sgitopnm %s > %s", NULL /* sentinel, indicating end of list of filters */ }; return flimage_description_via_filter(im, cmds, "Reading RGB...", 0); } All commands should be suitable format strings for function `sprintf()' and contain `%s' twice. The first one will be replaced by the input file name, the second by a filename which will be supplied by the library to hold the converted image. The list must be terminate with a `NULL' element. In the above example, `sgitopnm %s > %s' specifies the external command, `sgitopnm', and how it operates. Basically, the library will do a `sprintf(cmdbuf, cmd[i], irisfile, tmpfile)' and then execute `cmdbuf'. There is really no need for a load function as the filter will have already invoked the correct load function when it returns. For the record of capability queries, a dummy load function is needed: static int IRIS_load(FL_IMAGE * im) { fprintf(stderr, "We should never get here...\n"); return -1; } Writing an image is similar: static int IRIS_dump(FL_IMAGE *im) { static char *cmds[] = {"pnmtosgi %s > %s", NULL}; static char *cmds_rle[] = {"pnmtosgi -rle %s > %s", NULL}; static char *formats[] = {"ppm", "pgm", "pbm", NULL}; return flimage_write_via_filter(im, rle ? cmds_rle : cmds, formats, 0); } Again, the external commands should accept two arguments. The first argument will be supplied by the library, a temporary file that holds the converted image in a format the filter understands, and the second argument will be the requested output filename. For output, an additional argument is required. The additional argument `formats' specifies the image format accepted by the external filter. In this case, this is the pnm format. It is important that if the filter accepts more than one format, you should specify the formats in decreasing generality, i.e., ppm, pgm, pbm. With these functions in place, finally we're ready to add iris support into the library void add_iris(void) { flimage_add_format("SGI Iris", "iris", "rgb", FL_IMAGE_RGB|FL_IMAGE_GRAY|FL_IMAGE_MONO, IRIS_identify, IRIS_description, IRIS_load, IRIS_dump); } After a call of `add_iris()' you can now use `*note flimage_load()::' and `*note flimage_dump()::' to read and write SGI iris format just like any other format.  File: xforms.info, Node: Queries, Prev: Adding New Formats, Up: Supported Image Formats 37.5.3 Queries -------------- Since the number of formats supported by the library is dynamic in nature, some query routines are available to obtain support information. To obtain the number of currently supported image formats, use the routine int flimage_get_number_of_formats(void); The functions returns the number of formats supported, for reading or writing or both. To obtain detailed information for each format, the following can be used typedef struct { const char * formal_name; const char * short_name; const char * extension; int type; int read_write; int annotation; } FLIMAGE_FORMAT_INFO; const FLIMAGE_FORMAT_INFO *flimage_get_format_info(int n); where parameter `n' is an integer between 1 and the return value of `*note flimage_get_number_of_formats()::' . Upon function return a static buffer is returned containing the basic information about the image. The read_write field can be one of the following combinations thereof `FLIMAGE_READABLE' supports reading `FLIMAGE_WRITABLE' supports writing or the bitwise OR of both. These two routines are most useful for reporting or presenting capabilities to the user FLIMAGE_FORMAT_INFO *info; int n = flimage_get_number_of_formats(); fprintf(stderr,"FL supports the following format\n"); for (; n; n--) { info = flimage_get_format_info(n); fprintf(stderr,"%s format\t(%c%c)\n", info->short_name, (info->read_write & FLIMAGE_READABLE) ? 'r' : ' ', (info->read_write & FLIMAGE_WRITABLE) ? 'w' : ' '); }  File: xforms.info, Node: Setup and Configuration, Next: Simple Image Processing, Prev: Supported Image Formats, Up: Part VI Images 37.6 Setup and Configuration ============================ Although the image support is designed with integration into a GUI system in mind, it neither assumes what the GUI system is nor does it need a GUI system to work. As a matter of fact, for the most part it doesn't even need an X connection to work (obviously without a connection, you won't be able to display images). For this reason, some of the typical (and necessary) tasks, such as progress and error reporting, are by default implemented only to use text output (i.e., to `stderr'). Obviously, with a GUI in place this is not quite adequate. Hooks are available for application program to re-define what to do with these tasks. The interface to the library configuration is as follows void flimage_setup(FLIMAGE_SETUP *setup); where the parameter `setup' is a pointer to a structure defined as follows: typedef struct { void * app_data; int (*visual_cue) (FL_IMAGE *im, const char *msg); void (*error_message) (FL_IMAGE *im, const char *msg); const char * rgbfile; int do_not_clear; int max_frames; int delay; int double_buffer; int add_extension; } FLIMAGE_SETUP; with `app_data' The application can use this field to set a value so the field `image->app_data' in all image structures returned by the library will have this value. It's most useful to set this field to something that's persistent during the application run, such as the `fdui' structure of the main control panel. Note that `image->app_data' is different from `image->u_vdata' in that all image structures returned by the library have the same value of `image->app_data', which is set by the library. In contrast, `image->u_vdata' is set by the application on an image-by-image basis. `visual_cue' This is the function that will be called by all image reading, writing and processing routines. The function is meant to give the user some visual feedback about what is happening. For lengthy tasks, this function is called repeatedly and periodically to indicate what percentage of the task is completed and to give the application program a chance to check and process GUI activities (for example, via `*note fl_check_forms()::'). The first parameter to the function is the image currently being worked on and the second parameter is a short message, indicating the name of the task, such as "Reading JPG" etc. Two fields in the image structure can be used to obtain progress information. The member fields `image->total' indicates the total amount of work to be done in some arbitrary units (usually number of rows in the image). `image->completed' indicates how much of the task has been completed. The percentage of how much is completed is then simply the ratio of `image->completed' and `image->total', multiplied by 100. At the begin of a task `image->completed' is set to a value less or equal 1, and at the end of the task, `image->completed' is set to `image->total'. A special value of -1 for `image->completed' may be used to indicate a task of unknown length. `error_message' This is a function that is called when an error (of all severities) has occurred inside the library. It is recommanded that the application provide a means to show the messages to the user by sypplying this function. The first parameter is a pointer to the image that's being worked on, and the second parameter is a brief message, such as "memory allocation failed" etc. A convenience function, `*note flimage_error()::', is provided to call the error message handler. `rgbfile' This field should be set to the full path to the color name database (`rgb.txt') if your system has it in non-standard locations. On most systems, this file is `/usr/lib/X11/rgb.txt', which is the default if this field is not set.(1) `do_not_clear' By default, `*note flimage_display()::' clears the window before displaying the image. Set this member to 1 to disable window clearing. `no_auto_extension' By default, `*note flimage_dump()::' changes the filename extension to reflect the format. Set this member to 1 to disable extension substitution. `double_buffer' If set, all image display will by default double-buffered. Double-buffering an image is very expensive (in terms of both resource and speed) as the backbuffer is simulated using a pixmap. If there are no annotations, double-buffering an image does not really improve anything. It is far better to turn double-buffering on and off on a image-by-image basis using the `image->double_bufffer' field. `max_frames' This field specifies the maximum number of frames to read by `*note flimage_load()::'. The default maximum is 30 frames. `delay' This field specifies the delay (in milliseconds) between successive frames. It is used by the `*note flimage_display()::' routine. Note that it is always a good idea to clear the setup structure before initializing and using it FLIMAGE_SETUP mysetup; memset(mysetup, 0, sizeof mysetup); mysetup.max_frames = 100; mysetup.delay = 10; flimage_setup(&mysetup); It is possible to modify the image loading process by utilizing the following routines `*note flimage_load()::' is based on: FL_IMAGE *flimage_open(const char *name); This function takes a file name and returns an image sturcture pointer if the file is a recognized image file. Otherwise `NULL' is returned. The function FL_IMAGE *flimage_read(FL_IMAGE *im); takes an image structure returned by `*note flimage_open()::' and fills the image structure. Between `*note flimage_open()::' and `*note flimage_read()::' you can inspect or modify fields in the image structure. int flimage_close(FL_IMAGE *im); This function closes all file streams used to create the image. ---------- Footnotes ---------- (1) The routine where this field is used searches some more locations than the default and should work on most systems automagically.  File: xforms.info, Node: Simple Image Processing, Next: Utilities, Prev: Setup and Configuration, Up: Part VI Images 37.7 Simple Image Processing ============================ Some simple image processing capabilities are present in the Forms Library image support. All the image processing routines take an image as a parameter and process it in place. If appropriate, only the subimage specified by `(image->subx, image->suby)' and `(image->subw, image->subw)' is affected (note these are different fields from those for subimage displaying). The subimage fields are best set via user interaction, perhaps by having a rubber band that the user can drag to set the size. In the following, each routine will be briefly explained. * Menu: * Convolution:: * Tint:: * Rotation:: * Image Flipping:: * Cropping:: * Scaling:: * Warping:: * General Pixel Transformation:: * Image Annotation:: * Write Your Own Routines::  File: xforms.info, Node: Convolution, Next: Tint, Up: Simple Image Processing 37.7.1 Convolution ------------------ Convolution or filtering can be done easily using the following routine int flimage_convolve(FL_IMAGE *im, int **kernel, int krow, int kcol); This function takes a convolution kernel of `krow' by `kcol' and convolves it with the image. The result replaces the input image. The kernel size should be odd. If successful, the function returns a positive integer, otherwise a negative number. The kernel should be allocated by `*note fl_get_matrix()::'. To use a kernel that's a C 2-dimensional array (cast to a pointer to int), use the following function int flimage_convolvea(FL_IMAGE *im, int *kernel, int krow, int kcol); The difference between these two functions is in their usage syntax: int **kernel1 = fl_get_matrix(sizeof **kernel, n, m); int kernel2[n][m]; kernel1[x][y] = z; kernel2[x][y] = z; flimage_convolve(im, kernel1, n, m); flimage_convolvea(im, (int*) kernel2, n, m); /* note the cast */ Two special built-in kernels are designated with the following symbolic constants `FLIMAGE_SMOOTH' indicates a 3 by 3 smoothing kernel `FLIMAGE_SHARPEN' indicates a 3 by 3 sharpening kernel  File: xforms.info, Node: Tint, Next: Rotation, Prev: Convolution, Up: Simple Image Processing 37.7.2 Tint ----------- Tint as implemented in the Forms Library emulates the effect of looking at an image through a piece of colored glass. You can specify the color and transparency of the glass: int flimage_tint(FL_IMAGE *im, unsigned int packed, double opacity); where the parameter `packed' is a packed RGB color, specifying the color of the glass. `opacity' specifies how much the color of the image is absorbed by the glass. A value of 0 means the glass is totally transparent, i.e., the glass has no effect3, while a value of 1.0 means total opaqueness, i.e., all you see is the color of the glass. Any value between these two extremes results in a color that is a combination of the pixel color and the glass color. For example, to tint a part of the image bluish, you can set `packed' to `FL_PACK(0,0,200)' and use an opacity of 0(1). Tint is most useful in cases where you want to put some annotations on the image, but do not want to use a uniform and opaque background that completely obscures the image behind. By using tint, you can have a background that provides some contrast to the text, yet not obscures the image beneath completely. Tint operation uses the subimage settings. ---------- Footnotes ---------- (1) Strictly speaking, a piece of glass that is totally transparent can't have colors.  File: xforms.info, Node: Rotation, Next: Image Flipping, Prev: Tint, Up: Simple Image Processing 37.7.3 Rotation --------------- Image rotation can be easily done with the following routine int flimage_rotate(FL_IMAGE *im, int angle, int subpixel); where `angle' is the angle in one-tenth of a degree (i.e., a 45 degree rotation should be specified as 450) with a positive sign for counter-clock rotation. The parameter `subpixel' should be one of the following, specifying if subpixel sampling should be enabled. It can be set to either `FLIMAGE_NOSUBPIXEL' or `FLIMAGE_SUBPIXEL'. If subpixel sampling is enabled, the resulting image pixels are interpolated from the original pixels. This usually has an "anti-aliasing" effect that leads to less severe jagged edges and similar artifacts commonly encountered in rotations. However, it also means that a color indexed image gets converted to a RGB image. If preserving the pixel value is important, you should not turn subpixel sampling on. `*note flimage_rotate()::' return a negative number if it for some reason (usually due to running out of memory) fails to perform the rotation. Since the rotated image has to be on a rectangular grid, the regions that are not occupied by the image are filled with a fill color, where the default is black. If a different fill color is desired you can set the `image->fill_ccolor' field to a packed RGB color before calling the rotation function. Note that even for color indexed images the fill color should be specified in RGB. The rotation function will search the colormap for the appropriate index if no subpixel sampling is used. Repeated rotations should be avoided if possible. If you have to call it more than once it's a good idea to crop after rotations in order to get rid of the regions that contain only fill color.  File: xforms.info, Node: Image Flipping, Next: Cropping, Prev: Rotation, Up: Simple Image Processing 37.7.4 Image Flipping --------------------- Image flipping refers to the mirror operation in x- or y-direction at the center. For example, to flip the columns of an image, the left and right of the image are flipped (just like having a vertical mirror in the center of the image) thus the first pixel on any given row becomes the last, and the last pixel becomes the first etc. The API for flipping is as follows int flimage_flip(FL_IMAGE *im, int what); where `what' can be `'c'' or `'r''. indicating if column and row flipping is desired.  File: xforms.info, Node: Cropping, Next: Scaling, Prev: Image Flipping, Up: Simple Image Processing 37.7.5 Cropping --------------- There are two functions available to crop an image int flimage_autocrop(FL_IMAGE *im, unsigned int background); int flimage_crop(FL_IMAGE *im, int xl, int yt, int xr, int yb); The first function, as its name suggests, automatically crops an image using the background as the color to crop. The function works by searching the image from all four sides and removing all contiguous regions of the uniform background from the sides. The image is modified in place. If cropping is successful, a non-negative integer is returned, otherwise -1. If `background' is specified as the constant `FLIMAGE_AUTOCOLOR', the background is chosen as the first pixel of the image. The second function uses the parameters supplied by the user to crop the image. `xl' and `xr' are the offsets into the image from the left and the right sides, respectively, i.e., if both `xl' and `xr' are 1, the cropping removes the first column and the last column from the image. Parameters `yt' and `yb' specify the offsets into the image from the top and bottom of the image respectively. Note the offsets do not have to be positive. When they are negative, they indicate enlargement of the image. The additional regions are filled with the uniform color specified by `image->fill_color', a packed RGB color. This can be quite useful to add a couple of pixels of border to an image. For example, the following adds a 1 pixel wide yellow border to an image image->fill_color = FL_PACK(255,255,0); flimage_crop(image, -1, -1, -1, -1); Another function is available that can be used to obtain the auto-cropping offsets int flimage_get_autocrop(FL_IMAGE *im, unsigned background, int *xl, int *yt, int *xl, int *yb); This function works the same way as `*note flimage_autocrop()::', except that no actual cropping is performed. Upon function return the parameters `xl', `yt', `xl' and `yb' are set to the offsets found by the function. The application can then make adjustment to these offsets and call `*note flimage_crop()::'.  File: xforms.info, Node: Scaling, Next: Warping, Prev: Cropping, Up: Simple Image Processing 37.7.6 Scaling -------------- An image can be scaled to any desired size with or without subpixel sampling. Without subpixel sampling simple pixel replication is used, otherwise a box average algorithm is employed that yields an anti-aliased image with much less artifacts. A special option is available that scales the image to the desired size but keeps the aspect ratio of the image the same by filling the part of the image that would otherwise be empty. The main entry point to the scaling function is int flimage_scale(FL_IMAGE *im, int newwidth, int newheight, int option); where the parameters `newwidth' and `newheight' specify the desired image size. Parameter `option'q can be one of the following constants or the bitwise OR of them: `FLIMAGE_NOSUBPIXEL' scale the image with no subpixel sampling `FLIMAGE_SUBPIXEL' scale the image with subpixel sampling `FLIMAGE_ASPECT' scale the image with no aspect ratio change `FLIMAGE_CENTER' center the scaled image if aspect `FLIMAGE_NOCENTER' do not center the scaled image For example, `FLIMAGE_ASPECT|FLIMAGE_SUBPIXEL' requests fitting the image to the new size with subpixel sampling. `FLIMAGE_ASPECT' specifies a scaling that results in an image of the requested size (even if the scales are different for width and height) without changing the aspect ratio of the original image by filling in the stretched regions with the fill color `image->fill_color', a packed RGB color: im->fill_color = FL_PACK(255,0,0); flimage_scale(im, im->w+2, im->h, FLIMAGE_SUBPIXEL|FLIMAGE_ASPECT); This code generates an image that is two pixels wider than the original image but with the same aspect ratio. The two additional pixel columns on each side of the image are filled with the fill color (red), yielding a red border. The fitting can be useful in turning a series of images of unequal sizes into images of equal sizes with no perceptible change in image quality. Depending on what the application requires, simple scaling (zooming) with no subpixel sampling is much faster than box averaging or blending, but subpixel sampling tends to yield smoother images with less scaling artifacts.  File: xforms.info, Node: Warping, Next: General Pixel Transformation, Prev: Scaling, Up: Simple Image Processing 37.7.7 Warping -------------- Image warping (or texture mapping in 2D) refers to the transformation of pixel coordinates. Rotation, scaling and shearing etc. are examples of (linear and non-perspective) image warping. In typical applications some of the commonly used pixel coordinate transformations are implemented using more efficient algorithms instead of a general warping. For example, image rotation is often implemented using three shears rather than a general warp (Forms Library implements rotation via image warping). Non-perspective linear image warping in general is characterized by a 2x2 warp matrix `W' and a translation vector `T' with two elements as follows P' = W * P + T where `P' is a vector describing a position via it's x and y coordinates and `P'' is the position after warping. The elements `w[i][j]' of the warp matrix are constants (if the warp matrix isn't constant or is of higher order, we usually call such a transformation morphing rather than warping). Since our destination for the warped image is an array of pixels rather than a properly defined coordinate system (such as a window) the translation has no meaning. For the following discussion, we assume the translation vector is zero. (In doing the actual warping, the warped image is indeed shifted so it starts at the (0,0) element of the array representing it). Although, theoretically, any 2D matrix can be used as a warp matrix, there are practical constraints in image warping due to the discreteness of pixel coordinates. First of all, we have to snap all pixel coordinates onto a 2D rectangular integer grid. This in general will leave holes in the warped image because two pixels may get mapped to a single destination location, leaving a hole in the destination image. Secondly, truncation or rounding the resulting floating point values introduces errors. Because of these reasons, image warping is performed in reverse. That is, instead of looping over all pixel coordinates in the original image and transforming those into new coordinates, we start from the new coordinates and use inverse warping to obtain the coordinates of the pixel in the original image. This requires that the inverse of the warp matrix must exist (which is the case if `w[0][0] * w[1][1] != w[0][1] * w[1][0]', i.e., the warp matrix has a non-vanishing determinante). With inverse warping the transformation becomes a re-sampling of the original image, and subpixel sampling (anti-aliasing) can be easily implemented. The following function is available in the library to perform warping int flimage_warp(FL_IMAGE *im, float matrix[][2], int neww, int newh, int subpixel); where `matrix' is the warp matrix. `neww' and `newh' specify the warped image size. To have the warp function figure out the minimum enclosing rectangle of the warped image you can pass zeros for the new width and height. Nevertheless, you can specify whatever size you want and the warp function will fill the empty grid location with the fill color. This is how the aspect ratio preserving scaling is implemented. In general, the warped image will not be rectangular in shape. To make the image rectangular the function fills the empty regions. The fill color is specified by setting the `image->fill_color' field with a packed RGB color. The last argument, `subpixel' specifies if subpixel sampling should be used. Although subpixel sampling adds processing time, it generally improves image quality significantly. The valid values for this parameter is any logical OR of `FLIMAGE_NOSUBPIXEL', `FLIMAGE_SUBPIXEL' and `FLIMAGE_NOCENTER'. `FLIMAGE_NOCENTER' is only useful if you specify an image dimension that is larger than the warped image, and in that case the warped image is flushed top-left within the image grid, otherwise it is centered. To illustrate how image warping can be used, we show how an image rotation by an angle `deg' can be implemented: float m[2][2]; m[0][0] = m[1][1] = cos(deg * M_PI / 180.0); m[0][1] = sin(deg * M_PI / 180.0); m[1][0] = -m[0][1]; flimage_warp(im, mat, 0, 0, FLIMAGE_SUBPIXEL); Please note that the transformation is done in-place, i.e., after the function returns the image structure pointer, `im', points to the rotated image. If you specify a warp matrix with the off-diagonal elements being zero (scaling matrix), the image will only be scaled (in x-direction by `m[0][0]' and in y-direction by `m[1][1]') without being also rotated. By experimenting with various warp matrices you can obtain some interesting images. Just keep in mind that large values of the warp matrix elements tend to make the final image larger.  File: xforms.info, Node: General Pixel Transformation, Next: Image Annotation, Prev: Warping, Up: Simple Image Processing 37.7.8 General Pixel Transformation ----------------------------------- Many image processing tasks can be implemented as seperate RGB transformations. These transformations can be done very efficiently through the use of lookup tables. For this reason the following routine exists: int flimage_transform_pixels(FL_IMAGE *im, int *red, int *green, int *blue); where `red', `green' and `blue' are the lookup tables of a length of at least `FL_PCMAX + 1' (typically 256). The function returns a postive number on success and the image will be replaced. Note that this routine notices the settings of the subimage, i.e., you can transform a portion of the image. To illustrate the use of this routine let's look at how a simple contrast adjustment may be implemented: #include #include int AdjustContrast(FL_IMAGE *im) { int r[FL_PCMAX+1], g[FL_PCMAX+1], b[FL_PCMAX+1]; int i, scale = 10; /* in this example rgb are adjusted the same way */ for ( i = 0; i <= FL_PCMAX; i++) r[i] = g[i] = b[i] = i * log10(1 + i * scale / FL_PCMAX ) / log10( 1 + scale ); return flimage_transform_pixels(im, r, g, b); }  File: xforms.info, Node: Image Annotation, Next: Write Your Own Routines, Prev: General Pixel Transformation, Up: Simple Image Processing 37.7.9 Image Annotation ----------------------- You can annotate an image with text or simple markers (arrows etc.). The location of the annotation can either be in pixel coordinate system or some application defined coordinate system. * Menu: * Using Text Strings:: * Using Markers:: * Pixelizing the Annotation::  File: xforms.info, Node: Using Text Strings, Next: Using Markers, Up: Image Annotation 37.7.9.1 Using Text Strings ........................... To place text into the image, use the following routine int flimage_add_text(FL_IMAGE *im, const char *str, int len, int fstyle, int fsize, unsigned tcolor, unsigned bcolor, int nobk, double tx, double ty, int rotation); where `fstyle' and `fsize' are the same as the label font style and size defined earlier in Section 3.11.3. `tcolor' and `bcolor' specify the colors to use for the text `str' and the background if the `nobk' argument is false. If `nobk' is true the text is drawn without a background. `tx' and `ty' specify the location of the text relative to the image origin. The location specified is the lower-right corner of the text. Note that the location specified can be in some physical space other than pixel space. For example, if the pixel-pixel distance represents 10 miles on a map, you'd like to be able to specify the text location in miles rather than pixels. The location is converted into pixel space using the following code tx_pixel = im->xdist_scale * tx + im->xdist_offset; ty_pixel = im->ydist_scale * ty + im->ydist_offset; By default, the offsets `im->xdist_offset' and `im->yxdist_offset' are initialized to 0 and the scales `im->xdist_scale' and `im->ydist_scale' to 1. The function returns the current number of strings for the image. The interpretation of text used also used elsewhere applies, i.e., if `str' starts with character `@' a symbol is drawn. There is another function, maybe more convenient depending on the application, that you can use int flimage_add_text_struct(FL_IMAGE *im, const FLIMAGE_TEXT *text); With this function instead of passing all the parameters individual;y you pass a `FLIMAGE_TEXT' structure to the function. The structure has the following fields: `str' The string to append to the image. `len' Length of the string in bytes. `x, y' A location relative to the image origin, given in pixels (no conversion from other coordinate systems is done) `align' Specifies the alignment of the string relative to the give location. `style, size' The font style and size to use. `color' The text color `bcolor' The background color `nobk' If true indicates that no background is to be drawn. `angle' Angle (in thenth of a degree) the text is to be rotated from the default horizontal orientation. Currently only PostScript output handles this correctly. To delete the all texts you added to an image, use void flimage_delete_all_text(FL_IMAGE *im); You also can suppress the display of annotation text without deleting it. To do this, simply set `im->dont_display_text' to true.  File: xforms.info, Node: Using Markers, Next: Pixelizing the Annotation, Prev: Using Text Strings, Up: Image Annotation 37.7.9.2 Using Markers ...................... In addition to text strings you can also add simple markers (arrows, circles etc) to your image. To add a marker to an image use the following routines int flimage_add_marker(FL_IMAGE *im, const char *name, double x, double y, double w, double h, int linestyle, int fill, int rotation, FL_COLOR, FL_COLOR bcol); int flimage_add_marker_struct(FL_IMAGE *im, const FLIMAGE_MARKER *m); where `name' is the marker name (see below for a list of built-in markers). The marker name must consist of regular ASCII characters. `linestyle' indicates the line style (`FL_SOLID', `FL DOT' etc., see Chapter 27 for a complete list. `fill' indicates if the marker should be filled or not. `x' and `y' are the coordinates of the center of the marker in physical coordinates (i.e., the same transformation as described above for annotated texts is applied), `w' and `h' are the size of the bounding box of the marker, again in physical coordinates. Every marker has a natural orientation from which you can rotate it. The angle of rotation is given by `rotation' in tenth of a degree. `col' is the color of the marker, in packed RGB format. `bcol' is currently un-used. The second function takes a structure that specifies the marker. The members of the structure are as follows: `name' The name of the marker. `x, y' Position of center of the marker in pixel coordinates, relative to the origin of the image. `w, h' The size of the bounding box in pixel coordinates. `color' The color of the marker in packed RGB format. `fill' If true the marker is filled. `thickness' The line line thickness used for drawing. `style' The line style to be used for drawing. `angle' Angle of rotation in tenth of a degree from the marker's nature orientation. If successful both functions return the number of markers that are currently associated with the image, otherwise a negative number. Some built-in markers in different orientations are shown in Fig. 22.1. To delete all markers added to an image use the function void flimage_delete_all_markers(FL_IMAGE *im); Of course the library would not be complete without the ability for applications to define new markers. The following function is provided so you can define your own markers: int flimage_define_marker(const char *name, void (*draw) (FLIMAGE_MARKER *marker), const char *psdraw); When the marker is to be drawn the function `draw()' is called with the marker structure. In addition to the fields listed above the following fields are filled by the library to facilitate the operation of drawing the marker `display' The display to be drawn on. `gc' The GC to be used in drawing `win' The window to draw to. `psdraw' A string that draws a marker in a square with the corner coordinates (-1, -1), (-1, 1), (1, 1) and (1, -1) in PostScript. For example the rectangle marker has the following `psdraw' string: -1 -1 moveto -1 1 lineto 1 1 lineto 1 -1 lineto closepath Defining new markers is the preferred method of placing arbitary drawings onto an image as it works well with double-buffering and pixelization of the markers.  File: xforms.info, Node: Pixelizing the Annotation, Prev: Using Markers, Up: Image Annotation 37.7.9.3 Pixelizing the Annotation .................................. Annotations placed on the image are kept seperate from the image pixels themselves. The reasons for doing so are twofold. First, keeping the annotation seperate makes it possible to later edit the annotations. The second reason is that typically the screen has a lower resolutions than other output devices. By keeping the annotations seperate from the pixels makes it possible to obtain better image qualities when the annotations are rendered on higher-resolution devices (for example a PostScript printer). If for some reason making the annotations a part of the image pixels is desired, use the following routine int flimage_render_annotation(FL_IMAGE *image, FL_WINDOW win); The function returns -1 if an error occurs. The parameter `win' is used to create the appropriate pixmap. After the function returns the annotations are rendered into the image pixels (thus an annotation or a part of it that was outside of the image is lost). Note that during rendering the image type may change depending on the capabilities of `win'. Annotations that were kept separately are deleted. Note that the image must have been displayed at least once prior to calling this function for it to work correctly. You can always enlarge the image first via the cropping function with some solid borders. Then you can put annotation outside of the original image but within the enlarged image. Not all image formats support the storage of text and markers. This means if you attempt to save an image that has associated text and markers into an image format that does not support it, you may lose the annotation. All pnm formats supports the storage of annotations. To find out if a particular format supports annotation storage, look at the annotation field of the `FLIMAGE_FORMAT_INFO' structure. A zero value indicates it does not support it.  File: xforms.info, Node: Write Your Own Routines, Prev: Image Annotation, Up: Simple Image Processing 37.7.10 Write Your Own Routines ------------------------------- The only communication required between an image processing routine and the rest of the image routines is to let the display routine know that the image has been modified by setting `image->modified' to 1. This information is used by the display routine to invalidate any buffered displayable images that were created from the original image. After displaying, `image->modified' is reset by the display routine.  File: xforms.info, Node: Utilities, Prev: Simple Image Processing, Up: Part VI Images 37.8 Utilities ============== In the following some of the utilities that may come in handy when you're writing image manipulation routines are described. * Menu: * Memory Allocation:: * Color Quantization:: * Remarks::  File: xforms.info, Node: Memory Allocation, Next: Color Quantization, Up: Utilities 37.8.1 Memory Allocation ------------------------ To create a matrix to be used in several of the functions listed above use either `*note fl_get_matrix()::' described above or void *fl_make_matrix(int nrow, int ncol, unsigned int esize, void *inMem); where `nrow' and `ncol' are the number of rows and columns of the matrix respectively. `esize' is the size (in bytes) of each matrix element. Both functions return a two-dimensional array of entities of size `esize'. The first function initializes all elements to zero. The second function does not allocate nor initialize memory for the matrix itself. Instead it uses the memory with address `inMem' that is supplied by the caller, which should be a one-dimensional array of length `nrow * ncol * esize'. You can use the returned pointer as a regular two-dimensional array (`matrix[r][c]') or as a single array of length `nrow *ncol', starting from at `matrix[0]': short **matrix = fl_get_matrix(nrow, ncol, sizeof **matrix); /* access the matrix as a 2-d array */ matrix[3][4] = 5; /* or access it as 1D array */ *(matrix[0] + 3 * ncol + 4) = 5; /* most useful in image processing to use it as 1D array */ memcpy(saved, matrix, nrow * ncol * sizeof **matrix); To free a matrix allocated using one the above functions, use void fl_free_matrix(void *matrix); The function frees all memory allocated. After the function returns the matrix cab not be de-referenced anymore. In the case where the matrix was created by `*note fl_make_matrix()::' the function will only free the memory that's allocated to hold the matrix indices but not the memory supplied by the caller. It is the caller's responsibility to free that part of the memory. There are also some useful functions that manipulate images directly. The following is a brief summary of them. FL_IMAGE *flimage_dup(FL_IMAGE *im); This function duplicates an image `im' and returns the duplicated image. At the moment, only the first image is duplicated even if the input image has multiple frames. Furthermore, markers and annotations are not duplicated. Pixmap flimage_to_pixmap(FL_IMAGE *im, FL_WINDOW win); int flimage_from_pixmap(FL_IMAGE *im, Pixmap pixmap); The first function converts an image into a Pixmap (a server side resource) that can be used in the pixmap object (see pixmap-class???). The second function does the reverse. `im' must be a properly allocated image.