Chapter 2
XmHTML Widgets

 

Have you ever wanted to write an application capable of displaying HTML documents, went out to find a Widget which offered HTML display capabilities and finally give up on your wonderfull idea when no such widget seemed to be available? Maybe you did find a HTML widget but were not satisfied with its capabilities? With the XmHTML Widget described in this chapter you no longer have to despair.

With the XmHTML Widget one can create applications that range from simple HTML viewers to full-blown WWW browsers. The XmHTML Widget offers a widget capable of displaying HTML documents and, as one would expect, allows full interactive use of HTML documents thru various callback resources and convenience functions. Besides of being able to display HTML documents, displaying of plain text and images is also supported.

XmHTML widgets provide the following mechanisms for program control:

XmHTML widgets implement the full HTML 3.2 standard as well as a few extensions. The chapter on HTML Extensions lists the supported extensions. One supported extension that is worthwhile to mention is the <FRAMESET> extension.

The current implementation of the XmHTML widget does have its limitations though. The most important features lacking in the current release (1.0.22 Alpha) are support for HTML tables and progressive document loading. All these features will be added in due time.

As you will notice in this chapter, the behaviour and name of a number of XmHTML's resources and convenience functions closely resemble those of Motif's Text widget. The first reason for this similarity is that both widget's display text. The second reason is that, if you are already familiar with Motif's Text widget, the interface to a XmHTML widget will also look familiar and thus learning how to use a XmHTML widget will be a bit easier.

2.1 The Basics of a XmHTML Widget

To understand and appreciate most of the material described in this chapter, it is important to address some of the basic resources and functions provided by the XmHTML widget. The widget's text and HTML verification process, document fonts, dimensions of the widget and hyperlink resources are among the most important. We begin with how to create a standard XmHTML widget.

2.1.1 Creating a XmHTML Widget

Applications that wish to use XmHTML widgets need to include the file <XmHTML.h>. You can create a standard XmHTML widget using the XtVaCreateManagedWidget() function:
	Widget html_w;

	html_w = XtVaCreateManagedWidget("name", xmHTMLWidgetClass, parent,
		resource-value pairs,
		NULL);
The resource-value pairs can be any number of resources specific to the XmHTML widget or its superclasses. Please note that the XmHTML widget is subclassed from the Manager widget class.

Another way of creating a standard XmHTML widget is by using the XmCreateHTML() convenience function:

	Widget html_w;

	html_w = XmCreateHTML(parent, "name", args, nargs);
The args argument can be any number of resource-value pairs specified using the XtSetArg function. nargs specifies how many resource-value pairs are contained in the args argument.

2.1.2 The XmHTML Widget's Text

The first thing to learn about the XmHTML widget is how to access its internal text storage. The most simple method to do this is by using the XmNvalue resource. Unlike most widgets in the Motif toolkit that use text, the XmHTML widget does not use compound strings for its value. Instead, the value is specified as a regular C string as shown in example 2-1.

Example 2-1. The simple_html.c program

/* simple_html.c -- Create a minimally configured XmHTML widget */
#include <XmHTML.h>

main(int argc, char **argv)
{
	Widget toplevel;
	XtAppContext app;

	toplevel = XtVaAppInitialize(&app, "Demos", NULL, 0,
		&argc, argv, NULL, NULL);

	XtVaCreateManagedWidget("html", xmHTMLWidgetClass, toplevel,
		XmNvalue, "<html><body>A minimally configured XmHTML widget."
			"</body></html>",
		NULL);

	XtRealizeWidget(toplevel);
	XtAppMainLoop(app);
}
This short program simply creates a XmHTML widget whose initial value is set to "A minimally configured XmHTML widget." (See Figure 2-1).

[A Simple HTML Widget]
Figure 2-1. simple_html.c: a minimally configured XmHTML widget

HTML Verification

As one can see in the above example, the value of the XmNvalue resource contains five HTML elements: four markup and one text element. This brings us to one of the most important aspects of the XmHTML widget: it is a widget for displaying HTML documents. HTML documents are text documents written in the HyperText Markup Language, which is a language composed of a set of elements that define a document and guide its display.

In order for XmHTML to display such a document properly, it is necessary that the widget verifies the correctness of these markup elements. It does this by comparing the contents of the document that is to be displayed against the now official W3C HTML 3.2 recommendation (referred to as the HTML 3.2 standard). This standard defines both appearance and content of each markup element. During this verification process (which is performed before anything is displayed), the widget goes thru great lengths as to ensure that the document adheres to this standard. As such, the widget is capable of modifying the specified markup elements substantially (it will never modify text elements). The disadvantage of this document verification is that a document might not look as expected, but the main advantage is that even the most horrid HTML document will be displayed.

A detailed description of this document verification and repair process is given in the next chapter, so we will only mention that the more a document adheres to the HTML 3.2 standard, the less the widget will modify the markup elements.

Setting a XmHTML Widget's Text

The initial value of the XmNvalue resource may be set either at initialization time (as in example 2-1) or by using XtVaSetValues:
	XtVaSetValues(html_w, XmNvalue, text, NULL);
This resource always represents the entire text of the widget.

Another method for setting the text in a XmHTML widget is by using the convenience routine XmHTMLTextSetString:

	void
	XmHTMLTextSetString(html_w, value)
		Widget  html_w;
		char   *value;
Although the two methods produce the same results, the convenience routine may be more efficient since it accesses the internals of the widget directly (The XtVaSetValues() method involves going through the X Toolkit Intrinsics). On the other hand, if you are also setting many other resources at the same time, the XtVaSetValues method is better since it saves the overhead of multiple function calls (All resources can be set in a single call).

Whichever function you use, the value (however it is provided) is copied into the internals of the widget. This is important to known if you have provided the value via an allocated memory buffer: once the text is set in the widget, you can safely free this memory buffer.

A XmHTML widget's may contain large amounts of text provided there is enough memory available on the computer running the client application. The upper limit on the number of bytes a XmHTML widget may have is given by the maximum value of an unsigned int (4 gigabyte on 32 bit systems), so it is more likely that the user's computer will run out of memory before the XmHTML widget's maximum capacity is reached.

Getting a XmHTML Widget's Text

XmHTML widget's provide several methods for obtaining text. Before learning how to get the widget's text however, it is important to make a distinction between the source text and the displayed text. The source text is the text you have set into a XmHTML widget by using the XmNvalue resource (or by using the XmHTMLTextSetString() convenience function), while the displayed text is the source text after HTML verification has taken place. This distinction is important since XmHTML widget's will never modify the source text but instead create a new HTML document while the HTML verification process is taking place.

[Note: A new HTML document is not actually created: when text is set in a XmHTML widget, the HTML parser translates the source text to a list of internal objects. The new HTML document that is mentioned in the above paragraph only exists in theory but can be created upon request as we will soon see.]

You might ask yourself why this distinction is important. The answer is that it depends on the type of application you are using a XmHTML widget for. Lets assume you are using a XmHTML widget in an application that displays HTML documents you have retrieved via a network layer (a WWW browser for example). When the user of your application wishes to save the document he has been viewing, it is most likely he wishes to save the original document and not the verified document. On the other hand, when you are using a XmHTML widget in an application intended for writing and/or verifying HTML documents, the most obvious thing a user would want to save is his verified document.

Getting the Source Text

So how does one go about getting a XmHTML widget's text? To get the widget's source text, you can use either XtVaGetValues() or the XmHTMLTextGetSource() convenience function. Both methods return the value of the XmNvalue resource. The value returned is a pointer to the internal text storage, which may never be freed nor modified. The code fragment below demonstrates how to use the convenience function:
	String text_p;

	if((text_p = XmHTMLTextGetSource(html_w)) != NULL)
	{
		/* Save, display the text, but never modify or free it */
	}
Or, by using the Xt function XtVaGetValues():
	String text;

	XtVaGetValues(html_w, XmNvalue, &text_p, NULL);
Similar to using the XmHTMLTextSetString() convenience function to set a XmHTML widget's text, using the XmHTMLTextGetSource() to get a XmHTML widget's text may be more efficient since it accesses the internals of the widget directly instead of going through the X Toolkit Intrinsics.

[Note: It is also worthwhile to mention that all of the XmHTML... convenience functions access the internals of the XmHTML widget directly. None of them involves going through the X Toolkit Intrinsics.]

If you want to modify the source text, or you just want a copy of the text that you can modify for other reasons, then you must copy the text into your own local dataspace:

	char *text_p, buf[1024];

	text_p = XmHTMLTextGetSource(html_w);

	(void)strncpy(buf, text_p, 1024);
	/* modify "buf" all you want */
Here, we don't know how much text text_p points to, but for purposes of this demonstration, we assume it's not more than 1024 bytes. Just to be sure that we don't cause an overflow of buf, we use strncpy(). Let me stress that, unless you are intimately familiar with the nature of the XmHTML widget your are using, it is unwise to make general assumptions about the amount of text you are getting.

Getting the Displayed Text

The method to get the displayed text is very similar to the Motif Text XmTextGetString() convenience function: XmHTMLTextGetString(). Both functions allocate enough space (using XtMalloc()) to contain all the text and return a pointer to this newly allocated area. While the pointer can be modified any way you like, you must free it using XtFree() when you are through using it. The code fragment below demonstrates how this function may be used:
	String text_p;

	if((text_p = XmHTMLTextGetString(html_w)) != NULL)
	{
		/*
		* Allocated memory returned.
		* do anything with "text_p" you want.
		*/
		XtFree(text_p);
		/* we *must* free "text" or there will be a memory leak */
	}
This convenience function returns a HTML document created from the XmHTML widget's internal representation of the original source document after HTML verification has taken place. Depending on the adherance to the HTML 3.2 standard of the original source document, the newly created HTML document may or may not differ from the original source document. If there is a difference, it will only be found in the markup elements; XmHTML widget's never modify text elements.

Getting the displayed text of a XmHTML widget is potentially an expensive operation if the widget contains large amounts of text. A XmHTML widget needs to create a HTML document from the internal representation every time XmHTMLTextGetString() is called.

2.1.3 XmHTML Widget's Dimensions

Let's return to Example 2-1 and examine more closely what is exposed to the user. As you can notice from the output of the program (shown in Figure 2-1), a vertical scrollbar is displayed and the text is not entirely visible: the user has to drag the vertical scrollbar to see all the text contained in the XmHTML widget. When no size has been specified, the width of a XmHTML widget defaults to 20 ex (a TEX measure for average character width) and the height of a single line in the default font.

You specify the size of a XmHTML widget by using the XmNwidth and XmNheight resources (which a XmHTML widget inherits from the Core widget), as shown in Example 2-2:

Example 2-2. The simple_html2.c program

/* simple_html2.c -- Create another minimally configured XmHTML widget */
#include <XmHTML.h>

main(int argc, char **argv)
{
	Widget toplevel;
	XtAppContext app;

	toplevel = XtVaAppInitialize(&app, "Demos", NULL, 0,
		&argc, argv, NULL, NULL);

	/* create a XmHTML widget but this time we specify a size */
	XtVaCreateManagedWidget("html", xmHTMLWidgetClass, toplevel,
		XmNvalue, "<html><body>Another minimally configured XmHTML "
			"widget.</body></html>",
		XmNwidth, 200,
		XmNheight, 75,
	NULL);

	XtRealizeWidget(toplevel);
	XtAppMainLoop(app);
}
The output of this program is shown in Figure 2-2.

[Another Simple HTML Widget]
Figure 2-2. simple_html2.c: another minimally configured XmHTML widget

The specified width and height are only used at initialization. Once the application is up and running, the user can resize windows and effectively change those dimensions.

2.1.3.1 Autosizing a XmHTML Widget

As you can see in Figure 2-2, the widget's text is now entirely visible but a vertical scrollbar is still present. We could remove this vertical scrollbar by obtaining a handle to it and then unmanaging it, but another and more subtle way to remove the vertical scrollbar is to use the autosizing feature of a XmHTML widget as shown in Example 2-3.

Example 2-3. The autosize_html.c program

/* autosize_html.c -- Demonstrate the autosizing feature of a XmHTML widget */
#include <XmHTML.h>

main(int argc, char **argv)
{
	Widget toplevel;
	XtAppContext app;

	toplevel = XtVaAppInitialize(&app, "Demos", NULL, 0,
		&argc, argv, NULL, NULL);

	/* make sure we may resize ourselves */
	XtVaSetValues(toplevel, XmNallowShellResize, True, NULL);

	/* create a XmHTML widget but this time enable autosizing */
	XtVaCreateManagedWidget("html", xmHTMLWidgetClass, toplevel,
		XmNvalue, "<html><body>An AutoSizing XmHTML widget.</body></html>",
		XmNresizeWidth, True,
		XmNresizeHeight, True,
	NULL);

	XtRealizeWidget(toplevel);
	XtAppMainLoop(app);
}
The output of this program is shown in Figure 2-3.

[An Autosizing HTML Widget]
Figure 2-3. autosize_html.c: an autosizing XmHTML widget

When looking at Example 2-3, we notice a few things that are of interest. First of all, there is the XmNallowShellResize resource. The value of this resource determines whether a Shell widget (or any of its subclasses, in this case a topLevelShellWidget) will honor geometry requests from its children. Since we want the XmHTML widget to compute its own size, we must set this resource to True (it is False by default).

The second and most interesting thing to notice are the XmNresizeWidth and XmNresizeHeight resources. These resources control the autosizing feature of XmHTML widgets. The first enables autosizing in horizontal direction and the latter in vertical direction. When the XmNresizeWidth resource is set to True, XmHTML widget's base their initial width on the width of the longest text element in the source document, with a maximum of 80 characters. XmHTML widget's with autosizing enabled will never cover the entire screen: the maximum dimensions a XmHTML widget take will never exceed 80% of the available screen dimensions.

In strong contrast with specifying the dimensions of XmHTML widget's using the XmNwidth and XmNheight resources, autosizing is persistent: When the user resizes a window, the dimensions of a XmHTML widget are not affected. Autosizing remains effective as long as the autosizing resources are set.

You should use the autosizing feature of XmHTML widget's sparingly. Dynamic resizing of widgets is generally considered as poor design. This is especially true of widgets in a Shell or Dialog with other elements in it.

An example of acceptable use of dynamic resizing would be when a XmHTML widget is used in a popup dialog used for displaying small amounts of text (such as explanation of terminology in a Hypertext Help system).

2.1.3.2 XmHTML Widget's Dimensions and Resources

As we will see in section 2.1.5, XmHTML Children, a XmHTML widget creates three subwidgets: a DrawingArea and two scrollbars.

When a XmHTML widget is created, the size of the DrawingArea is set explicitly by the widget itself, but it uses the Motif default values when it creates the scrollbars. It is important that you realize this when you set the dimensions of a XmHTML widget by means of application-wide fallback resources or when specifying them in a resource file: if you specify the dimensions of a XmHTML widget using resource wildcarding you can be sure to expect unwanted behaviour whenever text is set into the widget.

The correct way to specify the widget dimensions is as follows:

static String fallbackResources[] = {
	"*XmHTML.width:       575",
	"*XmHTML.height:      600",
};
This will only set the width of the DrawingArea subwidget to 575 pixels and the height to 600 pixels, leading to the expected behaviour.

If on the other hand you incorrectly specify the dimensions as follows:

static String fallbackResources[] = {
	"*XmHTML*width:       575",
	"*XmHTML*height:      600",
};
you are not only setting the dimensions of the DrawingArea subwidget but also the dimensions for every subwidget created by a XmHTML widget. Needless to say that this kind of behaviour is unwanted. The correct way to specify the dimensions of both a XmHTML widget and its ScrollBar subwidgets is as follows:
static String fallbackResources[] = {
	"*XmHTML.width:       575",
	"*XmHTML.height:      600",
	"*XmHTML*verticalScrollBar.width:       25",
	"*XmHTML*horizontalScrollBar.height:    25",
};

[Note: although it is possible to use resource wildcarding to set the dimensions of a XmHTML widget when the width and height of a ScrollBar are explicitly set, it is advisable that this is never done. The problem is not limited to the dimensions of the ScrollBars subwidgets but extends to any subwidgets a XmHTML widget creates when it encounters a HTML document with a <FORM></FORM> declaration in it.]

2.1.4 Document Scrolling

Adding document scrolling to XmHTML widget's is easy: you don't. If you look at the output of Example 2-1 (Figure 2-1), you will notice that a vertical scrollbar is present while we didn't instruct the XmHTML widget to add one. When the XmHTML widget was created it noticed that it wasn't high enough to display all of the text and thus it added a vertical scrollbar to allow the user to view all of the text by dragging the scrollbar. If you resize the window vertically, the vertical scrollbar will disappear when the widget is high enough to contain all text (including the horizontal and vertical margins).

The behaviour you have observed in Example 2-1 is the default behaviour for the XmHTML widget: when not all of the text can be fitted in the available dimensions, a horizontal and/or vertical scrollbar is added, and when all text can be fitted in the available dimensions, the horizontal and/or vertical scrollbar is removed.

As there might be cases where you want the presence of a vertical scrollbar while one is not required, you can force the presence of a vertical scrollbar by using the XmNscrollBarDisplayPolicy resource as shown in the following example:

Example 2-4. The forced_html.c program

/* forced_html.c -- Force the display of the vertical scrollbar */
#include <XmHTML.h>

main(int argc, char **argv)
{
	Widget toplevel;
	XtAppContext app;

	toplevel = XtVaAppInitialize(&app, "Demos", NULL, 0,
		&argc, argv, NULL, NULL);

	/* create a XmHTML widget but this time we specify a size */
	XtVaCreateManagedWidget("html", xmHTMLWidgetClass, toplevel,
		XmNvalue, "<html><body>A minimally configured "
			"XmHTML widget.</body><html>",
		XmNwidth, 200,
		XmNheight, 75,
		XmNscrollBarDisplayPolicy, XmSTATIC,
		XmNscrollBarPlacement, XmTOP_LEFT,
		XmNmarginWidth, 5,
		XmNmarginHeight, 5,
        NULL);

	XtRealizeWidget(toplevel);
	XtAppMainLoop(app);
}
The XmNmarginWidth and XmNmarginHeight resources determine the spacing between the outer borders of a XmHTML widget and the displayed text. For demonstration purposes they have been set to a smaller value than the default value of 20. If you compile and run this program you will see that a vertical scrollbar is present and stays present when the widget is resized, even when the widget is high enough to contain all text.

Example 2-4 also shows the XmNscrollBarPlacement resource. This resource controls the placement of both horizontal and vertical scrollbar. In this case the vertical scrollbar is placed on the left side of the window and the horizontal scrollbar is placed on top of the window (making the window smaller will show the horizontal scrollbar). Possible values for this resource are:

XmHTML widget's do not provide any way to force the display of a horizontal scrollbar, you can only control it's placement.

Keyboard Scrolling

XmHTML widget's implement a full keyboard navigation interface by installing a number of translations. On most keyboards, the arrows provide the same behaviour as dragging the scrollbars while the PageUp and PageDown keys scroll through a document a page at a time. Additional keys are also available, see the XmHTML Reference Manual for the complete set of translations that XmHTML widget's install.

2.1.5 XmHTML Children

A XmHTML widget is made up of a number of subwidgets: a DrawingArea widget in which the document is displayed, and two ScrollBars. You can get the handles to these children using the Xt XtVaGetValues() method, as demonstrated in the following code fragment:
	Widget work_window, hsb, vsb;

	XtVaGetValues(html_w,
		XmNworkWindow, &work_window,
		XmNhorizontalScrollBar, &hsb,
		XmNverticalScrollBar, &vsb,
		NULL);
Getting XmHTML widget children can be usefull when you want to install your own event handlers or set additional resources on these children.

Let us now demonstrate a case where it is usefull to get a handle to one of XmHTML's children: the workWindow. Amongst the many convenience routines offered by the XmHTML widget's is the XmHTMLXYToInfo() function. This function takes a XmHTML widget and a pointer position as it's arguments and returns a structure containing information about the contents of the currently displayed document at the requested pointer position. The structure returned is of type XmHTMLInfoStructure, which is defined as follows:

typedef struct
{
	int line_no;		/* line number at selected position */
	Boolean is_map;		/* true when clicked image is an imagemap */
	int x,y;		/* position relative to image corner */
	XmImageInfo *image;	/* image data */
	XmHTMLAnchorPtr anchor;	/* possible anchor data */
}XmHTMLInfoStructure, *XmHTMLInfoPtr;
The XmImageInfo and XmHTMLAnchorPtr members of this structure will be explained in the following sections, so we will now only say that the first structure contains information about an image over which the pointer is located, while the latter contains information about a hyperlink over which the anchor is currently located.

A perfect way to display this information to the user would be to display a popup menu when the user clicks on the currently displayed document. In order for this to work, we need to attach an event handler to the workWindow of a XmHTML widget. The following example demonstrates how to attach the event handler to the workWindow and how to use the XmHTMLXYToInfo() function:

Example 2-5. The work_window.c program

/*
* work_window.c: attaching an event handler to the work_window of a XmHTML
* widget.
*/
#include <XmHTML.h>

void
attachInfoHandler(Widget html, Widget popup)
{
	Widget work_window;

	XtVaGetValues(html, XmNworkWindow, &work_window, NULL);

	/*
	* Add an event handler which responds to mouse clicks. "popup" is the
	* popup menu which is to be displayed, it is stored as client_data
	* for the event handler.
	*/
	XtAddEventHandler(work_window, ButtonPressMark, 0,
		(XtEventHandler)infoHandler, popup);
}

void
infoHandler(Widget work_window, Widget popup, XButtonPressedEvent *event)
{
	XmHTMLInfoPtr info;
	Widget html_w;
	WidgetList children;

	/* we only take events generated by button 3 */
	if(event->button != 3)
		return;

	/*
	* The work_window is a child of a XmHTML widget, so we can get a handle
	* to the XmHTML widget itself by using Xt's XtParent routine.
	*/
	html_w = XtParent(work_window);

	/* get the info for the selected position.  */
	info = XmHTMLXYToInfo(html_w, event->x, event->y);

	/*
	* Check the returned info structure. There will be nothing to display
	* if the pointer wasn't over an image or anchor when the user 
	* clicked his mouse.
	*/
	if(info == NULL || (info->image == NULL && info->anchor == NULL))
		return;

	/*
	* For this example we assume that the popup menu has two buttons:
	* a hyperlink button and an image button. We retrieve these children
	* of the popup menu using the XmNchildren resource of Motif's
	* rowColumn widget.
	*/
	XtVaGetValues(popup, XmNchildren, &children, NULL);

	/* check if the info structure has an anchor */
	if(info->anchor)
	{
		XmString label;
		label = XmStringCreateLocalized(info->anchor->href);
		XtVaSetValues(children[0], XmNlabelString, label, NULL);
		XmStringFree(label);
		XtManageChild(children[0]);
	}
	else
		XtUnmanageChild(children[0]);

	/* check if the info structure has an image */
	if(info->image)
	{
		XmString label;
		label = XmStringCreateLocalized(info->image->url);
		XtVaSetValues(children[1], XmNlabelString, label, NULL);
		XmStringFree(label);
		XtManageChild(children[1]);
	}
	else
		XtUnmanageChild(children[1]);

	/* the "popup" menu has now been configured, pop it up */
	XmMenuPosition(popup, event);
	XtManageChild(popup);
}
Note that the above example does not use any global variables; we can obtain all handles to the widgets we need. The handle to the popup menu is stored as client_data for the event handler, while the handles to the menu buttons of the popup menu can be easily obtained using one of Motif's RowColumn widget resources. It is also important to notice that it is very easy to obtain the widget id of the XmHTML widget. The event handler was attached to the workWindow of a XmHTML widget, and therefore this workWindow is the same widget as the work_window argument to the infoHandler routine. This is the reason why the XtParent(work_window) call returns the handle of the XmHTML widget.

Also notice that we do not have to free any of the members of the returned XmHTMLInfoStructure structure. In fact, we are not permitted to do this since the image and anchor members of this structure contain pointers to data owned by a XmHTML widget. Unless it is explicitly mentioned, all structures returned by any of XmHTML widget's resources, callback resources or convenience routines should be considered as read only.

Example 2-5 is a simple demonstration which does not use any callbacks on the popup menu buttons. In a real implementation however, callback resources could be attached to each of the popup menu buttons which would allow the user to jump to the selected hyperlink or display information about the selected image.

[Note: if you want to specify resource values for the subwidgets mentioned in this chapter by means of application fallback resources or in a resource file, the following names should be used: workWindow for the DrawingArea child, verticalScrollBar for the vertical ScrollBar child and horizontalScrollBar for the horizontal ScrollBar child.]

2.2 Modifying the Document Appearance

A HTML document typically contains a number of hyperlinks. Hyperlinks (also referred to as anchors) are often used in HTML documents to allow the reader to navigate the currently displayed document or between different documents. In order for the reader to recognize these anchors they should appear different from the main content of a HTML document. XmHTML widget's provide a number of resources that control the appearance of these anchors.

Another set of resources allow you to control the fonts that are used to render the displayed text.

2.2.1 Changing the Anchor Appearance

XmHTML widget's identify three different types of anchors: a regular anchor, a visited anchor and an anchor which has the target attribute set. The latter is a special type of anchor and is one of the extensions to the HTML 3.2 standard that are implemented by XmHTML. Typical use of this attribute can be found in HTML documents that utilize the HTML <FRAMESET> extension, where this attribute indicates a destination to which the contents of the activated anchor should be sent. This destination can target (amongst others) another XmHTML widget or a new dialog. Other use of this attribute can be found in Hypertext help systems where it might be used to indicate that the contents of the activated anchor should be displayed in a popup window.

A visited anchor is an anchor that the reader of the document has selected in the past.

Each anchor has three states: passive, armed and activated. An anchor is considered armed when a user has located the mouse pointer over an anchor. It is active when the user presses the first or second mouse button on an armed anchor. It is considered passive otherwise.

XmHTML widget's provide four ways to let an anchor stand out:

The first and second item in this list are always selectable, while the third and fourth item are mutually exclusive: you choose either to display the anchors as pushbuttons or to underline them. The latter offers the most control on anchor appearance, while some users may find pushbutton anchors aesthetically more pleasing (although it can be slightly more difficult to identify anchors). Both types allow you to control the color in which the anchor is displayed in both it's passive and activated state.

XmHTML widget's will display anchors as pushbuttons by default: the default value of the XmNanchorButtons resource is True. By setting this resource to False, anchors will be underlined instead of being displayed as a pushbutton.

2.2.1.1 Anchor Cursor Resources

To indicate that an anchor can be activated by the user, it is often convenient to not only render the anchor differently than the remainder of the document but also to change the cursor when the pointer is moved over an anchor.

XmHTML widget's will change the cursor to a hand with a pointing finger when the user moves the pointer over an anchor. The default cursor is a built-in cursor, but it can be changed to a different cursor by using the XmNanchorCursor resource. Although it will seldomly be needed, the XmNanchorDisplayCursor resource allows you to control whether or not XmHTML widget's should change the cursor when the pointer is moved over an anchor (it is True by default).

The following code fragment demonstrates how to create, set and destroy a cursor:

#include <X11/cursorfont.h>	/* standard X cursors */
#include <XmHTML.h>

void
setCursor(Widget html_w, Boolean destroy_cursor)
{
	static Cursor cursor;

	/* create a standard X cursor when it is not yet created */
	if(cursor == None)
		cursor = XCreateFontCursor(XtDisplay(html_w), XC_hand2);

	if(destroy_cursor)
	{
		/* only free the cursor when we have created one */
		if(cursor != None)
			XFreeCursor(XtDisplay(html_w), cursor);
		cursor = None;
	}
	else	/* set this cursor as the anchor cursor for this XmHTML widget */
		XtVaSetValues(html_w,
			XmNanchorCursor, cursor,
			XmNanchorDisplayCursor, True,
			NULL);
}
The cursor is created using XCreateFontCursor(), but this is not required; you may use other functions like XCreateGlyphCursor() or XCreatePixmapCursor(), if you like. See O'Reilly's X Window System Programming guides Volume One, Xlib Programmer's Manual, for more information.

It is also considered nice programming to use X resources sparingly (although in the case of cursor's it is not really required, they are a virtually unlimited resource under X). This is why the above fragment declares the allocated cursor as a static variable: once allocated the cursor id is always available and we can use it for every XmHTML widget that is provided as an argument to the above routine. When this routine is called with the destroy_cursor argument set to True the cursor is destroyed and reset.

2.2.1.2 Anchor Display Resources

Whether or not you select to display anchors as pushbuttons or to underline them, you can always specify the foreground color in which an anchor is rendered. You can specify a different foreground color for the passive state of each of the anchor types mentioned in the introduction to this section:

The XmNanchorActivatedForeground resource identifies the foreground color to be used when an anchor is activated. This resource is the same for all three anchor types.

When you opt to underline the anchors, you can also use the XmNanchorActivatedBackground resource to specify the background color to use when an anchor is activated.

[Note: When a HTML document contains a background image and displaying of background images is enabled (which is true by default), the XmNanchorActivatedBackground resource is also ignored when anchors are to be underlined, making the anchors appear as being transparent.]

You need to specify a Pixel for all three resources. You can specify a Pixel value by pre-allocating a pixel (by using any of Xlib's XAllocColor() routines) or by using Xt's XtVaTypedArg method. Both methods have their pro's and con's, but in either case you must free the allocated color values when your application exits.

When you have set the XmNanchorButtons resource to False, you can also specify the type of underlining for each of the three anchor types. XmHTML widget's support the following types of underlining:

The resource names are as follows:

Finally, there is the XmNhighlightOnEnter resource. When set to True (which is the default), a XmHTML widget will actively modify the look of an anchor whenever it becomes armed by highlighting the anchor. The actual color and type of highlighting used heavily depends on the content of the document that is currently displayed. When a background image is present, a XmHTML widget will base the highlight color on the value of the XmNanchorActivatedForeground resource and render the anchor's text in the computed color. When no background image is present, the highlight color will be based on the current document background and the anchor's text will be rendered in a rectangle filled with the computed color. If you decide to enable this resource, it is adviced that you offer a possibility to the user to toggle the value of this resource: a XmHTML widget attempts to do it's best when computing the highlight color, but might fail to find a suitable color. This can happen, for instance, on documents where the colors used by the background (fixed color or background image), text and anchors are poorly balanced.

2.2.2 Marking a Visited Anchor

You now know that XmHTML widget's contain the concept of visited anchors, which are anchors that the reader of the document has selected in the past (e.g, have already been visited). But when the user of your application is switching between documents the following question arises: how do you inform a XmHTML widget that an anchor was previously visited by the user?

The answer is fairly simple: you use the XmNanchorVisitedProc resource. When a procedure has been installed for this resource, a XmHTML widget will call this procedure to test if an anchor has already been visited. There are two arguments to this procedure: the widget id of a XmHTML widget and the name of the anchor. It should return True when the anchor should be rendered using the XmNanchorVisitedForeground resource and False when it should be rendered as a regular anchor.

Please note that a XmHTML widget only calls any installed procedure when a document is set into a XmHTML widget, and that it will be called for every anchor that is found in this document.

The following example shows how easy it can be to use this resource:

#include <XmHTML.h>

static int num_visited_anchors;
String visited_anchors[100];

static Boolean
visitedTestProc(Widget html_w, String anchor)
{
	int i;

	for(i = 0; i < num_visited_anchors; i++)
		if(!strcasecmp(visited_anchors[i], anchor))
			return(True);
	return(False);
}

int
main(int argc, char **argv)
{
	Widget toplevel;
	XtAppContext app;

	toplevel = XtVaAppInitialize(&app, "Demos", NULL, 0,
		&argc, argv, NULL, NULL);

	XtVaCreateManagedWidget("html", xmHTMLWidgetClass, toplevel,
		XmNanchorVisitedProc, visitedTestProc,
		NULL);

	XtRealizeWidget(toplevel);
	XtAppMainLoop(app);
}
In this example we assume that text can be set in the XmHTML widget, visited_anchors contains a list of already visited anchors and that num_visited_anchors contains the number of items in the visited anchor list. In section 2.5 we will demonstrate how to maintain this list.

2.2.3 Specifying the Document Fonts

One of the most important (if not the most important) aspect that will determine the way a document will look to the user concerns the fonts used to render the text in a document. Selecting an appropriate default font (and its size) is a very important decision when using a XmHTML widget, and it is more difficult than it might seem, especially if you want to internationalize your application.

Font specification for a XmHTML widget does not follow the standard Motif font specification rules (the XmFontList type). Due to the nature of the HTML language this would be rather impossible; it does not only contain font markup elements that allow the writer of a HTML document to change the style of a font, but also to change the typeface and size of a font. To make things even more complicated, all of these font markup elements can be used in arbitrary sequence.

In order to cope efficiently with this virtually unlimited number of font combinations in HTML documents, XmHTML widget's allocate their fonts directly and employ a smart font caching scheme that will keep the number of required font allocations to an absolute minimum. The font cache is shared by all XmHTML widget's on a per display basis (which effectively means that you can use a XmHTML widget for a multi-display application).

Another reason why XmHTML widget's do not use the Motif font specification rules is that the XmHTML font allocation routines go thru great lengths to ensure that a requested font is matched. When a requested font can not be exactly matched, the font allocation routines attempt to find a close match by following a number of different paths instead of giving up immediatly and using a default font. XmHTML widget's produce much better looking text than some of the most well known HTML browsers as a result of this.

Because XmHTML widget's allocate their fonts themselves instead of relying on Motif, a number of resources are offered that allow you, the programmer, to select a character set, foundry, typeface, spacing and size of the fonts that are to be used for displaying a document.

[Note: In order to fully appreciate the font resources of XmHTML widget's you are encouraged to read Appendix A of O'Reilly's X Window System Programming guides, Volume One, Xlib Programmer's Manual.]

Before explaining the various font resources I will mention that a XmHTML widget uses the contents of the font resources to compose a X Logical Font Description (XLFD) which is used to query your X server. Knowing this, it is obvious that you are allowed to use wildcards for all font resources (except size of course). This can be potentially dangerous: depending on the configuration of your font path and directories, the font returned by the X server may or may not be what is intended.

As a final note in this fairly long introduction, the default values for each of the various font resources have been choosen in such a way that every combination represents a font that is present in the default font path of the standard R5 X distribution.

2.2.3.1 Specifying the Character Set

The character set resource, XmNcharset, is a string representing the ISO character set encoding to be used. The default character set used by XmHTML widget's, ISO8859-1, represents the ISO Latin-1 character set (which is used by all of the fonts in the 75dpi and 100dpi directories of X's default font path). This character set is a superset of the standard ASCII character set, which includes various special characters used in European languages other than English.

The XmNcharset resource makes it relatively easy to select a font for non-European (non-English) languages. For example, "XmNcharset: koi8-*" specifies the koi8 Cyrillic font, while "XmNcharset: UJIS-*" identifies a Japanese font.

It is also important to notice that this resource is very important, if not the most important font resource. If a charset is specified which is unknown to the X server (or the charset is given as a wildcard), a XmHTML widget has almost no control about the fonts to use: the actual charset that is used will then only depend on the fontpath configuration of the X server and the specified font family. Therefore it is considered wise to first verify if the requested character set is available on the system your application is running on before changing it. See also section 2.2.3.2, Specifying the Font Family.

The font allocation scheme used by XmHTML will first try to load a font for a given character set and font family. When that fails it will wildcard the charset. This causes a XmHTML Widget to honor the following sequence of HTML tags with the default charset:

  <FONT FACE="symbol">alpha</FONT>

which is rendered as:

  alpha

[Note: this will render the word alpha in the symbol font if it is installed on your X server, and in english if you are viewing this document with almost any other browser or haven't got the symbol font installed.]

2.2.3.2 Specifying the Font Family

XmHTML widget's use two different types of font: a proportional font and a fixed-width font. Proportional fonts are fonts in which each character in a character set has a different width (an example of a proportional font is Times Roman), while fonts in which each character has the same width are called fixed-width fonts. Proportional fonts are more easy for the human eye to read than fixed-width fonts and therefore the main portion of the text in a HTML document is rendered in a proportional font. Fixed-width fonts are used to display preformatted text in a HTML document.

You specify the proportional font family by using the XmNfontFamily resource, and the fixed-width font family by using the XmNfontFamilyFixed resource. The value for both these resources is a sequence of four dash-seperated strings that define a font family. These four fields are:

  1. foundry: the type foundry that digitized and supplied the font, i.e. Adobe;
  2. family: the font family name (also known as typeface), i.e. roman;
  3. set width: a value describing a font's proportionate width according to the selected foundry, i.e. normal;
  4. spacing: type of character spacing for a font. m (for monospace, i.e. fixed width) or p (proportional, i.e., variable-width) are two well known font spacings.

The default value for the XmNfontFamily resource is adobe-times-normal-*, and the default value for the XmNfontFamilyFixed resource is adobe-courier-normal-*. Both font families represent a standard, scalable font, present on each X server.

You should always try to specify a scalable font whenever possible: HTML documents intend to use a lot of differently sized fonts (the header markup elements for example all require a different font size), and the displayed text will generally look much better if a scalable font is used instead of a bitmapped font.

A note of caution: before changing any of the fontFamily resources, the existence of this font (within the context of the current or new value of the XmNcharset resource) should be verified before actually changing it. An unsuccesfull attempt to change the default font settings can cause a XmHTML widget to exit your application: if it can not find a default font it simply can not display anything.

2.2.3.3 Specifying the Font Sizes

The last step in specifying the document fonts is to specify the font sizes for both the proportional and fixed-width font families. Font sizes are always specified in points and not in pixels: specifying a font size in pixels would require you to know in advance the x- and y-resolution of the screen your application is running on. The XmHTML widget determines the screen resolution and selects an appropriate pixel size automatically.

You can specify the text font size, the sup- and superscript font size and the font size for each of the header markup elements for the proportional font family. For the fixed-width font family you can specify the text font size and sup- and superscript size.

The sizes for the proportional font family are given by the XmNfontSizeList resource. The value for this resource is a comma seperated list of eight strings. The list below describes the fields in this resource.

  1. normal text size;
  2. sub and superscript font size;
  3. font size for the <H1> markup element;
  4. font size for the <H2> markup element;
  5. font size for the <H3> markup element;
  6. font size for the <H4> markup element;
  7. font size for the <H5> markup element;
  8. font size for the <H6> markup element;

The default value for this resource is "14,8,24,18,14,12,10,8".

You do not have to specify all values; when an element in this list has the value 0 (zero), the appropriate default font size is used.

The sizes for the fixed-width font family are given by the XmNfontSizeFixedList resource. The value for this resource is a comma seperated list of two strings, where the first value describes the normal text size and the second value the size for sup- and superscript text. The default value for this resource is "12,8".

2.3 XmHTML and Images

One of the strong and much admired features of the XmHTML Widget is it's built in image support. A XmHTML widget can support a wide range of different image types (from simple X11 bitmaps through complex GIF animations to full 32bit RGB PNG images with an alpha channel). For simple applications one does not need to know anything about this image support as a XmHTML widget can handle almost everything by itself. In fact, you only need to read this section if you want to exploit XmHTML's image support to it's fullest.

2.3.1 The XmImageInfo Structure

The basis of XmHTML's image API is formed by the XmImageInfo structure. XmHTML uses this structure to compose the actual image.
typedef struct _XmImageInfo
{
    /* regular image fields */
    String url;                    /* original location of image */
    unsigned char *data;           /* raw image data. ZPixmap format */
    unsigned char *clip;           /* raw clipmask data. XYBitmap format */
    Dimension width;               /* used image width, in pixels */
    Dimension height;              /* used image height, in pixels */
    int *reds;                     /* red image pixels */
    int *greens;                   /* green image pixels */
    int *blues;                    /* blue image pixels */
    unsigned int ncolors;          /* Number of colors in the image */
    int bg;                        /* transparent pixel index */
    unsigned char *rgba;           /* image data in rgba format */
    float fg_gamma;                /* image gamma */
    unsigned int options;          /* image option bits */

    /* Additional animation data */
    int x;                         /* logical screen x-position for a frame */
    int y;                         /* logical screen y-position for a frame */
    int loop_count;                /* animation loop count */
    unsigned char dispose;         /* image disposal method */
    int timeout;                   /* frame refreshment in milliseconds */
    int nframes;                   /* no of animation frames remaining */
    struct _XmImageInfo *frame;    /* ptr to next animation frame */

    /* image classification fields and original data */
    unsigned char type;            /* image type, see below */
    int depth;                     /* bits per pixel for this image */
    unsigned char colorspace;      /* colorspace for this image */
    unsigned char transparency;    /* transparency type for this image */
    Dimension swidth;              /* image width as read from image */
    Dimension sheight;             /* image height as read from image */
    unsigned int scolors;          /* Original number of colors in the image */

    XtPointer user_data;           /* any data to be stored with this image */
}XmImageInfo, *XmImageInfoStruct;
The first part of this structure contains the raw image data XmHTML requires to create an image. The second part is used for creating animations while the third part provides additional information about the image represented by this structure.

As will be shown in the following sections of this chapter, all of the convenience functions of a XmHTML widget that deal with images work with this structure, either as an argument or as a return value.

2.3.1.1 The XmImageInfo Structure Explained

Although this section is mostly intended for those that want to add support for an image type that is unknown to XmHTML, it also contains important information for those that want to enable what is known as delayed image loading or want to exploit XmHTML's built-in image support to its fullest extent. It explains every field in the XmImageInfo structure, what each field should contain and the default values assumed when an image is loaded using the XmHTMLImageDefaultProc.

url
This field represents the original location from where the image in question should be or has been obtained. It is actually the contents of the SRC attribute of the <IMG> tag. It is important that this field always contains a value, especially if you want to switch on delayed image loading.

data
This field represents the actual image data in ZPixmap format. It is a continuous stream of bytes in which each byte represents an index in the colormap for this image (as defined by the reds, greens and blues fields).

clip
This field represents a bitmap (a pixmap with depth 1) which a XmHTML widget will use to create a clipmask. It is a continuous stream of bits, where each bit defines if the underlying pixel should be rendered (bit set to 1) or should be left as it is (bit set to 0). It is only valid for transparent images, although it is very well possible to use this field for defining non-rectangular pixmaps. The dimensions of this bitmap should equal the actual image dimensions.

width and height
These fields contain the absolute dimensions of the final pixmap as given by the data field. They are specified in pixel units, and their product must match the size of the data field.

reds, greens, blues and ncolors
Each of these fields is an array of color component values which together form the RGB colormap of the image. Each array must have the same size, and this size is given by the value of ncolors. This number must be equal the the number of colors used by the image. A XmHTML widget uses these arrays to allocate the actual image colors and then maps the image data onto these colors.

bg
The index of the transparent pixel, or -1 when the image is fully opaque.

rgba
This field is only used for images containing an alpha channel. An alpha channel is a series of pixel values in an image used to compute partial pixel transparency. With partial pixel transparency fade in/out or anti-aliasing effects can be achieved regardless of the current background setting. The XmHTMLImageDefaultProc supports alpha channelled PNG images. The format of the data stored in this field is a continuous stream of bytes where each pixel is represented by its red, green, blue and alpha channel component.

fg_gamma
This field contains the gamma value of the display on which the image was created. It is only used in combination with alpha channelled images.

options
This field contains a combination of OR'd constants which tell a XmHTML Widget what it can and can't do with an XmImageInfo structure. A number of these constants define how and when a XmHTML should free a structure (or members of it), while others determine what type of operations are allowed and how the actual image should be processed.

"Set by default" indicates a bit set when the XmHTMLImageDefaultProc was used to read an image.

XmIMAGE_DELAYED
When set, XmHTML will ignore all fields except url and use a default image instead. This default image can be updated at a later stage using either the XmHTMLImageReplace or XmHTMLImageUpdate convenience function; See the section on Delayed Image Loading for more information.

XmIMAGE_DEFERRED_FREE
When set, XmHTML will take care of destroying this structure when a new document is loaded. Set by default;

XmIMAGE_IMMEDIATE_FREE
When set, this structure will be destroyed as soon as XmHTML no longer needs it;

XmIMAGE_RGB_SINGLE
set this bit when the reds, greens and blues fields have been allocated with one single malloc instead of three separate. Set by default;

XmIMAGE_ALLOW_SCALE
set this bit when scaling an image is allowed. Set by default;

XmIMAGE_FRAME_IGNORE
use with animations: set this bit when a frame falls outside the logical screen area. No pixmap is created but the timeout for the frame is kept.

XmIMAGE_CLIPMASK
This bit is set when the returned XmImageInfo structure contains clipmask data. XmHTML uses this info to create a clipping bitmap. Changing this bit from set to unset will lead to a memory leak while changing it from unset to set without providing a clipmask yourself will cause an error to happen. You can however set this bit when you are providing your own clipmask (to provide non-rectangular images for example), provided you fill the "clip" field with valid bitmap data.

XmIMAGE_SHARED_DATA
This bit is set when images share data. XmHTML sets this bit when the image in question is an internal image, e.i., one for which the image data may never be freed. Be carefull setting this bit yourself, since it prevents XmHTML from freeing the image data present in the XmImageInfo structure. It can easily lead to memory leaks when an image is not an internal image.

XmIMAGE_PROGRESSIVE
Setting this bit will enable progressive image loading. A function must have been installed on the XmNprogressiveReadProc resource prior to setting this bit. Installing a function on the XmNprogressiveEndProc is optional but strongly advised. When this bit is set all other bits and all members except the url field of the XmImageInfo structure will be ignored.

XmIMAGE_DELAYED_CREATION
This bit is read-only. It is used internally by XmHTML for images with an alpha channel. Alpha channel processing merges the current background with the original RGBA data (see above) from the image and uses the result to compose the actual on-screen image (the merged data is stored in the "data" field of the XmImageInfo structure). XmHTML needs to store the original data somewhere, and when this bit is set it is stored in the "rgba" field of the XmImageInfo structure. When this bit is set, the returned structure may not be freed as long as the current document is alive. You can discard it as soon as a new document is loaded.

type
This field contains a value which describes the type of image that is represented by this structure. This field is unused internally and is provided for your convenience only. You should consider this value read-only though as it might well be used in the future. Table 2-1 lists all possible values for this field.

Table 2-1. Supported Image Types
Type Image description
IMAGE_ERROR error on image loading
IMAGE_UNKNOWN unknown image
IMAGE_XPM X11 pixmap
IMAGE_XBM X11 bitmap
IMAGE_GIF CompuServe(C) Gif87a or Gif89a
IMAGE_GIFANIM animated gif
IMAGE_GIFANIMLOOP animated gif with NETSCAPE2.0 loop extension
IMAGE_GZF compatible Gif87a or Gif89a
IMAGE_GZFANIM compatible animated gif
IMAGE_GZFANIMLOOP compatible animated gif with NETSCAPE2.0 loop extension
IMAGE_JPEG JPEG image
IMAGE_PNG PNG image

2.3.2 The XmHTMLDefaultImageProc

2.3.3 Animations

2.3.4 XmHTML's Imagemap Support

2.4 XmHTML Widget Callback Functions

2.4.1 Anchor Activation Callback

2.4.2 Anchor Tracking Callback

2.4.3 Frame Notification Callback

2.4.4 Form Activation Callback

2.4.5 Imagemap Activation Callback

2.4.6 Document Verification Callback

2.4.7 Document Link Callback

2.4.8 Document Motion Callback

2.4.9 Document Arm Callback

2.4.10 Document Input Callback

2.5 Collective Example

2.6 Advanced XmHTML Programming Techniques

2.6.1 Adding Support for HTML Frames

2.6.2 Delayed Image Loading

When a document is loaded into a XmHTML widget, it might not always be possible to provide an image when the XmHTML widget encounters one in the document: a net connection might be slow or the image requested can be that large that loading it can take quite a long time. It can be quite annoying if XmHTML should block until the image is available. Therefore, XmHTML offers Delayed Image Loading. Delayed Image Loading fools XmHTML into thinking that it has the image it wants, so it will happily continue without a noticable slowdown, and display the document once it has been parsed and formatted.

Now, how does it work? Basically it is quite simple: when a XmHTML widget finds a <IMG> tag, it will call the function that has been installed for the XmNimageProc resource function (say, imageProc).

The XmNimageProc function should then initialise a blank XmImageInfo structure, fill in the url field and set the XmIMAGE_DELAYED bit (this last step is imperative). It should then call a function which requests an image from a remote server and return the new XmImageInfo to XmHTML. The image is retrieved asynchronously.

At some point in the future, the image has been retrieved by the network layer, and the application calls a routine (say replaceImage) which will replace the blank image in the XmImageInfo structure with the newly loaded one.

XmHTML widget's provide two functions for replacing or updating a delayed image: XmHTMLImageReplace and XmHTMLImageUpdate. The difference between these two functions is that the former will actually replace the given XmImageInfo structure with a new one while the latter will only update the given image.

The above will probably make more sense in an example, so here is one.

Example 2-?: Delayed Image Loading

#include <XmHTML.h>

XmImageInfo *imageProc(Widget w, String url)
{
	String filename;
	static XmImageInfo *image;

	image = NULL;

	/* test delayed image loading */
	if((filename = resolveFile(url)) != NULL)
	{
		image = XmHTMLImageDefaultProc(html, filename, NULL, 0);
		image_cache[current_image].image = image;
		image_cache[current_image++].name = strdup(filename);
	}
	else
	{
		image = (XmImageInfo*)malloc(sizeof(XmImageInfo));
		memset(image, 0, sizeof(XmImageInfo));
		image->options = XmIMAGE_DELAYED; /* Tell XmHTML this image is delayed */
		image->url = strdup(url);
		/* associate the new XmImageInfo with this url */
		image_cache[current_image].image = image;
		image_cache[current_image++].name = strdup(url);
		fetchFileFromServer(url);
	}
	return(image);
}
In the above example, it is first tested if the requested image is already available. When it is, XmHTMLImageDefaultProc is called to load the image, and it's return value is stored in an image cache.

When the requested image is not available on the other hand, a blank XmImageInfo structure is initialized and stored, the delayed bit is set and a function is called which will fetch the image asynchronously from a server. The blank XmImageInfo structure is then returned and XmHTML will continue where it left off. When the image has been received, the application calls the replaceImage function.

Example 2-?: Updating delayed images

#include <XmHTML.h>

void replaceImage(String url, unsigned char *buf, int len)
{
	int i;
	XmImageInfo *image;

	for(i = 0; i < image_cache_size; i++)
	{
		if(!(strcmp(image_cache[i].name, url)))
		{
			image = XmHTMLImageDefaultProc(html, url, buf, len);
			XmHTMLImageReplace(html, image_cache[i].image, image);
			image_cache[i].image = image;
		}
	}
}
As already explained in this section, the replaceImage function is called when an image has been retrieved from a server. Contents of the image from location url have been placed in the buffer buf with size len.

Remember that we have already associated a XmImageInfo structure with a url in the imageProc function, so the first thing we do is get the correct XmImageInfo. When we have found the correct entry, we simply call XmHTMLImageDefaultProc to actually load the image, replace the image represented by our old XmImageInfo structure and update the image cache.

In a real world implementation the previous XmImageInfo structure would also be freed. It would also call the XmHTMLRedisplay function after all images have been loaded to force XmHTML to do a recalculation of it's screen layout.

2.6.3 Adding Support for Other Imagetypes

2.6.4 Using XmHTML's Image Support for Other Purposes

One of the many nice features in a XmHTML widget is the built-in image support. What is even nicer is that this image support is also available for purposes other than use with a XmHTML widget: the XmHTML widget set contains a number of routines that allow you to read an image of any of the supported image types. You can then use the returned XmImage to your own likings. This section explains the XmImage image datatype and describes how to create, destroy and use such images.

2.6.4.1 The XmImage Structure

The basis of the external image support of the XmHTML widget set is formed by the XmImage structure. This structure is defined as follows:
typedef struct{
	/* regular image data */
	String file;		/* originating file */
	unsigned char type;	/* image type */
	Pixmap pixmap;		/* actual image	*/
	Pixmap clip;		/* for transparant pixmaps */
	int width;		/* image width */
	int height;		/* image height */	

	/* animation data */
	XmImageFrame *frames;	/* array of animation frames */
	int nframes;		/* no of frames following */
	int current_frame;	/* current frame count */
	int current_loop;	/* current loop count */
	int loop_count;		/* maximum loop count */
	XtIntervalId proc_id;	/* timer id for animations */
	Widget w;		/* image owner */
	XtAppContext context;	/* Application context for animations */

	/* Private data */
	unsigned long *pixels;
	int npixels;
  	struct _XColorContext *xcc;
}XmImage;
The pixmap field contains the actual image. It is this pixmap that you should use in a call to XCopyArea to put it directly in a window or in combination with Motif's XmNlabelPixmap resource to put the pixmap in a label widget.

If the value of the clip field is other than None, it means the original image is a transparent image. You can then use this field as a clipmask of a GC so your image will also be transparent.

[Note: the clipmask is a pixmap of depth 1, so it is actually a X11 bitmap.]

One important reminder before explaining how to create and use a XmImage: you should never touch any of the fields in the XmImage structure which are mentioned as private data. This private data is used when the XmImage is destroyed, so modifying any of these fields is a potential danger and leads at least to a memory leak. If you really want to know what these fields represent, here's a very short explanation of what they represent:

2.6.4.2 Creating a XmImage

You can create an XmImage by using the XmImageCreate convenience function. This function is defined as follows:
XmImage *XmImageCreate(Widget w, String file, Dimension width,
	Dimension height);
w must contain the id of a Widget (and not a Gadget) which will be the owner of this image, file is the name of a file representing the image that is to be loaded. The width and height arguments allow you to scale the image. If both have the value 0 (zero), the dimensions of the loaded image will be the dimensions as found in the image. Otherwise, XmImageCreate will scale to image to fit the given dimensions. Scaling is only done in the directions for which a value is given: if you only want to scale the image horizontally, you should only specify a value for width and set height to zero (or a value for height and 0 for width to only scale the image vertically).

Although the external image support is independent of a XmHTML widget, you can also use the XmImageInfo structure to create a XmImage. An example where this can be usefull is in conjuction with the XmHTMLXYToInfo() convenience function. As you may know by now, the return value of this function can contain a pointer to a XmImageInfo structure. Supposing this is the case and you would want to display a dialog box to the user with a thumbnail of the image, it is much more convenient to create a XmImage directly from the XmImageInfo structure instead of creating it from a file.

The syntax of the XmImageCreateFromInfo() convenience function is as follows:

XmImage *XmImageCreateFromInfo(Widget w, XmImageInfo *info, Dimension width,
	Dimension height);
The only difference with the XmImageCreate() function is that instead of a filename you now supply a XmImageInfo structure.

2.6.4.3 Destroying a XmImage

When you are done using a XmImage, you should destroy it using the XmImageDestroy convenience function. The syntax to this function is as follows:
void XmImageDestroy(XmImage *image);
This function will take care of destroying all pixmaps, possible animation data and will free the colors allocated to this image. When this function returns the given XmImage will no longer be valid.

One thing that this function will not do is release any XtIntervalId that might be present in the given XmImage structure. It is your responsibility to do this.

2.6.4.4 How to use a XmImage

Normal Images

Running Animations

For the sake of clarity, we will repeat the animation data of the XmImage structure here:
	/* animation data */
	XmImageFrame *frames;	/* array of animation frames */
	int nframes;		/* no of frames following */
	int current_frame;	/* current frame count */
	int current_loop;	/* current loop count */
	int loop_count;		/* maximum loop count */
	XtIntervalId proc_id;	/* timer id for animations */
	Widget w;		/* image owner */
	XtAppContext context;	/* Application context for animations */
The *frames field points to an array of structures of the following type:
typedef struct
{
	int x;			/* x position in logical screen */
	int y;			/* y position in logical screen */
	int w;			/* width of this particular frame */
	int h;			/* height of this particular frame */
	int timeout;		/* timeout for the next frame */
	unsigned char dispose;	/* previous frame disposal method */
	Pixmap pixmap;		/* actual image */
	Pixmap clip;		/* image clipmask */
	Pixmap prev_state;	/* previous screen state */

	/* private data */
	unsigned long *pixels;
	int npixels;
}XmImageFrame;
The total number of frames is given by the nframes field in the XmImage structure. Each member of this array of structures represents a single frame in an animation, so displaying all frames in sequence will result in an animation being shown to the user.

Before embarking on how to display animations, it is necessary that you understand the what the fields of the XmImageFrame structure represent.

x, y, w and h

The first frame in an animation determines the screen size of the animation. This is what is called the logical screen. Every following frame can have a size equal to or less than the logical screen size. The x and y fields of the XmImageFrame structure determine the place of a frame on the logical screen, while the w and h fields determine the size of a frame.

[Note: Frames crossing logical screen boundaries are automatically clipped by the XmImageCreate routines.]

timeout

This field field indicates the number of milliseconds that should expire before the next frame in an animation is displayed.

loop_count

This field indicates how many times an animation should be run. A loop_count of zero indicates forever. Any self-respecting application should run animations with a non-zero loop_count only for the specified number of loops.

dispose

This indicates how the previous frame should be removed before a new frame is displayed. This field can have the following values:

XmIMAGE_DISPOSE_NONE (value = 1)
do nothing, overlay the previous frame with the current frame;
XmIMAGE_DISPOSE_BY_BACKGROUND (value = 2)
Restore to background color. The area used by the previous frame should be restored to the background color/image;

XmIMAGE_DISPOSE_BY_PREVIOUS (value = 3)
Restore to previous. The area used by the previous frame should be restored to what was there prior to rendering the previous frame.

Handling disposal methods 2 and 3 are one of the most difficult items to deal with when running an animation. This is the reason why the prev_state field is present in the XmImageFrame structure.

The use of the remaining fields of the XmImageFrame structure will become clear in the following example which demonstrates how to run an animation and how to deal with the different disposal methods.

Example 2-?: Running an animation

[Put in a copy of the src/paint.c, routine DrawFrame]




©Copyright 1996-1997 by Ripley Software Development
Last update: September 19, 1997 by Koen