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.
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.
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).
Figure 2-1. simple_html.c: a minimally configured XmHTML widget
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.
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.
[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.
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.
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.
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.
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.
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.
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).
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.]
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.
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.]
Another set of resources allow you to control the fonts that are used to render the displayed text.
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.
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.
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.
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.
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).
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.
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.]
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:
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.
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.
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".
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.
"Set by default" indicates a bit set when the XmHTMLImageDefaultProc was used to read an image.
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 |
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.
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:
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.
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.
/* 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:
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