_INTRINSICS OF THE X TOOLKIT_
by Todd Lainhart


[LISTING ONE]

!
! Resource specifications for simple text editor
!
*textEdit.rows: 24
*textEdit.columns:  80
*textEdit.resizeWidth:  False
*textEdit.resizeHeight: False
*textEdit.translations: #override \n\
                        Ctrl<Key>Right: forward-word()  \n\
                        Ctrl<Key>Left:  backward-word() \n\
                        Ctrl<Key>a:     beginning-of-line() \n\
                        Ctrl<Key>e:     end-of-line() \n\
                        Ctrl<Key>a, Ctrl<Key>a:  beginning-of-file() \n\
                        Ctrl<Key>e, Ctrl<Key>e:  end-of-file()



[LISTING TWO]


/*~PKG*************************************************************************
 *  Package Name: xm_main.c
 *  Synopsis: Implements a simple text editor using the Motif toolkit.
 *  Features Supported: Not much.
 *  References: Xt Programming and Apps by Doug Young.
 *  Xm Programming Reference and Guide by OSF.
 *  Xt Programming Reference and Guide by O'Reilly.
 *  Usage: Bind this with a couple of other support objs.
 *  Known Bugs/Deficiencies:
 *  Modification History: 11/01/90    twl  original
 */

/******************************************************************************
 *  Header files included. */
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/Text.h> 
#include <Xm/RowColumn.h>
#include <Xm/PushBG.h>
#include <Xm/FileSB.h>
#include <Xm/SelectioB.h>
#include "xm_callbacks.h"
      
/******************************************************************************
 *  Constants and variables local to this package. */
   
/* These widgets are the popup menu items, externalized here so that
 * functions within this package can have access (for the setting/unsetting
 * of selectability.  */
static Widget CopyMenuItem;
static Widget CutMenuItem;
static Widget PasteMenuItem;
static Widget PasteFileMenuItem;
static Widget WriteFileMenuItem;

static void   ExitApp();
                   
/* The actions table for declaring new translations. */
static
XtActionsRec actionTable[] = 
{
    { "exit", ExitApp },
};

/******************************************************************************
 *  Procedure:  ExitApp
 *  Synopsis: Action procedure for exiting application
 *  Assumptions: None.
 *  Features Supported:
 *  Known Bugs/Deficiencies: We're not interested in checking state of the editor before going down.
 *      Regardless of the circumstances, down we go.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void
ExitApp( Widget parent, XEvent *event, String *actionArgs, Cardinal argNdx )
{

    XtCloseDisplay( XtDisplay( parent ) );
    exit( 0 );
}

/******************************************************************************
 *  Procedure:  DisplayTextEditMenu
 *  Synopsis: Event handler to display the text body popup menu.
 *  Assumptions:  The parent is a Text Widget.
 *  Features Supported:
 *  Known Bugs/Deficiencies: External resources should be considered.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void
DisplayTextEditMenu( textBody, popupMenu, xEvent ) 
  
    Widget  textBody;           /* Owner of the event handler */
    Widget  popupMenu;          /* Data passed by the registering procedure */
    XEvent *xEvent;             /* Passed to all event handlers */
{
  
    Arg     argList[25];        /* Resource argument list */
    int     argNdx;             /* Index into resource list */

    int     menuButton;         /* MENU button assigned to popup */
    char   *selectedText;       /* The text selected for the widget invoking
    
                   
    /* We're assuming that the owning widget of this event handler is of 
     * type XmCText. If not, get out. */
    if ( !XmIsText( textBody ) )
    {
        printf( "DisplayTextEditMenu: Not Text\n" ); 
        exit( 1 );
    }

    /* We're also assuming that the the data passed by the event handler
     * is a popup menu widget.  If not, get out. */
    if ( !XmIsRowColumn( popupMenu ) )
    {
        printf( "DisplayTextEditMenu: Not RowColumn\n" );
        exit( 1 );
    }
   
    /* Check to see if the button that caused this event is the menu 
     * button.  If not, get out. */ 
    argNdx = 0;
    XtSetArg( argList[argNdx], XmNwhichButton, &menuButton ); argNdx++;
    XtGetValues( popupMenu, argList, argNdx ); 
    if ( xEvent->xbutton.button != menuButton )
    {
        return;
    } 
    
    /* We need to set the selectability of the menu items here. For most menu 
     * items, that involves checking to see if any text has been selected. */
    selectedText = XmTextGetSelection( textBody );
   
    /* The Copy menu item. */
    if ( selectedText != NULL )
    {
        XtSetSensitive( CopyMenuItem, TRUE );
    }
    else
    {
        XtSetSensitive( CopyMenuItem, FALSE );
    }
       
    /* The Cut menu item. */
    if ( selectedText != NULL )
    {      
        XtSetSensitive( CutMenuItem, TRUE );
    }
    else
    {
        XtSetSensitive( CutMenuItem, FALSE );
    }
       
    /* The Paste menu item.  See if there's something in the clipboard, 
     * and set sensitivity accordingly. */
    if ( selectedText == NULL )
    {                            
        if ( ClipboardIsEmpty( textBody ) )
        {
            XtSetSensitive( PasteMenuItem, FALSE );
        }
        else
        {
            XtSetSensitive( PasteMenuItem, TRUE );
        }
    }
    else
    {
        XtSetSensitive( PasteMenuItem, FALSE );
    }
     
    /* The PasteFile menu item.  Let's say that we can only paste from a file 
     *  if no text has been selected. */
    if ( selectedText == NULL )
    {                            
        XtSetSensitive( PasteFileMenuItem, TRUE );
    }
    else
    {
        XtSetSensitive( PasteFileMenuItem, FALSE );
    }
     
    /* The WriteFile menu item. */
    if ( selectedText != NULL )
    {      
        XtSetSensitive( WriteFileMenuItem, TRUE );
    }
    else
    {
        XtSetSensitive( WriteFileMenuItem, FALSE );
    } 

    XmMenuPosition( popupMenu, xEvent );
    XtManageChild( popupMenu );

}

/*~PROC************************************************************************
 *  Procedure: CreateTextEditPopup
 *  Synopsis: Creates the Popup menu displayed over the text edit area.
 *      Callbacks are also defined here.
 *  Assumptions: 
 *  Features Supported:
 *  Known Bugs/Deficiencies: External resources should perhaps be considered.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 
static Widget
CreateTextEditPopup( Widget parent )
{

    Widget  textPopup;      /* Created popup returned */
    Arg     argList[25];    /* Resource argument list */
    int     argNdx;         /* Index into argument list */ 

    Widget  fileDialog;     /* File selection dialog box */
    Widget  promptDialog;   /* Text input prompt */
       
    /* We assume a text edit widget as parent.  If not, get out. */
    if ( !XmIsText( parent ) )
    {
        printf( "CreateTextEditPopup: Not Text\n" );
        exit( 1 );
    }

    /* Create the popup menu.  We'll tell Xt to manage it at the time that 
     * it needs to be displayed. */
    textPopup = XmCreatePopupMenu( parent, "textPopup", NULL, 0 ); 

    /* Add the menu items (buttons). */  
    argNdx = 0;
    XtSetArg( argList[argNdx], XmNlabelString, XmStringCreateLtoR( "Copy", XmSTRING_DEFAULT_CHARSET ) ); argNdx++;
    CopyMenuItem = XmCreatePushButtonGadget( textPopup, "copyMenuItem", argList, argNdx );
    XtManageChild( CopyMenuItem );

    argNdx = 0;
    XtSetArg( argList[argNdx], XmNlabelString, XmStringCreateLtoR( "Cut", XmSTRING_DEFAULT_CHARSET ) ); argNdx++;
    CutMenuItem = XmCreatePushButtonGadget( textPopup, "cutMenuItem", argList, argNdx );
    XtManageChild( CutMenuItem );

    argNdx = 0;
    XtSetArg( argList[argNdx], XmNlabelString, XmStringCreateLtoR( "Paste", XmSTRING_DEFAULT_CHARSET ) ); argNdx++;
    PasteMenuItem = XmCreatePushButtonGadget( textPopup, "pasteMenuItem", argList, argNdx );
    XtManageChild( PasteMenuItem );

    argNdx = 0;
    XtSetArg( argList[argNdx], XmNlabelString, XmStringCreateLtoR( "Paste From File...", XmSTRING_DEFAULT_CHARSET ) ); argNdx++;
    PasteFileMenuItem = XmCreatePushButtonGadget( textPopup, "pasteFileMenuItem", argList, argNdx );
    XtManageChild( PasteFileMenuItem );
      
    argNdx = 0;
    XtSetArg( argList[argNdx], XmNlabelString, XmStringCreateLtoR( "Write To File...", XmSTRING_DEFAULT_CHARSET ) ); argNdx++;
    WriteFileMenuItem = XmCreatePushButtonGadget( textPopup, "writeFileMenuItem", argList, argNdx );
    XtManageChild( WriteFileMenuItem );
                        
    /* Add the File Selection dialog, to be invoked by PasteFileMenu button. */  
    argNdx = 0;
    XtSetArg( argList[argNdx], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL ); argNdx++;
    XtSetArg( argList[argNdx], XmNdialogTitle, XmStringCreateLtoR( "Paste From File", XmSTRING_DEFAULT_CHARSET ) ); argNdx++;
    XtSetArg( argList[argNdx], XmNselectionLabelString, XmStringCreateLtoR( "Directory", XmSTRING_DEFAULT_CHARSET ) ); argNdx++ ;
    XtSetArg( argList[argNdx], XmNautoUnmanage, True ); argNdx++;
    fileDialog = XmCreateFileSelectionDialog( parent, "fileDialog", argList, argNdx );

    /* Add a selection dialog, to be invoked by the WriteFileMenu button. */ 
    argNdx = 0;
    XtSetArg( argList[argNdx], XmNdialogStyle, XmDIALOG_APPLICATION_MODAL ); argNdx++;
    XtSetArg( argList[argNdx], XmNdialogTitle, XmStringCreateLtoR( "Write To File", XmSTRING_DEFAULT_CHARSET ) ); argNdx++;
    XtSetArg( argList[argNdx], XmNselectionLabelString, XmStringCreateLtoR( "File", XmSTRING_DEFAULT_CHARSET ) ); argNdx++ ;
    XtSetArg( argList[argNdx], XmNtextColumns, 32 ); argNdx++;
    promptDialog = XmCreatePromptDialog( parent, "promptDialog", argList, argNdx );

    /* Add callbacks for the menu buttons. */
    XtAddCallback( CopyMenuItem,      XmNactivateCallback, CopyCB,      parent );
    XtAddCallback( CutMenuItem,       XmNactivateCallback, CutCB,       parent );
    XtAddCallback( PasteMenuItem,     XmNactivateCallback, PasteCB,     parent );
    XtAddCallback( PasteFileMenuItem, XmNactivateCallback, PasteFileCB, fileDialog );
    XtAddCallback( WriteFileMenuItem, XmNactivateCallback, WriteFileCB, promptDialog );

    /* Add callbacks for the dialog box buttons. */
    XtAddCallback( fileDialog,   XmNokCallback,     FileDialogOKCB,   parent ); 
    XtAddCallback( fileDialog,   XmNcancelCallback, UnMapDialogCB,    fileDialog );
    XtAddCallback( fileDialog,   XmNhelpCallback,   UnMapDialogCB,    fileDialog );
    XtAddCallback( promptDialog, XmNokCallback,     PromptDialogOKCB, parent );
    XtAddCallback( promptDialog, XmNcancelCallback, UnMapDialogCB,    promptDialog );
    XtAddCallback( promptDialog, XmNhelpCallback,   UnMapDialogCB,    promptDialog );

    return( textPopup );

}

/*~PROC************************************************************************
 *  Procedure: main
 *  Synopsis: Initializes the Intrinsics, creates all of the higher-level widgets
 *  necessary to make the application happen, and enters the main loop.
 *  Assumptions:
 *  Usage: Command-line arguments are ignored (for now).
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void
main( int argc, char *argv[] )
{

    Widget  topShell;         /* Top level shell created by the Intrinsics */
    Widget  textEdit;         /* Main edit Text Widget */
    Widget  textMenu;         /* Popup menu associated with the text editor */
                                      
    Arg     argList[25];        /* Resource argument list */
    int     argNdx;             /* Index into resource list */

    /* Initialize the Intrinsics. */
    topShell = XtInitialize( argv[0], "Editor", NULL, 0, &argc, argv );
    
    /* Create the scrolled Text Widget */
    argNdx = 0;
    XtSetArg(argList[argNdx], XmNscrollVertical,   True );              argNdx++;
    XtSetArg(argList[argNdx], XmNscrollHorizontal, True );              argNdx++;
    XtSetArg(argList[argNdx], XmNeditMode,         XmMULTI_LINE_EDIT ); argNdx++;

    textEdit = XmCreateScrolledText( topShell, "textEdit", argList, argNdx );
    XtManageChild( textEdit );
                 
    /* Create the context-sensitive popup menu for this Widget */
    textMenu = CreateTextEditPopup( textEdit ); 

    /* Add the event handler to the Text Widget, invoking the popup menu. */
    XtAddEventHandler( textEdit, ButtonPressMask, FALSE, DisplayTextEditMenu, textMenu );
     
    /* Register new actions to be associated with our app. */
    XtAddActions( actionTable, XtNumber( actionTable ) );

    /* Map the editor, and enter the event dispatch loop. */
    XtRealizeWidget( topShell );
    XtMainLoop();

}


[LISTING THREE]

/*~PKG*************************************************************************
 *  Package Name: xm_callbacks.c
 *  Synopsis: Common text manipulation callbacks.
 *  Features Supported:
 *  References: Xt Programming and Apps by Doug Young.
 *      	Xm Programming Reference and Guide by OSF.
 *      	Xt Programming Reference and Guide by O'Reilly.
 *  Usage: Include "xm_callbacks.h"
 *  Known Bugs/Deficiencies:
 *  Modification History: 11/01/90    twl  original
 */

/*~HDR*************************************************************************
 *  Header files included.
 */ 
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <X11/Xatom.h>
#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/Text.h>
#include <Xm/FileSB.h> 

#include "xm_clipboard.h"

/*~PROC************************************************************************
 *  Procedure:  MapDialogCB
 *  Synopsis: Maps the referenced dialog box.
 *  Assumptions: The parent has been realized.
 *      The widget passed to this callback is a subclass of dialogshell.
 *  Features Supported:
 *  Known Bugs/Deficiencies:
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void
MapDialogCB( source, dialog, callbackData )
    
    Widget source;                      /* Source of the callback */
    Widget dialog;                      /* Data passed to the callback by the register procedure */
    XmAnyCallbackStruct *callbackData;  /* Generic data passed to all callback procedures */
{  

    XtManageChild( dialog );

}

/*~PROC************************************************************************
 *  Procedure:  UnMapDialogCB
 *  Synopsis: Unmaps the referenced dialog box.
 *  Assumptions:  The parent has been realized.
 *      The widget passed to this callback is a subclass of dialogshell.
 *  Features Supported:
 *  Known Bugs/Deficiencies:
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void
UnMapDialogCB( source, dialog, callbackData )
    
    Widget source;                      /* Source of the callback */
    Widget dialog;                      /* Data passed to the callback by the register procedure */
    XmAnyCallbackStruct *callbackData;  /* Generic data passed to all callback procedures */
{  

    XtUnmanageChild( dialog );

}

/*~PROC************************************************************************
 *  Procedure:  CutCB
 *  Synopsis: Callback procedure for cutting text from the referenced text 
 *  widget to the clipboard.  Callback for the "Cut" menu item.
 *  Assumptions: 
 *  Features Supported:
 *  Known Bugs/Deficiencies: Cursor should change to a wait state.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void
CutCB( source, textID, callbackData )
    
    Widget source;                      /* Source of the callback */
    Widget textID;                      /* Data passed to the callback by the register procedure */
    XmAnyCallbackStruct *callbackData;  /* Generic data passed to all callback procedures */
{ 
  
    XClientMessageEvent clientMessage;  /* X client message structure */
    Time                timestamp;      /* X Event time */
    int                 clipStat;       /* Return status of clipboard call */

    /* Get the event timestamp */ 
    timestamp = ((XButtonEvent *)callbackData->event)->time;

    /* Copy the selected text to the clipboard. */
    clipStat = CopyToClipboard( textID, timestamp );

    /* Delete the selected text from the Text Widget */ 
    if ( clipStat == True )
    {
        clientMessage.type         = ClientMessage;
        clientMessage.display      = XtDisplay( textID );
        clientMessage.message_type = XmInternAtom( XtDisplay( textID ), "KILL_SELECTION", FALSE );
        clientMessage.window       = XtWindow( textID );
        clientMessage.format       = 32;
        clientMessage.data.l[0]    = XA_PRIMARY;
        XSendEvent( XtDisplay( textID ), clientMessage.window, TRUE, NoEventMask, &clientMessage ); 
    }

} 

/*~PROC************************************************************************
 *  Procedure:  CopyCB
 *  Synopsis: Callback procedure for copying text from the referenced text 
 *  widget to the clipboard.  Callback for the "Copy" menu item.
 *  Assumptions: 
 *  Features Supported:
 *  Known Bugs/Deficiencies: The cursor should change into a waiting cursor.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void
CopyCB( source, textID, clientData ) 

    Widget source;                      /* Source of the callback */
    Widget textID;                      /* Data passed to the callback as defined by the registering procedure */
    XmAnyCallbackStruct *clientData;    /* Generic data passed to all callback procedures */

{
    Time     eventTime;                 /* Time stamp for the clipboard */
         
    /* Get the time the event occurred */
    eventTime = ((XButtonEvent *)clientData->event)->time;

    /* Copy the selected text (if any) to the clipboard */
    CopyToClipboard( textID, eventTime );

}

/*~PROC************************************************************************
 *  Procedure:  PasteCB
 *  Synopsis: Callback procedure for pasting text from the referenced text widget
 *      to the clipboard.  Callback for the "Paste" menu item.
 *  Assumptions: 
 *  Features Supported:
 *  Known Bugs/Deficiencies: External resources should be considered.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 
void
PasteCB( source, textID, callbackData )

    Widget  source;                     /* Owner of the callback */
    Widget  textID;                     /* Data passed to the callback routine by */
                                        /* the registering procedure */
    XmAnyCallbackStruct *callbackData;  /* Data passed to all callbacks */ 

{
    char    *pasteText;         /* That text which is to be retrieved from the paste buffer */
    Time     eventTime;         /* Time stamp for the clipboard routines */
    Arg      argList[25];       /* Resource retrieval array */
    int      argNdx;            /* Index into resource array */

    XmTextPosition textCursorPos; /* Position of Text Widget insertion cursor */
      
    /* Get the time the event occurred (for transaction timestamping) */
    eventTime = ((XButtonEvent *)callbackData->event)->time;
     
    /* Get the latest text from the clipboard. */
    pasteText = RetrieveFromClipboard( textID, eventTime );

    /* See if we have a hit.  If not, get out. */
    if ( pasteText == NULL )
    {
        return;
    }

    /* Get the insertion point of the text Widget */
    argNdx = 0;
    XtSetArg( argList[argNdx], XmNcursorPosition, &textCursorPos ); argNdx++;
    XtGetValues( textID, argList, argNdx );

    /* ...and insert the text */
    XmTextReplace( textID, textCursorPos, textCursorPos, pasteText );

    XtFree( pasteText );

}

/*~PROC************************************************************************
 *  Procedure:  PasteFileCB
 *  Synopsis: Callback procedure for the Paste from File... menu item.
 *      Currently, just the dialog box is displayed.
 *  Assumptions: 
 *  Features Supported:
 *  Known Bugs/Deficiencies: External resources should be considered.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void
PasteFileCB( source, dialog, callbackData )

    Widget  source;                     /* Owner of the callback */
    Widget  dialog;                     /* Data passed to the callback routine by */
                                        /* the registering procedure */
    XmAnyCallbackStruct *callbackData;  /* Data passed to all callbacks */ 
 
{ 
    XtManageChild( dialog );
}    

/*~PROC************************************************************************
 *  Procedure:  WriteFileCB
 *  Synopsis: Callback procedure for the Write to File... menu item.
 *      Currently, just the dialog box is displayed.
 *  Assumptions: 
 *  Features Supported:
 *  Known Bugs/Deficiencies: External resources should be considered.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void
WriteFileCB( source, dialog, callbackData )

    Widget  source;                     /* Owner of the callback */
    Widget  dialog;                     /* Data passed to the callback routine by */
                                        /* the registering procedure */
    XmAnyCallbackStruct *callbackData;  /* Data passed to all callbacks */ 
 
{   
    XtManageChild( dialog );
}

/*~PROC************************************************************************
 *  Procedure:  FileDialogOKCB
 *  Synopsis: Callback procedure for the activation of the OK button on the file selection
 *      dialog box.
 *  Assumptions: The file to be pasted is ASCII.
 *      The source of the callback is a file selection dialog box.
 *  Features Supported:
 *  Known Bugs/Deficiencies: External resources should be considered.
 *      The file to be pasted is not checked for type (should be ASCII).
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 
void
FileDialogOKCB( source, textID, callbackData )

    Widget                            source;       /* Owner of the callback */
    Widget                            textID;       /* Data passed to the callback routine */
    XmFileSelectionBoxCallbackStruct *callbackData; /* Data passed to all file selection callbacks */
{  
    char       *pasteFile;              /* Filename returned from the dialog */
    int         pasteFileLen;           /* Length of referenced file */
    char       *pasteText;              /* Contents of reference file */
    struct stat statBuf;                /* Buffer for stat() results */
    FILE       *fileDesc;               /* UNIX file descriptor */
   
    Arg         argList[25];            /* Resource retrieval array */
    int         argNdx;                 /* Index into resource array */

    XmTextPosition textCursorPos;    /* Position of Text Widget insertion cursor */

    if ( !XmIsText( textID ) )
    {
        printf( "FileDialogOKCB: Not Text Widget\n" );
        exit( 1 );
    }
      
    if ( !XmIsFileSelectionBox( source ) )
    {
        printf( "FileDialogOKCB: Not dialog box\n" );
        exit( 1 );
    }

    /* Get the filename */
    XmStringGetLtoR( callbackData->value, XmSTRING_DEFAULT_CHARSET, &pasteFile );

    /* Open the file */ 
    fileDesc = fopen( pasteFile, "r" );
    if ( fileDesc == NULL )
    {
        /* Display an error prompt, and get out */
        printf( "FileDialogOKCB: File not available for read\n" );
        exit( 1 );
    }

    /* Get its length, read the contents, and close it up. */
    stat( pasteFile, &statBuf );
    pasteFileLen = statBuf.st_size;
    pasteText  = XtMalloc( pasteFileLen );
    fread( pasteText, sizeof( char ), pasteFileLen, fileDesc );
    fclose( fileDesc );

    /* Paste the contents at the current insertion point. */
    argNdx = 0;
    XtSetArg( argList[argNdx], XmNcursorPosition, &textCursorPos ); argNdx++;
    XtGetValues( textID, argList, argNdx );
    XmTextReplace( textID, textCursorPos, textCursorPos, pasteText ); 

    /* Free up resources */
    XtFree( pasteFile );
    XtFree( pasteText );
    
    /* Bring down the dialog box */
    XtUnmanageChild( source );

}

/*~PROC************************************************************************
 *  Procedure:  PromptDialogOKCB
 *  Synopsis: Callback procedure for the activation of the OK button on the prompt
 *      dialog box.
 *  Assumptions: 
 *  Features Supported:
 *  Known Bugs/Deficiencies: External resources should be considered. 
 *      Minimal error checking on file creation and write.
 *  Modification History: 08/20/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 
void
PromptDialogOKCB( source, textID, callbackData )

    Widget                        source;       /* Owner of the callback */
    Widget                        textID;       /* Data passed to the callback routine */
    XmSelectionBoxCallbackStruct *callbackData; /* Data passed to all selection callbacks */
{
          
    char       *writeFile;              /* Filename returned from the dialog */
    int         writeFileLen;           /* Length of referenced file */
    char       *writeText;              /* Contents of reference file */
    struct stat statBuf;                /* Buffer for stat() results */
    FILE       *fileDesc;               /* UNIX file descriptor */
         
    char       *selectedText;           /* That text which is marked as selected in textID */

    if ( !XmIsText( textID ) )
    {
        printf( "PromptDialogOKCB: Not Text Widget\n" );
        exit( 1 );
    }
       
    /* If no text selected, we can leave. */
    selectedText = XmTextGetSelection( textID );
    if ( selectedText == NULL )
    {
        return;
    }

    /* Get the filename */
    XmStringGetLtoR( callbackData->value, XmSTRING_DEFAULT_CHARSET, &writeFile );
        
    /* Open the file */ 
    fileDesc = fopen( writeFile, "w" );
    if ( fileDesc == NULL )
    {
        /* Display an error, and get out */
        printf( "PromptDialogOKCB: Error on file creation\n" );
        exit( 1 );
    }
      
    /* Write the file, and close it up */
    fwrite( selectedText, sizeof( char ), strlen( selectedText ), fileDesc );
    if ( fclose( fileDesc ) != NULL )
    {
        /* Display an error, and get out */
        printf( "PromptDialogOKCB: Error on file close\n" );
        exit( 1 ); 
    } 

}


[LISTING FOUR]

/*~PKG*************************************************************************
 *  Package Name: xm_clipboard.c
 *  Synopsis: Implements clipboard store and retrieve procedures.
 *  Features Supported:
 *  References: Xt Programming and Apps by Doug Young.
 *      Xm Programming Reference and Guide by OSF.
 *      Xt Programming Reference and Guide by O'Reilly.
 *  Usage: Include "xm_clipboard.h"
 *  Known Bugs/Deficiencies:
 *  Modification History: 11/01/90    twl  original
 */

/*~HDR*************************************************************************
 *  Header files included.  */
#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/Text.h>
#include <Xm/CutPaste.h>

/*~LOC*DATA********************************************************************
 *  Constants and variables local to this package.  */
   
#define CBLABEL "TextEdit"

/*~PROC************************************************************************
 *  Procedure:  CopyToClipboard
 *  Synopsis: Retrieve selected text from reference textID, and copy it to the system
 *      clipboard.  Returns True if successful, False if not.
 *  Assumptions: 
 *  Features Supported:
 *  Known Bugs/Deficiencies: Text only supported.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int
CopyToClipboard( Widget textID, Time timestamp )
{    

    char    *selectedText;      /* That text which is marked as selected in textID */
    int      clipStat;          /* Return value from XmClipboard routines */
    XmString clipLabel;         /* The label used to identify the clipboard string */
    long     clipID, copyID;    /* The handles used in identifying clipboard transactions */

    /* Sanity check. */
    if ( !XmIsText( textID ) )
    {
        printf( "CopyToClipboard: Not Text Widget\n" );
        exit( 1 );
    }

    /* If no text selected, we can leave. */
    selectedText = XmTextGetSelection( textID );
    if ( selectedText == NULL )
    {
        return( False );
    }
                                      
    /* Create the label that appears in the clipboard. */
    clipLabel = XmStringCreateLtoR( CBLABEL, XmSTRING_DEFAULT_CHARSET );
       
    /* Poll the clipboard, asking for permission to start. */ 
    clipStat = ClipboardLocked;
    while( clipStat == ClipboardLocked )
    {
        clipStat = XmClipboardStartCopy( XtDisplay( textID ), XtWindow( textID ),
                                         clipLabel, timestamp, textID, NULL,
                                         &clipID );
    }
  
    /* Copy the data to the clipboard until successful. */
    clipStat = ClipboardLocked;
    while( clipStat == ClipboardLocked )
    {
        clipStat = XmClipboardCopy( XtDisplay( textID ), XtWindow( textID ), clipID,
                                    XtRString, selectedText, (long)strlen( selectedText ), 0
                                    &copyID );

    }

    /* End the transaction... */
    clipStat = ClipboardLocked;
    while( clipStat == ClipboardLocked )
    {
        clipStat = XmClipboardEndCopy( XtDisplay( textID ), XtWindow( textID ), clipID );

    }
    
    /* ... cleanup, and leave. */  
    XtFree( selectedText );
    XmStringFree( clipLabel );

    return( True );
}

/*~PROC************************************************************************
 *  Procedure:  RetrieveFromClipboard
 *  Synopsis: Return text from the clipboard.
 *  Assumptions: The caller assumes responsibility for freeing returned string.
 *  Features Supported:
 *  Known Bugs/Deficiencies: Text only supported.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char *
RetrieveFromClipboard( Widget textID, Time timestamp )
{

    char    *pasteText;         /* That text which is to be retrieved from the paste buffer */
    int      pasteTextLen;      /* Length of text in clipboard */
    int      clipStat;          /* Return value from XmClipboard routines */
    XmString clipLabel;         /* The label used to identify the clipboard string */
    long     clipID, privateID; /* The handles used in identifying clipboard transactions */
    long     outlen;            /* Length of data retrieved from clipboard */

    /* Check to be sure that we have a text Widget */
    if ( !XmIsText( textID ) )
    {
        printf( "RetrieveFromClipboard: Widget not Text\n" );
        exit( 1 );
    }                 
      
    /* Start our clipboard transaction */
    clipStat = ClipboardLocked;
    while( clipStat == ClipboardLocked )
    {
        clipStat = XmClipboardStartRetrieve( XtDisplay( textID ), XtWindow( textID ),
                                             timestamp );
    }  
    
    /* Get the length of the clipboard contents */
    clipStat     = ClipboardLocked;
    pasteTextLen = 0;
    while( clipStat == ClipboardLocked )
    {
        clipStat = XmClipboardInquireLength( XtDisplay( textID ), XtWindow( textID ),
                                             XmRString, &pasteTextLen ); 
        if ( clipStat == ClipboardNoData )
        {
            return( NULL );
        }
    }
                 
    /* Retrieve the data (allocating a string buffer) */
    pasteText = XtMalloc( pasteTextLen + 1 );

    clipStat = ClipboardLocked;
    while( clipStat == ClipboardLocked )
    {
        clipStat = XmClipboardRetrieve( XtDisplay( textID ), XtWindow( textID ),
                                        XmRString, pasteText, pasteTextLen,
                                        &outlen, &privateID );
    }
     
    /* End the clipboard session... */
    clipStat = ClipboardLocked;
    while( clipStat == ClipboardLocked )
    {
        clipStat = XmClipboardEndRetrieve( XtDisplay( textID ), XtWindow( textID ) );
    }
           
    /* ... and return the clipboard contents. */
    return( pasteText );

}

/*~PROC************************************************************************
 *  Procedure:  ClipboardIsEmpty
 *  Synopsis: Returns FALSE, if no items in the clipboard.
 *  Assumptions:
 *  Features Supported:
 *  Known Bugs/Deficiencies: Text only supported.  Returns False (no data) if clipboard is locked.
 *  Modification History: 11/01/90  twl     original
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int
ClipboardIsEmpty( Widget w )
{

    int clipStat;       /* Clipboard status value */
    int textLength;     /* Length of text in clipboard */

    clipStat = XmClipboardInquireLength( XtDisplay( w ), XtWindow( w ), XmRString,
                                         &textLength );

    if ( clipStat == ClipboardSuccess )
    {
        return( False );
    }
    else
    {
        return( True );
    }
}


[LISTING FIVE]

#ifndef XM_CALLBACKS_H
#define XM_CALLBACKS_H
/*****************************************************************************
 *  Include File Name: xm_callbacks.h
 *  Contents: Interface to the callbacks package.
 *  This include file is dependent on the following include file(s):
 *    None.
 *  Modification History: 11/01/90  twl     original
 */


/*~EXP*PROC********************************************************************
 *  Procedures and functions exported by this package. */  
extern void
MapDialogCB( Widget source, Widget dialog, XmAnyCallbackStruct *callbackData );

extern void
UnMapDialogCB( Widget source, Widget dialog, XmAnyCallbackStruct *callbackData );

extern void
CutCB( Widget source, Widget textID, XmAnyCallbackStruct *callbackData );

extern void
CopyCB( Widget source, Widget textID, XmAnyCallbackStruct *callbackData );

extern void
PasteCB( Widget source, Widget textID, XmAnyCallbackStruct *callbackData );

extern void
PasteFileCB( Widget source, Widget textID, XmAnyCallbackStruct *callbackData );

extern void
WriteFileCB( Widget source, Widget textID, XmAnyCallbackStruct *callbackData );
      
extern void
FileDialogOKCB( Widget source, Widget textID, XmFileSelectionBoxCallbackStruct *callbackData );

extern void
PromptDialogOKCB( Widget source, Widget textID, XmSelectionBoxCallbackStruct *callbackData );

#endif


[LISTING SIX]


#ifndef XM_CLIPBOARD_H
#define XM_CLIPBOARD_H
/*****************************************************************************
 *
 *  Include File Name: xm_clipboard.h
 *
 *  Contents:
 *    Interface to the Clipboard package.
 *
 *  This include file is dependent on the following include file(s):
 *    None.
 *
 *  Modification History:
 *    11/01/90  twl     original
 */


/*~EXP*PROC********************************************************************
 *
 *  Procedures and functions exported by this package.
 */  
extern int
CopyToClipboard( Widget textID, Time timestamp );

extern char *
RetrieveFromClipboard( Widget textID, Time timestamp );
        
extern int
ClipboardIsEmpty( Widget w );

#endif


[LISTING SEVEN]

#
# Makefile to build textedit
#

#
# Macros
# 

CC=/bin/cc 
DEBUG=-g
INCLUDE_DIRS=-I /usr/include/Xm -I /usr/include/X11
SYS_DEFS=$(SYS_T) $(RUN_T) -DSYSV
CC_SWITCHES= -c $(SYS_DEFS) $(INCLUDE_DIRS) $(DEBUG)

LD=/bin/ld
LIBDIRS=-L/usr/X11/lib
LIBS=-lXm -lXtm -lXaw -lX11
LD_SWITCHES=$(LIBDIRS) $(LIBS)
  
# 
# Inference rules
#
.SUFFIXES: .c .o .ln

.c.o:
	$(CC) $(CC_SWITCHES) $<

OBJS=\
    xm_main.o\
    xm_clipboard.o\
    xm_callbacks.o

#
# Targets
#

all:    textedit
     
textedit:  $(OBJS)
	$(LD) -o $@ $(OBJS) $(LD_SWITCHES)

xm_main.o:      xm_callbacks.h

xm_callbacks.o: xm_clipboard.h

#-------------------------
#   Misc targets
#-------------------------
clean:
	-rm *.bak *.o
               
lint:
	lint $(INCLUDE_DIRS) -DSYSV *.c