Common Desktop Environment: ToolTalk Messaging Overview

Appendix B
The CoEd Demonstration Program


Contents of Appendix:
The CoEd Ptype File
The CoEd.C File
The Coeditor.C File
This appendix contains the files and source code listing showing the ToolTalk related code for a ToolTalk demonstration program called CoEd. The CoEd demo program uses the ToolTalk Desktop Services message set. It illustrates how an editor can use the ToolTalk service to keep all changes made by the user in sync if multiple instances of the editor are editing the same file at the same time.

The CoEd Ptype File

The CoEd ptype file, shown in Code Example B-1.

Code Example B-1 CoEd Ptype File

ptype DT_CoEd {                                                 /* Process type identifier */
    start "CoEd";                                               /* Start string */
    handle:                                                     /* Receiving process */
    /*
     * Display ISO_Latin_1
     */
    session         Display( in    ISO_Latin_1 contents     ) => start opnum = 1;   /* Signature */
    session         Display( in    ISO_Latin_1 contents,
                                     in    messageID   counterfoil     ) => start opnum = 2;
    session         Display( in    ISO_Latin_1 contents,
                                     in    title       docName     ) => start opnum = 3;
    session         Display( in    ISO_Latin_1 contents,
                                     in    messageID   counterfoil,
                                     in    title       docName     ) => start opnum = 4;
    /*
     * Edit ISO_Latin_1
     */
    session             Edit(  inout ISO_Latin_1 contents     ) => start opnum = 101;
    session             Edit(  inout ISO_Latin_1 contents,
                                    in    messageID   counterfoil     ) => start opnum = 102;
    session             Edit(  inout ISO_Latin_1 contents,
                                    in    title       docName     ) => start opnum = 103;
    session             Edit(  inout ISO_Latin_1 contents,
                                    in    messageID   counterfoil,
                                    in    title       docName     ) => start opnum = 104;
    /*
     * Compose ISO_Latin_1
     */
    session             Edit(  out   ISO_Latin_1 contents     ) => start opnum = 201;
    session             Edit(  out   ISO_Latin_1 contents,
                                    in    messageID   counterfoil     ) => start opnum = 202;
    session             Edit(  out   ISO_Latin_1 contents,
                                    in    title       docName     ) => start opnum = 203;
    session             Edit(  out   ISO_Latin_1 contents,
                                    in    messageID   counterfoil,
                                    in    title       docName     ) => start opnum = 204;
    /*
     * Open an ISO_Latin_1 buffer
     */
    session             Open(  in    ISO_Latin_1 contents,
                                    out   bufferID    docBuf,
                                    in    boolean     readOnly      ) => start opnum = 400;
    session             Open(  in    ISO_Latin_1 contents,
                                    out   bufferID    docBuf,
                                    in    boolean     readOnly,
                                    in    boolean     mapped        ) => start opnum = 401;
    session             Open(  in    ISO_Latin_1 contents,
                                    out   bufferID    docBuf,
                                    in    boolean     readOnly,
                                    in    boolean     mapped,
                                    in    integer     shareLevel    ) => start opnum = 402;
    session             Open(  in    ISO_Latin_1 contents,
                                    out   bufferID    docBuf,
                                    in    boolean     readOnly,
                                    in    boolean     mapped,
                                    in    integer     shareLevel,
                                    in    locator     initialPos    ) => start opnum = 403;
};

The CoEd.C File

The CoEd.C file, shown in Code Example B-2, shows the ToolTalk code that needs to be included in every application to initialize the toolkit, join a ToolTalk session and registering patterns, and add the ToolTalk service to its event loop.


Note: This file also contains ToolTalk code that is specific to CoEd in its role as an editor application. This code includes declaring a ptype and processing the start message.

Code Example B-2 The CoEd.C File

/*
 * CoEd.cc
 *
 * Copyright (c) 1991,1993 by Sun Microsystems.
 */

#include <stdlib.h>
#include <desktop/tttk.h>                           // Include the ToolTalk messaging toolkit
#include <CoEd.h>
#include "CoEditor.h"
#include "CoEdTextBuffer.h"

XtAppContext            myContext;
Widget                  myTopWidget  = 0;
Display        *myDpy;
int         abortCode = 0;
Tt_pattern     *sessPats = 0;                                   // Patterns returned when session joined
int            timeOutFactor               = 1000;
int            maxBuffers                  = 1000;
int         *pArgc;
char        **globalArgv;

const char     *ToolName = "CoEd";
const char     *usage   =
"Usage: CoEd [-p01] [-w n] [-t n] [file]\n"
"   -p      print ToolTalk procid\n"
"   -0      do not open an initial composition window\n"
"   -1      be a single-buffer editor\n"
"   -w      sleep for n seconds before coming up\n"
"   -t      use n as timeout factor, in milliseconds (default: 1000)\n"
;

void
main(
    int    argc,
    char **argv
)
{
    static const char *here = "main()";
    int   delay   = 0;
    int   printid = 0;
    int   compose = 1;
    char *file    = 0;

    OlToolkitInitialize( 0 );
    XtToolkitInitialize();
    myContext = XtCreateApplicationContext();
    //
    // This display may get closed, and another opened, inside
    // CoEditor::_init(), if e.g. our parent is on a different screen
    //
    pArgc = &argc;
    globalArgv = argv;
    myDpy = XtOpenDisplay( myContext, 0, 0, "CoEd", 0, 0, &argc, argv );

    int c;
    while ((c = getopt( argc, argv, "p01w:t:" )) != -1) {
            switch (c) {
                case `p':
            printid = 1;
            break;
            case `0':
            compose = 0;
            break;
            case `1':
            maxBuffers = 1;
            break;
            case `w':
            delay = atoi( optarg );
            break;
            case `t':
            timeOutFactor = atoi( optarg );
            break;
            default:
            fputs( usage, stderr );
            exit( 1 );
            }
    }
    if (optind < argc) {
            file = argv[ optind ];
    }
    while (delay > 0) {
            sleep( 1 );
            delay--;
    }

    int myTtFd;                                                  // Obtain process identifier
    // Initialize toolkit and create a ToolTalk communication endpoint
    char *myProcID = ttdt_open( &myTtFd, ToolName, "SunSoft", "%I", 1 );

    // Declare ptype
    ttmedia_ptype_declare( "DT_CoEd", 0, CoEditor::loadISOLatin1_,
                   (void *)&myTopWidget, 1 );

    // Process the message that started us, if any
    tttk_Xt_input_handler( 0, 0, 0 );
    if (abortCode != 0) {
            // Error in message that caused us to start.
            exit( abortCode );
    }

    if (CoEditor::numEditors == 0) {
            // started by hand, not by ToolTalk
            if (file == 0) {
            if (compose) {
                new CoEditor( &myTopWidget );
            }
            } else {
              new CoEditor( &myTopWidget, file );
            }
    }
    //
    // If sessPats is unset, then we have not joined the desktop
    // session yet.  So join it.
    //
    if (sessPats == 0) {
            Widget session_shell = CoEditor::editors[0]->shell;
            if (maxBuffers > 1) {
                //
                // In multi-window mode, no single window is the
                // distinguished window.
                //
                session_shell = myTopWidget;
            }
            sessPats = ttdt_session_join( 0, 0, session_shell, 0, 1 );
    }

        XtAppAddInput( myContext, myTtFd, (XtPointer)XtInputReadMask,
               tttk_Xt_input_handler, myProcID );

    XtAppMainLoop( myContext );
}

The Coeditor.C File

The Coeditor.C file, shown in Code Example B-3, shows the ToolTalk code that needs to be included in every editor application to pass a media callback and reply when a request has been completed. It also shows other optional ToolTalk functions that can be included in an editor application.


Note: Ellipses (...) indicates code that has been omitted.

Code Example B-3 The CoEditor.C File

... 

CoEditor::CoEditor(
    Widget *parent
)
{
    _init();
    _init( parent );
}

CoEditor::CoEditor(
    Widget     *parent,
    const char *file
)
{
    _init();
    _init( parent );
    _load( file );
}

CoEditor::CoEditor(
    Widget        *parent,
    Tt_message    msg,
    const char     * /*docname*/,
    Tt_status      &status
)
{
    _init();
    status = _init( msg );
    if (status != TT_OK) {
            return;
    }
    _init( parent );
    status = _acceptContract( msg );
}

CoEditor::CoEditor(
    Widget        *parent,
    Tt_message    msg,
    int                         /*readOnly*/,
    const char     *file,
    const char     * /*docname*/,
    Tt_status      &status
)
{
    _init();
    status = _init( msg );
    if (status != TT_OK) {
            return;
    }
    _init( parent );
    status = _load( file );
    if (status != TT_OK) {
            return;
    }
    status = _acceptContract( msg );
}

CoEditor::CoEditor(
    Widget        *parent,
    Tt_message msg,
    int                     /*readOnly*/,
    unsigned char  *contents,
    int             /*len*/,
    const char     * /*docname*/,
    Tt_status      &status
)
{
    _init();
    status = _init( msg );
    if (status != TT_OK) {
            return;
    }
    _init( parent );
    XtVaSetValues( (Widget)_text,
            XtNsourceType,          (XtArgVal)OL_STRING_SOURCE,
            XtNsource,              (XtArgVal)contents,
            NULL );
    _textBuf  = OlTextEditTextBuffer( _text );
    RegisterTextBufferUpdate( _textBuf, CoEditor::_textUpdateCB_,
                  (caddr_t)this );
    status = _acceptContract( msg );
}

CoEditor::~CoEditor()
{
    //
    // No need for a separate save if we are sending the document
    // back in a reply.
    //
    if (_contract == 0) {
            if (_modifiedByMe) {
            // we revert before quitting if we don't want to save
            _save();
            }
    } else {
            int   len;
            char *contents = _contents( &len );
            // Reply to media load callback with edited contents of text
            ttmedia_load_reply( _contract, (unsigned char *)contents,
                    len, 1 );
            if (contents != 0) {
            XtFree( contents );
            }
        _contract = 0;
    }
    numEditors--; // XXX assumes user destroys windows LIFO!
}

Tt_message
CoEditor::loadISOLatin1_(
    Tt_message          msg,
    Tttk_op             op,
    Tt_status           diagnosis,
    unsigned char  *contents,
    int             len,
    char        *file,
    char        *docname,
    void        *pWidget
)
{
    static const char *here = "CoEditor::loadISOLatin1_()";

    Tt_status status   = TT_OK; 
    CoEditor *coEditor = 0;
    if (diagnosis != TT_OK) {
            // toolkit detected an error
            if (tt_message_status( msg ) == TT_WRN_START_MESSAGE) { 
            //
            // Error is in start message!  We now have no
            // reason to live, so tell main() to exit().
            //
            abortCode = 2;
            }
            // let toolkit handle the error
            return msg;
    }
    if ((op == TTME_COMPOSE) && (file == 0)) {
            coEditor = new CoEditor( (Widget *)pWidget, msg, docname,
                     status );
    } else if (len > 0) {
            coEditor = new CoEditor( (Widget *)pWidget, msg,
                     (op == TTME_DISPLAY),
                     contents, len, docname, status );
    } else if (file != 0) {
            coEditor = new CoEditor( (Widget *)pWidget, msg,
                     (op == TTME_DISPLAY),
                     file, docname, status );
    } else {
            // Fail a message
            tttk_message_fail( msg, TT_DESKTOP_ENODATA, 0, 1 );
    }
    tt_free( (caddr_t)contents );
    tt_free( file );
    tt_free( docname );
    return 0;
}

void
CoEditor::_init()
{
    _baseFrame          = 0;
    _controls           = 0;
    _fileBut            = 0;
    _editBut            = 0;
    _scrolledWin        = 0;
    _text               = 0;
    _textBuf            = 0;
    _modifiedByMe       = FALSE;
    _modifiedByOther    = 0;
    _contract           = 0;
    _contractPats       = 0;
    _filePats           = 0;
    _file               = 0;
    _x      = INT_MAX;
    _y      = INT_MAX;
    _w      = INT_MAX;
    _h      = INT_MAX;
}

Tt_status
CoEditor::_init(
    Tt_message msg
)
{
    int width, height, xOffset, yOffset;
    width = height = xOffset = yOffset = INT_MAX;
    _contract = msg;
    ttdt_sender_imprint_on( 0, msg, 0, &_w, &_h, &_x, &_y,
                10 * timeOutFactor );
    return TT_OK;
}

typedef enum {
    Open,
    Save,
    SaveAs,
    Revert
} FileOp;

static const char *fileButs[] = {
    "Open...",
    "Save",
    "Save as...",
    "Revert"
};

const int numFileButs = sizeof( fileButs ) / sizeof( const char * );

typedef enum {
    Undo,
    Cut,
    Copy,
    Paste,
    Delete,
    SelText,
    SelAppt
} EditOp;

static const char *editButs[] = {
    "Undo",
    "Cut",
    "Copy",
    "Paste",
    "Delete",
    "Text as ISO_Latin_1",
    "Text as Appointment"
};

const int numEditButs = sizeof( editButs ) / sizeof( const char * );

void
CoEditor::_init(
    Widget *parent
)
{
    if (*parent == 0) {
            if (_contract != 0) {
            //
            // Re-open display, since $DISPLAY may have changed by
            // ttdt_sender_imprint_on().
            //
            XtCloseDisplay( myDpy );
            myDpy = XtOpenDisplay( myContext, 0, 0, "CoEd", 0, 0, 
                           pArgc, globalArgv );
        }
        *parent = XtAppCreateShell( 0, "CoEd",
                applicationShellWidgetClass, myDpy, 0, 0 );
            XtVaSetValues( *parent,
                   XtNmappedWhenManaged, False,
                   XtNheight, 1,
                   XtNwidth, 1,
                   0 );
            XtRealizeWidget( *parent );
    }
    shell = XtCreatePopupShell( "CoEd",
                applicationShellWidgetClass, *parent, 0, 0 );
    XtVaSetValues( shell, XtNuserData, this, 0 );
    // Pop up next to our parent
    if ((_x != INT_MAX) && (_y != INT_MAX) && (_w != INT_MAX)) {
            // XXX Be smarter about picking a geometry
            Dimension x = _x + _w;
            Dimension y = _y;
            XtVaSetValues( shell, XtNx, x, XtNy, y, 0 );
    }
    XtAddCallback( shell, XtNdestroyCallback, CoEditor::_destroyCB_,
               this );
    OlAddCallback( shell, XtNwmProtocol, CoEditor::_wmProtocolCB_, this );
    _baseFrame = XtVaCreateManagedWidget(
                "baseFrame", rubberTileWidgetClass, shell, 0 );
    _controls = XtVaCreateManagedWidget( "controls",
                controlAreaWidgetClass, _baseFrame,
                XtNweight, (XtArgVal)0,
                0 );
    _fileBut = XtVaCreateManagedWidget( "File",
                menuButtonWidgetClass, _controls, 0 );
    Widget menuPane;
    XtVaGetValues( _fileBut, XtNmenuPane, &menuPane, 0 );
    for (int i = 0; i < numFileButs; i++) {
            Widget but = XtVaCreateManagedWidget( fileButs[i],
                oblongButtonWidgetClass, menuPane,
                XtNuserData, i, 0 );
            XtAddCallback( but, XtNselect, CoEditor::_fileButsCB_, this );
    }
    _editBut = XtVaCreateManagedWidget( "Edit",
                menuButtonWidgetClass, _controls, 0 );
    XtVaGetValues( _editBut, XtNmenuPane, &menuPane, 0 );
    for (i = 0; i < numEditButs; i++) {
            Widget but = XtVaCreateManagedWidget( editButs[i],
                oblongButtonWidgetClass, menuPane,
                XtNuserData, i, 0 );
            XtAddCallback( but, XtNselect, CoEditor::_editButsCB_, this );
    }

    _scrolledWin = XtVaCreateManagedWidget(
                "scrolledWin", scrolledWindowWidgetClass,
                _baseFrame,
                XtNforceVerticalSB,(XtArgVal)True,
                0 );
    _text = (TextEditWidget)XtVaCreateManagedWidget(
                "text", textEditWidgetClass, _scrolledWin,
                0 );
    XtVaSetValues( (Widget)_text, XtNuserData, this, 0 );

    XtRealizeWidget( shell );
    XtPopup( shell, XtGrabNone );
    if (numEditors < MaxEditors) {
            editors[ numEditors ] = this;
            numEditors++;
    }
    if (numEditors >= maxBuffers) {
            tt_ptype_undeclare( "DT_CoEd" );
    }
}

Tt_status
CoEditor::_unload()
{
    Tt_status status = TT_OK;
    if (_filePats != 0) {
            // Unregister interest in ToolTalk events and destroy patterns
            status = ttdt_file_quit( _filePats, 1 );
            _filePats = 0;
    }
    if (_file != 0) {
            free( _file );
            _file = 0;
    }
    return status;
}

Tt_status
CoEditor::_load(
    const char *file
)
{
    int reloading = 1;
    if (file != 0) {
            if ((_file != 0) && (strcmp( file, _file ) != 0)) {
            reloading = 0;
            _unload();
            } else {
            _file = strdup( file );
            }
    }
        // Join a file Can be called recursively, below
    if (_filePats == 0) {
            _filePats = ttdt_file_join( _file, TT_SCOPE_NONE, 1,
                        CoEditor::_fileCB_, this );
    }
    XtVaSetValues( (Widget)_text,
            XtNsourceType,                  (XtArgVal)OL_DISK_SOURCE,
            XtNsource,                      (XtArgVal)_file,
            NULL );
    _textBuf  = OlTextEditTextBuffer( _text );
    RegisterTextBufferUpdate( _textBuf, CoEditor::_textUpdateCB_,
                  (caddr_t)this );
    if (_modifiedByMe && reloading) {
            ttdt_file_event( _contract, TTDT_REVERTED, _filePats, 1 );
    }
    _modifiedByMe = 0;
        // Does the file have any changes pending?
    _modifiedByOther = ttdt_Get_Modified( _contract, _file, TT_BOTH,
                          10 * timeOutFactor );
    if (_modifiedByOther) {
            int choice = userChoice( myContext, _baseFrame,
                "Another tool has modifications pending for "
                "this file.\nDo you want to ask it to save "
                "or revert the file?", 3, "Save", "Revert",
                "Ignore" );
            Tt_status status = TT_OK;
        switch (choice) {
            case 0:
            // Save pending changes
            status = ttdt_Save( _contract, _file, TT_BOTH,
                        10 * timeOutFactor );
            break;
            case 1:
            // Revert file to last version
            status = ttdt_Revert( _contract, _file, TT_BOTH,
                        10 * timeOutFactor );
            break;
            }
            if (status != TT_OK) {
            char *s = tt_status_message( status );
            userChoice( myContext, _baseFrame, s, 1, "Okay" );
            tt_free( s );
            } else if (choice == 0) {
            // file was saved, so reload
            return _load( 0 );
            } else if (choice == 1) {
            // file was reverted
            _modifiedByOther = 0;
            }
    }
    return TT_OK;
}

Tt_status
CoEditor::_load(
    unsigned char  *contents,
    int            //len
)
{
    _unload();
    XtVaSetValues( (Widget)_text,
            XtNsourceType,                  (XtArgVal)OL_DISK_SOURCE,
            XtNsource,                      (XtArgVal)contents,
            NULL );
    _textBuf  = OlTextEditTextBuffer( _text );
    RegisterTextBufferUpdate( _textBuf, CoEditor::_textUpdateCB_,
                  (caddr_t)this );
    _modifiedByMe = 0;
    _modifiedByOther = 0;
    return TT_OK;
}

//
// Caller responsible for reporting any errors to user
//
Tt_status
CoEditor::_save()
{
    Tt_status status;
    if (_file != 0) {
            if (SaveTextBuffer( _textBuf, _file ) != SAVE_SUCCESS) {
                return TT_DESKTOP_EIO;
            }
            _modifiedByMe = 0;
            _modifiedByOther = 0;
            // File has been saved
            ttdt_file_event( _contract, TTDT_SAVED, _filePats, 1 );
    }
    if (_contract != 0) {
            int   len = 0;
            char *contents = 0;
            if (_file == 0) {
            // If you worry that the buffer might be big,
            // you could instead try a a temp file to
            // transfer the data "out of band".
            contents = _contents( &len );
            }
            status = ttmedia_Deposit( _contract, 0, "ISO_Latin_1",
                      (unsigned char *)contents,
                      len, _file, 10 * timeOutFactor );
            if (status != TT_OK) {
            return status;
            }
            _modifiedByMe = 0;
            _modifiedByOther = 0;
        if (contents != 0) {
                XtFree( contents );
            }
    }
    return status;
}

Tt_status
CoEditor::_revert() // XXX how about we always just send Revert? :-)
{
    if (! _modifiedByMe) {
            return TT_OK;
    }
    return _load( 0 ); // XXX what if it's not a file? keep last deposit
}

void
CoEditor::_destroyCB_(
    Widget    w,
    XtPointer coEditor,
    XtPointer call_data
)
{
    ((CoEditor *)coEditor)->_destroyCB( w, call_data );
}

void
CoEditor::_destroyCB(
    Widget    ,
    XtPointer //call_data
)
{
    delete this;
}

void
CoEditor::_wmProtocolCB_(
    Widget    w,
    XtPointer coEditor,
    XtPointer wmMsg
)
{
    ((CoEditor *)coEditor)->_wmProtocolCB( w, (OlWMProtocolVerify*)wmMsg );
}

void
CoEditor::_wmProtocolCB(
    Widget     w,
    OlWMProtocolVerify     *wmMsg
)
{
    switch (wmMsg->msgtype) {
        case OL_WM_DELETE_WINDOW:
            if (_modifiedByMe) {
            int choice =
                userChoice( myContext, _baseFrame,
                        "The text has unsaved changes.",
                        3, "Save, then Quit",
                        "Discard, then Quit",
                        "Cancel" );
            switch (choice) {
                case 0:
                break;
                case 1:
                _revert();
                break;
                case 2:
                return;
            }
            }
            if (numEditors > 1) {
            XtDestroyWidget( shell );
            } else {
            // XXX OlWmProtocolAction() doesn't call destructor?!
            delete this;
            OlWMProtocolAction( w, wmMsg, OL_DEFAULTACTION );
            }
            break;
        default:
            OlWMProtocolAction( w, wmMsg, OL_DEFAULTACTION );
            break;
    }
}

void
CoEditor::_fileButsCB_(
    Widget    button,
    XtPointer coEditor,
    XtPointer call_data
)
{
    ((CoEditor *)coEditor)->_fileButsCB( button, call_data );
}

void
CoEditor::_fileButsCB(
    Widget    button,
    XtPointer //call_data
)
{
    FileOp op;
    XtVaGetValues( button, XtNuserData, &op, 0 );
    Tt_status status = TT_OK;
    switch (op) {
        case Open:
            break;
        case Revert:
            status =_revert();
            break;
        case Save:
            status =_save();
            break;
        case SaveAs: 
            break;
    }
    if (status != TT_OK) {
            _adviseUser( status );
    }
}

void
CoEditor::_editButsCB_(
    Widget    button,
    XtPointer coEditor,
    XtPointer call_data
)
{
    ((CoEditor *)coEditor)->_editButsCB( button, call_data );
}

void
CoEditor::_editButsCB(
    Widget    button,
    XtPointer //call_data
)
{
    EditOp op;
    XtVaGetValues( button, XtNuserData, &op, 0 );
    Tt_status status = TT_OK;
    switch (op) {
            int len;
            char        *contents;
            const char     *mediaType;
            Tt_message msg;
            Tt_pattern     *pats;
        case SelText:
        case SelAppt:
            if (op == SelText) {
                mediaType = "ISO_Latin_1";
            } else {
            mediaType = "DT_CM_Appointment";
            }
            //contents = _selection( &len );
            contents = _contents( &len );
            if (len <= 0) {
            return;
            }
            // Media load callback
            msg = ttmedia_load( _contract, CoEditor::_mediaLoadMsgCB_,
                    this, TTME_EDIT, mediaType,
                    (unsigned char *)contents, len, 0, 0, 1 );
            if (contents != 0) {
            XtFree( contents );
            }
            status = tt_ptr_error( msg );
            if (status != TT_OK) {
            break;
            }
            pats = ttdt_subcontract_manage( msg, 0, shell, this );
            status = tt_ptr_error( pats );
            if (status != TT_OK) {
            break;
            }
            break;
    }
    if (status != TT_OK) {
            char *s = tt_status_message( status );
            char buf[ 1024 ];
            sprintf( buf, "%d: %s", status, s );
            tt_free( s );
            userChoice( myContext, _baseFrame, buf, 1, "Okay" );
    }
}

char *
CoEditor::_contents(
    int *len
)
{
    _textBuf  = OlTextEditTextBuffer( _text );
    TextLocation  start    = { 0, 0, 0 };
    TextLocation  end      = LastTextBufferLocation( _textBuf );
    char         *contents = GetTextBufferBlock( _textBuf, start, end );

    *len = 0;
    if (contents != 0) {
            *len = strlen( contents );
    }
    return contents;
}

Tt_status
CoEditor::_acceptContract(
    Tt_message msg
)
{
    static const char *here = "CoEditor::_acceptContract()";

    _contract = msg;
    if (tt_message_status( msg ) == TT_WRN_START_MESSAGE) {
            //
            // Join session before accepting start message,
            // to prevent unnecessary starts of our ptype
            //
            Widget session_shell = shell;
            if (maxBuffers > 1) {
            //
            // If we are in multi-window mode, just use
            // our unmapped toplevel shell as our session
            // shell, since we do not know if any particular
            // window will exist the whole time we are in
            // the session.
            //
            session_shell = XtParent(shell );
            }
            // Join the session and register patterns and callbacks
            sessPats = ttdt_session_join( 0, 0, session_shell, this, 1 );
    }
        // Accept responsibility to handle a request
    _contractPats = ttdt_message_accept(
                msg, CoEditor::_contractCB_, shell, this,
                1, 1 );
    Tt_status status = tt_ptr_error( _contractPats );
    if (status != TT_OK) {
            return status;
    }
    return status;
}

Tt_message
CoEditor::_contractCB_(
    Tt_message,                                 //msg,
    Tttk_op,                                    //op,
    Widget,                                     //shell,
    void        *,                              //coEditor,
    Tt_message                                  //Contract
)
{
    return 0;
}

void
CoEditor::_editButCB_(
    Widget    w,
    XtPointer coEditor,
    XtPointer call_data
)
{
    ((CoEditor *)coEditor)->_editButCB( w, call_data );
}


void
CoEditor::_editButCB(
    Widget    ,
    XtPointer //call_data
)
{
    int         len;
    char   *contents = _contents( &len );
        // Media Load Callback
    Tt_message msg = ttmedia_load( _contract, CoEditor::_mediaLoadMsgCB_,
                       this, TTME_EDIT, "ISO_Latin_1",
                       (unsigned char *)contents,
                       len, 0, 0, 1 );
    if (contents != 0) {
            XtFree( contents );
    }
    Tt_pattern *pats = ttdt_subcontract_manage( msg, 0, shell, this );
}


Tt_message
CoEditor::_mediaLoadMsgCB_(
    Tt_message                  msg,
    Tttk_op                     op,
    unsigned char  *contents,
    int             len,
    char           *file,
    void           *clientData
)
{
    return ((CoEditor *)clientData)->_mediaLoadMsgCB( msg, op,
                        contents, len, file );
}

Tt_message
CoEditor::_mediaLoadMsgCB(
    Tt_message                  msg,
    Tttk_op,
    unsigned char  *contents,
    int                         len,
    char           *file
)
{
    if (len > 0) {
            XtVaSetValues( (Widget)_text,
            XtNsourceType,                  (XtArgVal)OL_STRING_SOURCE,
            XtNsource,             (XtArgVal)contents,
            NULL );
            _textBuf  = OlTextEditTextBuffer( _text );
            RegisterTextBufferUpdate( _textBuf, CoEditor::_textUpdateCB_,
                      (caddr_t)this );
            // ReplaceBlockInTextBuffer
    } else if (file != 0) {
    }
    tt_message_destroy( msg );
    return 0;
}

void
CoEditor::_textUpdateCB_(
    XtPointer           coEditor,
    XtPointer           pTextBuffer,
    EditResult          status
)
{
    if (coEditor == 0) {
            return;
    }
    ((CoEditor *)coEditor)->_textUpdateCB( (TextBuffer *)pTextBuffer,
                           status );
}

void
CoEditor::_textUpdateCB(
    TextBuffer     *textBuf,
    EditResult                  //editStatus
)
{
    //Tt_status status;
    if (_textBuf != textBuf) {
            fprintf( stderr, "_textBuf != textBuf" );
    }
    if ((! _modifiedByMe) && TextBufferModified( _textBuf )) {
            _modifiedByMe = TRUE;
            // File has changes pending
            ttdt_file_event( _contract, TTDT_MODIFIED, _filePats, 1 );
    }
}

Tt_message
CoEditor::_fileCB_(
    Tt_message                  msg,
    Tttk_op                     op,
    char           *pathname,
    void           *coEditor,
    int                 trust,
    int                 me
)
{
    tt_free( pathname );
    if (coEditor == 0) {
            return msg;
    }
    return ((CoEditor *)coEditor)->_fileCB( msg, op, pathname,
                        trust, me );
}

Tt_message
CoEditor::_fileCB(
    Tt_message                  msg,
    Tttk_op                     op,
    char        *pathname,
    int,                            //trust
    int                             //me
)
{
    tt_free( pathname );
    Tt_status status = TT_OK;
    switch (op) {
        case TTDT_MODIFIED:
            if (_modifiedByMe) {
            // Hmm, the other editor either doesn't know or
            // doesn't care that we are already modifying the
            // file, so the last saver will win.
            // XXX Or: a race condition has arisen!
            } else {
            // Interrogate user if she ever modifies the buffer
            _modifiedByOther = 1;
            XtAddCallback( (Widget)_text, XtNmodifyVerification,
                (XtCallbackProc)CoEditor::_textModifyCB_, 0 );
            }
            break;
        case TTDT_GET_MODIFIED:
            tt_message_arg_ival_set( msg, 1, _modifiedByMe );
            tt_message_reply( msg );
            break;
        case TTDT_SAVE:
            status = _save();
            if (status == TT_OK) {
            tt_message_reply( msg );
            } else {
            // Fail message
            tttk_message_fail( msg, status, 0, 0 );
            }
            break;
        case TTDT_REVERT:
            status = _revert();
            if (status == TT_OK) {
            tt_message_reply( msg );
            } else {
            // Fail message
            tttk_message_fail( msg, status, 0, 0 );
            }
            break;
        case TTDT_REVERTED:
        case TTDT_SAVED:
        case TTDT_MOVED:
        case TTDT_DELETED:
            printf( "CoEditor::_fileCB(): %s\n", tttk_op_string( op ));
            break;
    }
    tt_message_destroy( msg );
    return 0;
}

void
CoEditor::_textModifyCB_(
    TextEditWidget                  text,
    XtPointer                       ,
    OlTextModifyCallData *mod
)
{
    CoEditor *coEditor = 0;
    XtVaGetValues( (Widget)text, XtNuserData, &coEditor, 0 );
    if (coEditor == 0) {
            return;
    }
    coEditor->_textModifyCB( mod );
}

void
CoEditor::_textModifyCB(
    OlTextModifyCallData *mod
)
{
    if (_modifiedByOther != 1) {
            return;
    }
    int cancel = userChoice( myContext, _baseFrame,
               "Another tool has modifications pending for this file.\n"
               "Are you sure you want to start modifying the file?",
               2, "Modify", "Cancel" );
    if (cancel) {
            mod->ok = FALSE;
    }
    _modifiedByOther = 2;
}

void
CoEditor::_adviseUser(
    Tt_status status
)
{
    char *s = tt_status_message( status );
    char buf[ 1024 ];
    sprintf( buf, "%d: %s", status, s );
    tt_free( s );
    userChoice( myContext, _baseFrame, buf, 1, "Okay" );
}



Generated with CERN WebMaker