Multiline stylized text
PtWidget → PtBasic → PtContainer → PtCompound → PtMultiText
For more information, see the diagram of the widget hierarchy.
<photon/PtMultiText.h>
The PtMultiText widget class lets you display and edit multilined stylized text.
Lines can be automatically wrapped on character or word boundaries. Optional scrollbars are provided to make it easy to view wide or long documents. Multiple colors and fonts are supported on any line.
The text buffer of the PtMultiText widget is a zero-terminated string consisting of multibyte (UTF-8) characters. You can set attributes (such as font, color, or style) for any segment of text, but this information isn't embedded in the text buffer.
The MultiText widget provides many features that make it ideal for multilined data entry fields or as the basis for an editor:
You can control all these features via the widget's resources and convenience functions. For example, to force the widget to wrap long lines at word boundaries, set the Pt_EMT_WORD bit of the Pt_ARG_MULTITEXT_WRAP_FLAGS resource:
PtSetArg( &argt, Pt_ARG_MULTITEXT_WRAP_FLAGS, Pt_EMT_WORD, Pt_EMT_WORD ); PtSetResources( mtwidget, 1, &argt );
If you set both the word- and character-wrap flags, word wrapping is applied.
You can also control the amount of space left between lines of text using Pt_ARG_LINE_SPACING. The value of this resource is the number of pixels to leave between the descenders of one line of text and the ascenders of the next.
To display a horizontal scrollbar:
|
You can set the contents of the text buffer using the Pt_ARG_TEXT_STRING, Pt_ARG_TEXT_SUBSTRING, or Pt_ARG_MULTITEXT_SEGMENTS resources, or the PtTextModifyText() or PtMultiTextModifyText() convenience functions. The PtMultiText widget automatically wraps the text according to the wrap flags, and displays scrollbars if the Pt_ARG_SCROLLBAR_X_DISPLAY and/or Pt_ARG_SCROLLBAR_Y_DISPLAY resources allow for them.
If you set the text using the Pt_ARG_TEXT_STRING resource, the new text replaces the entire text buffer of the widget, and all the lines of text are drawn using the same attributes. The font is the one specified by the Pt_ARG_TEXT_FONT resource inherited from PtLabel. The text foreground and background colors are taken from the values of the Pt_ARG_COLOR and Pt_ARG_FILL_COLOR resources.
If you set the text using Pt_ARG_TEXT_SUBSTRING, only the specified portion of the text buffer is affected. Text can be deleted and/or inserted. The text inserted is drawn with the same attributes as the text at the insertion point.
You can control the following attributes of a range of text:
There are several methods for setting the attributes that affect a given range of text:
When setting the text and attributes via the Pt_ARG_MULTITEXT_SEGMENTS resource, you provide the resource with an array of PtMultiLines_t structures. Each element of the array has a text string and an associated set of attributes that are used when displaying the text.
The following example shows how to create a multiline text widget with two ranges of text with different attributes:
#include <stdio.h> #include <stdlib.h> #include <Pt.h> #include <Ph.h> char Verdana24[MAX_FONT_TAG]; PtMultiLines_t hello[] = { { "Hello\n", Pt_DEFAULT_FONT, Pt_DEFAULT_COLOR, Pt_INHERIT_COLOR }, { "World!", Pt_DEFAULT_FONT, Pg_BLUE, Pt_INHERIT_COLOR } }; main() { PtWidget_t *window; PtArg_t args[2]; int nargs = 0; PhDim_t dim = { 300, 150 }; if (PtInit(NULL) == -1) PtExit(EXIT_FAILURE); if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT, 0, NULL)) == NULL) PtExit( EXIT_FAILURE ); if(PfGenerateFontName("Verdana", 0, 24, Verdana24) == NULL) { perror("Unable to generate font name"); } else { hello[1].font = Verdana24; } PtSetArg( &args[nargs++], Pt_ARG_DIM, &dim, 0 ); PtSetArg( &args[nargs++], Pt_ARG_MULTITEXT_SEGMENTS, &hello, sizeof( hello )/sizeof(hello[0]) ); PtCreateWidget( PtMultiText, Pt_DEFAULT_PARENT, nargs, args ); PtRealizeWidget( window ); PtMainLoop(); }
You can insert a new range of text into the text buffer and specify its attributes using the PtMultiTextModifyText() function. To delete and/or insert text such that it takes on the attributes in effect at the insertion point, use PtTextModifyText().
The following shows how our “Hello, world” example could be rewritten to insert text into the widget:
#include <stdio.h> #include <stdlib.h> #include <Pt.h> #include <Ph.h> main() { PtWidget_t *window, *mtext; PtArg_t args[2]; int nargs = 0; PhDim_t dim = { 300, 150 }; PtMultiTextAttributes_t attr; char Verdana24[MAX_FONT_TAG]; if (PtInit(NULL) == -1) PtExit(EXIT_FAILURE); if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT, 0, NULL)) == NULL) PtExit( EXIT_FAILURE ); if(PfGenerateFontName("Verdana", 0, 24, Verdana24) == NULL) { perror("Unable to generate font name"); attr.font = Pt_DEFAULT_FONT; } else { attr.font = Verdana24; } PtSetArg( &args[nargs++], Pt_ARG_DIM, &dim, 0 ); mtext = PtCreateWidget( PtMultiText, Pt_DEFAULT_PARENT, nargs, args ); PtTextModifyText( mtext, NULL, NULL, 0, "Hello, \n", 8 ); PtMultiTextModifyText( mtext, NULL, NULL, -1, "World! \n", 8, attr, Pt_MT_FONT ); PtRealizeWidget( window ); PtMainLoop(); }
With a WYSIWYG editor, you can select a range of text in the text widget and change the text attributes of that range. This requires the ability to modify the attributes of a range programmatically. Modifying the attributes of a range is also useful for marking blocks of text, as may be done to indicate the results of a search. You can modify the attributes of a range of text by using the PtMultiTextModifyAttributes() function or by setting the Pt_ARG_MULTITEXT_RANGE_ATTRIBUTES resource.
The following example shows how to change the font of the text selection to 14-point Helvetica, by using PtMultiTextModifyAttributes():
PtMultiTextAttributes_t attr; int start, end; char Helvetica_14[MAX_FONT_TAG]; if( PtTextGetSelection( text, &start, &end ) ) { if(PfGenerateFontName("Helvetica", 0, 14, Helvetica_14) == NULL) { perror("Unable to generate font name"); } else { attr.font = Helvetica_14; PtMultiTextModifyAttributes( text, &start, &end, &attr, Pt_MT_FONT ); } }
This code segment first determines the start and end of the text selection by calling PtTextGetSelection(). This gives the start and end positions to use in a subsequent call to PtMultiTextModifyAttributes().
The following example shows how to change the font of the text selection to 14-point Helvetica, by setting the Pt_ARG_MULTITEXT_RANGE_ATTRIBUTES resource:
PtMultiTextAttributes_t attr; PtMultiTextControl_t mtc; char Helvetica_14[MAX_FONT_TAG]; if( PtTextGetSelection( text, &mtc.tc.start, &mtc.tc.end ) ) { if(PfGenerateFontName("Helvetica", 0, 14, Helvetica_14) == NULL) { perror("Unable to generate font name"); } else { PtArg_t argt; attr.font = Helvetica_14; mtc.attributes = &attr; PtSetArg( &argt, Pt_ARG_MULTITEXT_RANGE_ATTRIBUTES, &mtc, Pt_MT_FONT ); PtSetResources( text, 1, &argt ); } }
The members of the PtMultiTextControl_t structure (also known as PtMultiTextCallback_t) used in the example are:
If you implement editing functions that allow operations that alter the attributes of fonts in text ranges, you should always obtain the current attributes in effect at the start of each range and make changes based on those.
Since the cursor-motion callback notifies your application whenever the cursor is moved, the callback can also notify the application when the user presses the pointer button over a “hot-spot” used for a hypertext link.
You can store the data for a hypertext link itself on the pointer maintained in the tag for the text segment. The cursor-motion callback can then simply:
You can refine this technique further by changing the callback so that it registers only an event handler that invokes the action associated with the hypertext link, then deregisters itself. This lets you define more complex behavior (e.g. proper “activate” behavior for the link). In this case, the link is followed only if the user releases the pointer button when the pointer is over the link.
When dealing with structured text such as hypertext, it's a good idea to break the text down into nodes. Define a data structure for these nodes, defining the type of node and any data associated with the node. Create a range of text for each node, attaching a pointer to the node as the Pt_MT_TAG attribute for the range, and add the range to the widget. The text's original structure can be obtained in callbacks by looking at the tag in the attributes member of the callback data structure.
The following illustrates a simple node structure, allowing either plain text or a hypertext link:
typedef enum text_node_enum { tnText, tnHyperlink } TextNodeType_t; typedef text_node_str { TextNodeType_t type; void *data; } TextNode_t;
The following code illustrates how a hypertext link can be activated within a multiline text widget:
struct line_str { TextNode_t node; char *text; } lines[] = { { {tnText, NULL}, "Click " }, { {tnHyper, (void *)"file.html#id"}, "here" }, { {tnText, NULL}, " to follow a hypertext link"} }; int follow_link(PtWidget_t *widget, void *client_data, PtCallbackInfo_t *info) { PtMultiTextCallback_t *cbs = (PtTextCallback_t *)info->cbdata; if (info->reason == Pt_CB_MOTION_VERIFY && info->event != NULL && (info->event->type & Ph_EV_BUT_PRESS)) { TextNode_t *node = (TextNode_t *)cbs->attributes->tag; printf("URL referenced: %s\n", node->data); } return (Pt_CONTINUE); } void init_text(PtWidget_t *text) { PtMultiTextAttributes_t reg, hyper; int nlines; PtMultiTextCreateAttributes(®); PtMultiTextCreateAttributes(&hyper); hyper.text_color = Pg_DGREEN; for (nlines = 0; nlines < sizeof(lines)/ sizeof(lines[0]); nlines++) { PtMultiTextAttributes_t *attr = lines[nlines].node.type == tnHyper ? &hyper : ® PtMultiTextModifyText (text, 0, 0, -1, lines[nlines].text, &lines[nlines].node, attr, Pt_MT_TAG|Pt_MT_FOREGROUND); } PtAddCallback(text, Pt_CB_MOTION_VERIFY, follow_link, NULL); }
The data member of a hyperlink node is assumed to be a string indicating what action to take. In this case, it refers to another document to load by a URL of the form used by the Photon Helpviewer.
As for all widgets, Pt_ARG_DIM holds the dimensions of a PtMultiText widget. For example, suppose you have a multitext widget that can show four lines of text. If you type more than four lines, say six lines, the widget displays a scrollbar to let you know there are more lines. Querying Pt_ARG_DIM gives the dimensions of the four lines of text.
If you need to determine the dimensions of the entire text — the six lines in our example — you'll need to calculate it as described below:
Use the Pt_ARG_MULTITEXT_QUERY_LINE resource to query the first line (line 1). This gives you information in the form of a PtMultiTextQuery_t structure, which contains a pointer to a PtMultiTextLine_t structure. The PtMultiTextLine_t structure contains a PhRect_t structure (see the Photon Library Reference) that specifies the extent for that line. Calculate the dimensions of the line as:
height = extent.lr.y - extent.ul.y + 1; width = extent.lr.x - extent.ul.x + 1;
The lines are organized as a linked list, using the next and previous pointers in the PtMultiTextLine_t structure. Traverse all the lines until the next pointer is NULL, calculating:
When you've examined all the lines, you'll have the “virtual dimensions” of the text input area (i.e. the area that the text would occupy if it had enough room to do so)
If you have only one font in the PtMultiText widget, the method of finding the dimensions can be simplified. For example, to find the virtual height, calculate the height of the first line and multiply it by the number of lines.
If you select some text and hold down the Ctrl key, you can drag the selected text to a PtText, PtMultiText, PtTerminal, or PtTty widget.
Resource | C type | Pt type | Default |
---|---|---|---|
Pt_ARG_MULTITEXT_BOTTOM_LINE | long | Scalar | None (write-only) |
Pt_ARG_MULTITEXT_FLAGS | long | Flag | See below |
Pt_ARG_MULTITEXT_NUM_LINES | long | Scalar | 1 (read-only) |
Pt_ARG_MULTITEXT_NUM_LINES_VISIBLE | short | Scalar | None (read-only) |
Pt_ARG_MULTITEXT_QUERY_CHARACTER | PtMultiTextQuery_t * | Complex | None (read-only) |
Pt_ARG_MULTITEXT_QUERY_LINE | PtMultiTextQuery_t * | Complex | None (read-only) |
Pt_ARG_MULTITEXT_RANGE_ATTRIBUTES | PtMultiTextControl_t * | Complex | None |
Pt_ARG_MULTITEXT_ROWS | long | Scalar | None (write-only — see below) |
Pt_ARG_MULTITEXT_SEGMENTS | PtMultiLines_t, short | Array | None (write-only) |
Pt_ARG_MULTITEXT_TABS | int, int | Array | {20} |
Pt_ARG_MULTITEXT_TOP_LINE | long | Scalar | 1 |
Pt_ARG_MULTITEXT_WRAP_FLAGS | short | Flag | See below |
Pt_ARG_MULTITEXT_X_SCROLL_POS | short | Scalar | 0 |
Pt_ARG_MULTITEXT_Y_SCROLL_POS | long | Scalar | 1 |
Pt_ARG_SCROLLBAR_X_DISPLAY | unsigned short | Scalar | Pt_NEVER |
Pt_ARG_SCROLLBAR_X_HEIGHT | unsigned short | Scalar | 0 (use scrollbar default of 15) |
Pt_ARG_SCROLLBAR_Y_DISPLAY | unsigned short | Scalar | Pt_NEVER |
Pt_ARG_SCROLLBAR_Y_WIDTH | unsigned short | Scalar | 0 (use scrollbar default of 15) |
The convenience functions can make it easier for you to use the complex resources. For more information, see “Convenience functions,” below. |
C type | Pt type | Default |
---|---|---|
long | Scalar | None |
Set the bottom line (top line + number of visible lines -1).
C type | Pt type | Default |
---|---|---|
long | Flag | Pt_EMT_SCROLL_TO_CURSOR |
Flags that affect the appearance and behavior of the widget. The valid bits are:
C type | Pt type | Default |
---|---|---|
long | Scalar | 1 |
The line number of the last line in the buffer. The first line is line 1, not 0.
C type | Pt type | Default |
---|---|---|
short | Scalar | None |
The number of lines that are currently visible.
C type | Pt type | Default |
---|---|---|
PtMultiTextQuery_t * | Complex | None |
Use this resource to get information about a certain character. This resource is a complex one, so it needs special handling. When getting, set the arguments to PtSetArg() as follows:
C type | Pt type | Default |
---|---|---|
PtMultiTextQuery_t * | Complex | None |
Use this resource to get information about a certain line. This resource is a complex one, so it needs special handling. When getting, set the arguments to PtSetArg() as follows:
C type | Pt type | Default |
---|---|---|
PtMultiTextControl_t * | Complex | None |
This resource modifies/queries the attributes of a specified range. This resource is a complex one, so it needs special handling.
When setting this resource, set the arguments to PtSetArg() as follows:
When getting the value of this resource, set the arguments to PtSetArg() as follows:
C type | Pt type | Default |
---|---|---|
long | Scalar | None (see below) |
Specifies the number of rows. Setting this resource sets the widget's height dimension based on the height of the current font and number of rows specified. There's no default value for this resource because the widget initially uses its dimension to determine the number of rows.
This is a “one-shot” resource; it changes the size of the widget when you set it, based on the widget's current settings. If you later change the font, the value of Pt_ARG_MULTITEXT_ROWS isn't used to recalculate the height of the widget. |
C type | Pt type | Default |
---|---|---|
PtMultiLines_t, short | Array | None |
This resource provides an easy way for you to define a multisegment message. (A segment is a set of contiguous characters that share common attributes, such as font, color, and so on.)
All the text provided in the PtMultiLines_t array is concatenated and used to replace the text in the Pt_ARG_TEXT_STRING resource. The rest of the information in the array is used to build up an index of segments into the text. The array itself isn't preserved within the widget. Consequently, you should treat Pt_ARG_MULTITEXT_SEGMENTS as a write-only resource. To retrieve the text, use the Pt_ARG_TEXT_STRING resource.
C type | Pt type | Default |
---|---|---|
int, int | Array | {20} |
Provides a means of specifying tab stops to the multitext widget. The array provided is an array of integers, each of which is a tab spacing, in pixels, relative to the last tab position. The last tab spacing is repeated; for example, a tab array of {10,20,10} produces tab stops at 10, 30, 40, 50, 60, ... pixels.
C type | Pt type | Default |
---|---|---|
long | Scalar | 1 |
Set or get the top line or vertical scroll position, in lines (where the first line is line 1).
C type | Pt type | Default |
---|---|---|
short | Flag | Pt_EMT_WORD|Pt_EMT_NEWLINE |
This resource controls how the multitext widget wraps. The possible values are:
If both the word and character wrap flags are on, word wrapping is applied.
C type | Pt type | Default |
---|---|---|
short | Scalar | 0 |
The horizontal scroll position (in pixels).
C type | Pt type | Default |
---|---|---|
long | Scalar | 1 |
Set or get the top line or vertical scroll position in lines, where the first line is line 1. This resource is the same as Pt_ARG_MULTITEXT_TOP_LINE.
C type | Pt type | Default |
---|---|---|
unsigned short | Scalar | Pt_NEVER |
This resource indicates when to display the horizontal scrollbar. The possible values are:
In order to display a horizontal scrollbar, you need to clear Pt_EMT_WORD and Pt_EMT_CHAR in the widget's Pt_ARG_MULTITEXT_WRAP_FLAGS resource. |
C type | Pt type | Default |
---|---|---|
unsigned short | Scalar | 0 (use scrollbar default of 15) |
The height of the horizontal scrollbar. If you set this resource to 0, widget uses the default size of 15.
C type | Pt type | Default |
---|---|---|
unsigned short | Scalar | Pt_NEVER |
This resource indicates when to display the vertical scrollbar. The possible values are:
C type | Pt type | Default |
---|---|---|
unsigned short | Scalar | 0 (use scrollbar default of 15) |
The width of the vertical scrollbar. If you set this resource to 0, widget uses the default size of 15.
If the widget modifies an inherited resource, the “Default override” column indicates the new value. This modification affects any subclasses of the widget.
By default, this widget has the bit Pt_TEXT_BLINKING_CURSOR in Pt_ARG_TEXT_FLAGS set to on. |
Pt_CB_ACTIVATE is inherited from PtBasic, but its behavior is different for a PtMultiText widget. Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
If reason_subtype is Pt_EDIT_ACTIVATE or Pt_CHANGE_ACTIVATE, cbdata points to a PtMultiTextCallback_t structure that contains at least the following members:
PtTextCallback_t tc; PtMultiTextAttributes_t const *attributes; PtMultiTextSegment_t *seg; void *extended_data;
Before using text, check the length field to make sure text contains valid data. In addition, note that text might not be zero-terminated.
For more information, see PtTextModifyText() or PtMultiTextModifyText().
These callbacks should return Pt_CONTINUE.
Pt_CB_GOT_FOCUS and Pt_CB_LOST_FOCUS are inherited from PtBasic, but the cbdata member of the callback information is different for a PtMultiText widget.
Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
The members of the PtMultiTextCallback_t structure are used as follows:
The Pt_CB_GOT_FOCUS callbacks should return Pt_CONTINUE.
The Pt_CB_LOST_FOCUS callbacks should return:
Or:
Pt_CB_TEXT_CHANGED (Pt_CB_MODIFY_NOTIFY), and Pt_CB_MOTION_NOTIFY are inherited from PtText but the cbdata member of the callback information is different for a PtMultiText widget.
Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
The members of the PtMultiTextCallback_t structure are used as follows:
These callbacks should return Pt_CONTINUE.
Pt_CB_MODIFY_VERIFY is inherited from PtText, but the cbdata member of the callback information is different for a PtMultiText widget.
Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
The members of the PtMultiTextCallback_t structure are used as follows:
Before using text, check the length field to make sure text contains valid data. In addition, note that text might not be zero-terminated.
For more information, see PtTextModifyText() or PtMultiTextModifyText().
These callbacks should return Pt_CONTINUE.
Pt_CB_MOTION_VERIFY (Pt_CB_MODIFY_NOTIFY), is inherited from PtText, but the cbdata member of the callback information is different for a PtMultiText widget.
Each callback is passed a PtCallbackInfo_t structure that contains at least the following members:
If cbinfo->event is NULL, the cursor motion occurred because:
Or:
These callbacks should return Pt_CONTINUE.
The PtMultiText widget defines several convenience functions and data structures that make it easier to use the widget once it's been created. Here's a brief overview: