Text Widgets

  • the Athena text widgets are more complicated than the widgets that we have seen so far
  • a text widget is used for displaying, entering and editing text, the exact functionality of a text widget depends upon how its resources are set
  • a text widget really consists of three widgets, the text widget itself handles the interaction with the user and coordinates the other two widgets
  • the source widget stores and manipulates the text for the text widget, this text could be stored on a file, or it could be a string in main memory
  • the sink widget displays the text on the window for the text widget, this widget is responsible for the formatting of the text on the screen, how the text is displayed
  • we will only look at the Ascii text widget, this is a text widget for the display of standard ascii text, we could construct a widget for displaying other types of text
  • an ascii text widget also has an Ascii source and an Ascii sink as sub-widgets
  • the resources of these widgets control how the text is displayed and manipulate, we will briefly summarize some of these resources
  • the editType resource controls how the user can modify the text displayed in the widget, the possible values for this resource and their interpretation are:
        XawtextRead       The user can't change any of the
    		      text in the text widget
    
        XawtextAppend     The user can only add text to the end
    		      of the text buffer and modify the
    		      entered text, the user cannot modify
    		      the original contents of the buffer
    
    
        XawtextEdit       The user can freely modify any of the
    		      text that is in the buffer, no
    		      restriction are placed on modification
    
  • there are several resources that are used to control the source of the text
  • the type resource indicates where the text comes from, if the value of this resource is XawAsciiString the text is stored in a string in main memory, if the value of this resource is XawAsciiFile then the text is stored in a file
  • in the case of XawAsciiString the source can maintain the string (the source will malloc memory for the string when it is required), or the application can manage the text memory
  • if the application decided to manage the string memory then the useStringInPlace resource must be set to TRUE, the value of the string resource must be set to a pointer to the block of memory used for the string, and the length resource must be set to the length (in bytes) of this block of memory
  • if the source is an XawAsciiFile type then the string resource contains the name of the file that the text is stored on
  • for any type of text widget the string resource can be used to retrieve the current contents of the text buffer, this can be done using the XtGetValues procedure
  • the Ascii text widget can put scroll bars on the left side and bottom of the text area, these scroll bars can be used to scroll the text vertically and horizontally
  • the scrollHorizontal and scrollVertical resources are used to control these scroll bars, the possible values of these resources are:
        XawtextScrollNever          Never use a scrollbar in this
    		                direction
    
        XawtextScrollAlways         Always use a scrollbar in this
    		                direction
    
        XawtextScrollWhenNeeded     Only place a scrollbar on the
    		                widget when the complete contents
    		                of the text buffer can't be
    		                displayed in the window
    
  • the default height of the text widget is enought to hold one line of text, and the default width is 100 pixels, this is enought space for about 10 character, for most applications these resources need to be changed
  • as an example we will look at a simple program that displays a file of text
  • the file to be displayed is passed as a parameter to this program, it creates a text widget to place the text in
  • it sets the type resource toXawAsciiFile to indicate that the text is coming from a file, and sets the string resource to the file name (from argv[1])
  • resource are set to always have a vertical scroll bar and to use a horizontal scroll bar if the text in the file is wider than the text widget
      /*******************************************************************
       *
       *                         text1
       *
       *  Simple test program to show of the text widget
       *
       *****************************************************************/
    
      #include <X11/StringDefs.h>
      #include <X11/Intrinsic.h>
      #include <X11/Xaw/Form.h>
      #include <X11/Xaw/AsciiText.h>
      #include "../lib/lib.h"
    
      main(argc,argv)
      int argc;
      char **argv; {
           Widget toplevel;
           Widget form;
           Widget quit;
           Widget text;
           int n;
           Arg wargs[10];
    
           toplevel = XtInitialize(argv[0],"text",NULL,0,
    		 &argc;,argv);
    
           if(argc != 2) {
    	    printf("usage: text1 [ x options ] filename\n");
    	    exit(1);
           }
    
           form = XtCreateManagedWidget("form", formWidgetClass,
    		 toplevel, NULL, 0);
    
           quit = quit_button(form);
    
           n = 0;
           XtSetArg(wargs[n], XtNfromVert, quit); n++;
           XtSetArg(wargs[n], XtNtype, XawAsciiFile); n++;
           XtSetArg(wargs[n], XtNstring, argv[1]); n++;
           XtSetArg(wargs[n], XtNwidth, 400); n++;
           XtSetArg(wargs[n], XtNheight, 500); n++;
           XtSetArg(wargs[n], XtNscrollHorizontal, XawtextScrollWhenNeeded); n++;
           XtSetArg(wargs[n], XtNscrollVertical, XawtextScrollAlways); n++;
    
           text = XtCreateManagedWidget("text", asciiTextWidgetClass,
    		 form, wargs, n);
    
           XtRealizeWidget(toplevel);
    
           XtMainLoop();
    
      }
    
  • this example program is not very interesting, we must specify the file to be read when we start the program
  • a better approach would be to use another text widget to enter the name of the file to be displayed (I know a menu of file names would be event better, but that spoils the example)
  • we need to have a text widget where we can enter a file name, and once the file name has been entered we can display the file contents
  • there are two ways in which this can be done:
    1. enter the name of the file in a window, and then press a button to open the file
    2. open the file when a certain key, such as a return is pressed
  • we will use both approaches, but it turns out that the second one doesn’t work to well due to a bug in the Athena text widget
  • before we can set this program up we need to know something about text editing
  • the text widget supports a full range of editing commands, including searching and include text from another file, the text editing commands are similar to emacs
  • the text widget keeps keeps two pointers one for the insertion position (where characters will appear when the user types) and one for the display position (the character in the upper left hand corner of the window)
  • all pointers into the text buffer are long int’s and they are offsets from the start of the buffer, that is the position before the first character as value 0
  • there are two resources that are used for setting these positions, these resources are insertPosition and displayPosition
  • either the user or the program can select text, the user selects text by pressing the left mouse button and dragging it over the text to be selected
  • the program selects text by calling the following function:
        XawTextSetSelection(w, left, right)
        Widget    w;
        long left;
        long right;
    
  • after this call the text between the left and right pointers is highlighted and becomes the primary selection
  • to unhighlight the selected text (either the user or program could have selected it), the following function is used:
        XawTextUnsetSelection(w)
        Widget    w;
    
  • the current text selection can be determined by calling the following procedure
        XawTextGetSelectionPos(w, begin, end)
        Widget    w;
        long *begin;
        long *end;
    
  • the program can also replace text that is in the text buffer, note that this operation cannot be performed on a text widget that is read only, it must be possible to edit the text in order for this procedure to be used
        int XawTextReplace(w, start, end, text)
        Widget         w;
        long      start;
        long      end;
        XawTextBlock   *text;
    
  • the start and end parameters specify the text that is to be replaced, the text starting at start and up to, but not including end is replaced, if start and end are equal then a text insertion is performed
  • the text parameter contains the text to be inserted into the text buffer, the declaration of the XawTextBlock data structure is:
        typedef struct {
    	 int       firstPos;
    	 int       length;
    	 char      *ptr;
    	 unsigned long  format;
        } XawTextBlock, *XawTextBlockPtr;
    
  • the ptr field points to the block of text to be included in the text buffer
  • firstPos is the index of the first character in the block of text pointed to by ptr that will be included in the text buffer
  • length is the number of characters to be included in the text buffer
  • format specifies the format of the characters, at the present time this is ignored
  • the possible return values from this routine are:
        XawEditDone       the text replacement was successful
    
        XawPositionError  in append mode the start position is not
    		      the last character in the buffer
    
        XawEditError      the source is read only, or the range
    		      to be deleted is larger than the text
    		      buffer
    
  • for file text sources there are two other useful routines, these routines are used to write the text in the buffer to a file, the declarations of these routines are:
        XawAsciiSave(w)
        Widget    w;
    
        XawAsciiSaveAsFile(w, name)
        Widget    w;
        char *name;
    
  • the XawAsciiSave procedure saves the text buffer to the file named by the string resource, this is the same file that the text was read from
  • the awAsciiSaveAsFile procedure writes the text buffer to another file, the name of this file is given by the name parameter
  • now we are ready to set up our example program
  • the simplest of the two techniques for getting the file name is to use an open button to signal that the file name has been entered
  • to do this all we need to do is create an open command widget and the text widget where the file name is entered
  • for the text widget there is an initial value string that contains the prompt “filename: “, this is the initial value of the string resource
  • when we create the text widget we make sure that it is in edit mode (so we can add characters) and that the insert position is after the file name prompt
  • the call back procedure for the command widget must retrieve the string from the text widget, set the main text widget to that file, and then clean up the filename text widget so it can be used to open another file
  • the string resource is really a resource of the source widget, so we must first retrieve the source widget, using the textSource resource of the text widget, then we can retrieve the string
  • the tricky part is checking for a return used to terminate a file name and signal that the file should be opened
  • the source widget has a callback that can be used to determine when the text buffer is modifed, the documentation states that this callback is called after the text buffer has been updated, but this is wrong, it is actually called before
  • this causes two major problems:
    1. the callback is always one character behind what the user has typed, so we need to type two returns
    2. the callback will end up calling itself and get into an infinite loop
  • to see why this happens, we must remember that we clear out the old file name from the text buffer in the callback procedure, this is a change to the text buffer, therefore, the callback procedure must be called, which will again pick up the file name , …..
  • the only way around this problem is to have a static busy flag in the callback procedure, this flag is set when the procedure is called and unset when it exits, yuch!
  • because of the bugs in the text widget this callback is not really as clean as we would like it to be
  • there is one other thing that we must be careful of, the main text widget must be created in the main program, this simplifies the screen layout
  • but at this point the file name hasn’t been specified, so we can’t pass a file name in the string resource
  • to get around this problem, the main text widget is initially created in string mode with an initial string that states that no file has been selected
  • this reserves space for the text widget, and saves screen reorganization when the file name is specified
  • we could convert this program into a text editor by making the main text widget editable, and adding a save command button that would write the text buffer to a file
      /*******************************************************************
       *
       *                         text2
       *
       *  Simple test program to show of the text widget
       *
       *****************************************************************/
    
      #include <X11/StringDefs.h>
      #include <X11/Intrinsic.h>
      #include <X11/Xaw/Command.h>
      #include <X11/Xaw/Form.h>
      #include <X11/Xaw/AsciiText.h>
      #include "../lib/lib.h"
    
      char filename[] = "filename: ";
      char no_file_message[] = "  *****  no file selected **** ";
      Widget fname;
      Widget quit;
      Widget text;
      Widget form;
    
    
      void text_call(w, client, call)
      Widget w;
      XtPointer client;
      XtPointer call; {
           static Widget source = 0;
           int len;
           char *name;
           char *string;
           int n;
           Arg wargs[5];
           XawTextBlock block;
           static int busy = 0;
    
           if(busy)
    	    return;
    
           busy = 1;
           if(source == 0) {
    	    n = 0;
    	    XtSetArg(wargs[n], XtNtextSource, &source;); n++;
    	    XtGetValues(fname, wargs, n);
           }
    
           n = 0;
           XtSetArg(wargs[n], XtNstring, &string;); n++;
           XtGetValues(source, wargs, n);
    
           len = strlen(string);
           if(string[len-1] == '0) {
    	    name = (char *) malloc(len-9);
    	    strcpy(name,string+10);
    	    name[len-11] = 0;
    
    	    block.firstPos = 0;
    	    block.length = 0;
    	    XawTextReplace(fname, 10, len+1, █);
    	    n = 0;
    	    XtSetArg(wargs[n],XtNinsertPosition, 10); n++;
    	    XtSetArg(wargs[n],XtNdisplayPosition, 0); n++;
    	    XtSetValues(fname, wargs, n);
    
    	    n = 0;
    	    XtSetArg(wargs[n], XtNtype, XawAsciiFile); n++;
    	    XtSetArg(wargs[n], XtNstring, name); n++;
    	    XtSetValues(text, wargs, n);
           }
    
           busy = 0;
    
      }
    
    
      void open_command(w, client, call)
      Widget w;
      XtPointer client;
      XtPointer call; {
           Widget source;
           int n;
           Arg wargs[10];
           char *string;
           int len;
           char *name;
           XawTextBlock block;
    
           n = 0;
           XtSetArg(wargs[n], XtNtextSource, &source;); n++;
           XtGetValues(fname, wargs, n);
           n = 0;
           XtSetArg(wargs[n], XtNstring, &string;); n++;
           XtGetValues(source, wargs, n);
    
           len = strlen(string);
           name = (char *) malloc(len-9);
           strcpy(name,string+10);
    
           block.firstPos = 0;
           block.length = 0;
           XawTextReplace(fname, 10, len, █);
           n = 0;
           XtSetArg(wargs[n],XtNinsertPosition, 10); n++;
           XtSetArg(wargs[n], XtNdisplayPosition, 0); n++;
           XtSetValues(fname, wargs, n);
    
           n = 0;
           XtSetArg(wargs[n], XtNtype, XawAsciiFile); n++;
           XtSetArg(wargs[n], XtNstring, name); n++;
           XtSetValues(text, wargs, n);
    
      }
    
      main(argc,argv)
      int argc;
      char **argv; {
           Widget toplevel;
           Widget source;
           Widget command;
           int n;
           Arg wargs[10];
    
           toplevel = XtInitialize(argv[0],"text",NULL,0,
    		 &argc;,argv);
    
           form = XtCreateManagedWidget("form", formWidgetClass,
    		 toplevel, NULL, 0);
    
           quit = quit_button(form);
    
           n = 0;
           XtSetArg(wargs[n], XtNfromVert, quit); n++;
           XtSetArg(wargs[n], XtNtype, XawAsciiString); n++;
           XtSetArg(wargs[n], XtNwidth, 400); n++;
           XtSetArg(wargs[n], XtNheight, 500); n++;
           XtSetArg(wargs[n], XtNstring, no_file_message); n++;
           XtSetArg(wargs[n], XtNscrollHorizontal, XawtextScrollWhenNeeded); n++;
           XtSetArg(wargs[n], XtNscrollVertical, XawtextScrollAlways); n++;
    
           text = XtCreateManagedWidget("text", asciiTextWidgetClass,
    	    form, wargs, n);
    
           command = XtCreateManagedWidget("open",commandWidgetClass,
    		 form, NULL, 0);
    
           n = 0;
           XtSetArg(wargs[n], XtNfromHoriz, quit); n++;
           XtSetValues(command, wargs, n);
    
           XtAddCallback(command, XtNcallback, open_command, 0);
    
           n = 0;
           XtSetArg(wargs[n], XtNfromHoriz, command); n++;
           XtSetArg(wargs[n], XtNtype, XawAsciiString); n++;
           XtSetArg(wargs[n], XtNstring, filename); n++;
           XtSetArg(wargs[n], XtNwidth, 200); n++;
           XtSetArg(wargs[n], XtNeditType, XawtextEdit); n++;
           XtSetArg(wargs[n], XtNinsertPosition, 10); n++;
    
           fname = XtCreateManagedWidget("filename",asciiTextWidgetClass,
    		 form, wargs, n);
    
           n = 0;
           XtSetArg(wargs[n], XtNtextSource, &source;); n++;
           XtGetValues(fname, wargs, n);
    
           n = 0;
           XtSetArg(wargs[n], XtNdataCompression, FALSE); n++;
           XtSetValues(source, wargs, n);
    
           XtAddCallback(source, XtNcallback, text_call, NULL);
    
           XtRealizeWidget(toplevel);
    
           XtMainLoop();
    
      }