Files
cdesktop/cde/lib/tt/demo/CoEd/CoEd/CoEditor.C
2018-04-28 12:30:20 -06:00

1463 lines
33 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
//%% (c) Copyright 1993, 1994 Hewlett-Packard Company
//%% (c) Copyright 1993, 1994 International Business Machines Corp.
//%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
//%% (c) Copyright 1993, 1994 Novell, Inc.
//%% $TOG: CoEditor.C /main/4 1998/05/18 16:41:01 rafi $
/*
* CoEditor.C 1.14 94/11/17
*
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
*/
#include "CoEditor.h"
#include <stdarg.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <Xm/CascadeB.h>
#include <Xm/DialogS.h>
#include <Xm/Label.h>
#include <Xm/PanedW.h>
#include <Xm/PushB.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/AtomMgr.h>
#include <Xm/Protocols.h>
extern const char *ToolName;
extern int abortCode;
extern int timeOutFactor;
extern Tt_pattern *sessPats;
extern Display *myDpy;
extern char *myAltMediaType;
extern XtAppContext myContext;
extern int maxBuffers;
extern int *pArgc;
extern char **globalArgv;
const int MaxEditors = 100;
CoEditor *CoEditor::editors[ MaxEditors ];
int CoEditor::numEditors = 0;
static void
userChoiceCB(
Widget but,
XtPointer pChoice,
XtPointer //callData
)
{
XtVaGetValues( but, XmNuserData, pChoice, 0 );
}
#define TIGHTNESS 20
static int
userChoice(
XtAppContext context,
Widget parent,
const char *explanation,
unsigned int numChoices,
...
)
{
const char *choiceString;
Dimension bh, bw;
Position bx, by;
Widget action_area, but, label, notice, pane, rc;
XmString label_str;
XtVaGetValues(parent,
XmNx, &bx,
XmNy, &by,
XmNwidth, &bw,
XmNheight, &bh,
0);
notice = XtVaCreatePopupShell( "notice",
xmDialogShellWidgetClass, parent,
XmNtitle, "Message",
XmNdeleteResponse, XmDESTROY,
XmNx, bx,
XmNy, by+(bh/2),
0 );
pane = XtVaCreateWidget("pane", xmPanedWindowWidgetClass, notice,
XmNsashWidth , 1,
XmNsashHeight, 1,
0 );
rc = XtVaCreateWidget("control_area", xmRowColumnWidgetClass, pane, 0 );
label_str = XmStringCreate((char *) explanation,
XmFONTLIST_DEFAULT_TAG);
label = XtVaCreateManagedWidget("label", xmLabelWidgetClass, rc,
XmNlabelString, label_str,
XmNleftAttachment, XmATTACH_FORM,
XmNtopAttachment, XmATTACH_FORM,
0 );
XtVaSetValues(label, XmNlabelString, label_str, 0);
XmStringFree(label_str);
XtManageChild(rc);
action_area = XtVaCreateWidget("action_area", xmFormWidgetClass, pane,
XmNfractionBase, TIGHTNESS*numChoices - 1,
0 );
va_list args;
va_start( args, numChoices );
int choice = -1;
for (int i = 0; i < numChoices; i++) {
choiceString = va_arg( args, const char * );
but = XtVaCreateManagedWidget( choiceString,
xmPushButtonWidgetClass, action_area,
XmNleftAttachment, i ? XmATTACH_POSITION : XmATTACH_FORM,
XmNleftPosition, TIGHTNESS*i,
XmNtopAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_FORM,
XmNrightAttachment,
i != numChoices-1 ? XmATTACH_POSITION : XmATTACH_FORM,
XmNrightPosition, TIGHTNESS*i + (TIGHTNESS-1),
XmNshowAsDefault, i == 0,
XmNdefaultButtonShadowThickness, 1,
XmNuserData, i,
0 );
XtAddCallback( but, XmNactivateCallback, userChoiceCB, &choice );
}
XtManageChild(action_area);
XtManageChild(pane);
XtPopup( notice, XtGrabExclusive );
while (choice < 0) {
XEvent event;
XtAppNextEvent( context, &event );
XtDispatchEvent( &event );
}
XtDestroyWidget(notice);
return choice;
}
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 );
XmTextSetString(_text, (char *) contents);
XtAddCallback(_text, XmNvalueChangedCallback,
(XtCallbackProc) 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 );
ttmedia_load_reply( _contract, (unsigned char *)contents,
len, 1 );
if (contents != 0) {
XtFree( contents );
}
_contract = 0;
}
numEditors--;
for (int i = 0; i < MaxEditors; i++) {
if (editors[i] == this) {
editors[i] = 0;
}
}
}
Tt_message
CoEditor::loadISOLatin1_(
Tt_message msg,
void *pWidget,
Tttk_op op,
Tt_status diagnosis,
unsigned char *contents,
int len,
char *file,
char *docname
)
{
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 {
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;
_text = 0;
_modifiedByMe = FALSE;
_modifiedByOther= 0;
_contract = 0;
_subContract = 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,
myContext, 10 * timeOutFactor );
return TT_OK;
}
typedef enum {
Open,
Save,
SaveAs,
Revert,
QuitChildren
} FileOp;
static const char *fileButs[] = {
"Open...",
"Save",
"Save as...",
"Revert",
"Quit Children"
};
const int numFileButs = sizeof( fileButs ) / sizeof( const char * );
typedef enum {
Undo,
Cut,
Copy,
Paste,
Delete,
SelText
} EditOp;
static const char *editButs[] = {
"Undo",
"Cut",
"Copy",
"Paste",
"Delete",
"Text as alternate media type",
};
const int numEditButs = sizeof( editButs ) / sizeof( const char * );
void
CoEditor::_init(
Widget *parent
)
{
Arg args[20];
Atom WM_DELETE_WINDOW;
XmString label_str;
int n;
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, XmNuserData, 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 );
WM_DELETE_WINDOW = XmInternAtom( XtDisplay(shell),
"WM_DELETE_WINDOW", False );
XmAddWMProtocolCallback( shell, WM_DELETE_WINDOW,
(XtCallbackProc) CoEditor::_wmProtocolCB_, this );
XtVaSetValues( shell, XmNdeleteResponse, XmDO_NOTHING, 0 );
_baseFrame = XtVaCreateManagedWidget(
"baseFrame", xmFormWidgetClass, shell, 0 );
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 5); n++;
XtSetArg(args[n], XmNrightOffset, 5); n++;
XtSetArg(args[n], XmNtopOffset, 5); n++;
_controls = XmCreateMenuBar(_baseFrame, "controls", args, n );
_fileBut = XmCreatePulldownMenu(_controls, "File", NULL, 0 );
label_str = XmStringCreateSimple("File");
XtVaCreateManagedWidget("File", xmCascadeButtonWidgetClass, _controls,
XmNlabelString, label_str,
XmNmnemonic, 'F',
XmNsubMenuId, _fileBut,
0);
XmStringFree(label_str);
for (int i = 0; i < numFileButs; i++) {
Widget but = XtVaCreateManagedWidget( fileButs[i],
xmPushButtonWidgetClass, _fileBut,
XmNuserData, i, 0 );
XtAddCallback( but, XmNactivateCallback,
CoEditor::_fileButsCB_, this );
}
_editBut = XmCreatePulldownMenu( _controls, "Edit", NULL, 0);
label_str = XmStringCreateSimple("Edit");
XtVaCreateManagedWidget("Edit", xmCascadeButtonWidgetClass, _controls,
XmNlabelString, label_str,
XmNmnemonic, 'E',
XmNsubMenuId, _editBut,
0);
for (i = 0; i < numEditButs; i++) {
Widget but = XtVaCreateManagedWidget( editButs[i],
xmPushButtonWidgetClass, _editBut,
XmNuserData, i, 0 );
XtAddCallback( but, XmNactivateCallback,
CoEditor::_editButsCB_, this );
}
XtManageChild(_controls);
n = 0;
XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
XtSetArg(args[n], XmNresizable, True); n++;
XtSetArg(args[n], XmNuserData, this); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, _controls); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 5); n++;
XtSetArg(args[n], XmNrightOffset, 5); n++;
XtSetArg(args[n], XmNtopOffset, 5); n++;
XtSetArg(args[n], XmNbottomOffset, 5); n++;
XtSetArg(args[n], XmNrows, 24); n++;
XtSetArg(args[n], XmNcolumns, 80); n++;
_text = XmCreateScrolledText(_baseFrame, "text", args, n);
XtManageChild(_text);
XtRealizeWidget( shell );
XtPopup( shell, XtGrabNone );
if (numEditors < MaxEditors) {
for (int i = 0; i < MaxEditors; i++) {
if (editors[i] == 0) {
editors[i] = this;
break;
}
}
numEditors++;
}
if (numEditors >= maxBuffers) {
tt_ptype_undeclare( "Sun_CoEd" );
}
}
Tt_status
CoEditor::_unload()
{
Tt_status status = TT_OK;
if (_filePats != 0) {
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 );
}
}
// Can be called recursively, below
if (_filePats == 0) {
_filePats = ttdt_file_join( _file, TT_SCOPE_NONE, 1,
CoEditor::_fileCB_, this );
}
_read_file(_text, _file);
XtAddCallback(_text, XmNvalueChangedCallback,
(XtCallbackProc) CoEditor::_textUpdateCB_, (caddr_t)this );
if (_modifiedByMe && reloading) {
ttdt_file_event( _contract, TTDT_REVERTED, _filePats, 1 );
}
_modifiedByMe = 0;
XtSetSensitive( shell, False );
_modifiedByOther = ttdt_Get_Modified( _contract, _file, TT_BOTH,
myContext, 10 * timeOutFactor );
XtSetSensitive( shell, True );
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:
status = ttdt_Save( _contract, _file, TT_BOTH,
myContext, 10 * timeOutFactor );
break;
case 1:
status = ttdt_Revert( _contract, _file, TT_BOTH,
myContext, 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;
}
// Read in the contents of a file to the text widget.
//
// XXX: Needs to be made more robust, and handle large files.
Tt_status
CoEditor::_read_file(
Widget widget,
char *file
)
{
char buf[BUFSIZ], *text;
struct stat statb;
int len;
FILE *fp;
Tt_status status;
// Make sure the file is a regular text file and open it.
if (stat(file, &statb) == -1 || (statb.st_mode & S_IFMT) != S_IFREG ||
!(fp = fopen(file, "r"))) {
perror(file);
return TT_ERR_FILE;
}
// Put the contents of the file in the Text widget by allocating enough
// space for the entire file, reading the file into the space, and using
// XmTextSetString() to show the file.
len = (int) statb.st_size;
if (!(text = XtMalloc((unsigned)(len+1)))) {
sprintf(buf, "%s: XtMalloc(%ld) failed", file, len);
XmTextSetString(widget, buf);
} else {
if (fread(text, sizeof(char), len, fp) != len) {
status = TT_ERR_FILE;
} else {
status = TT_OK;
text[len] = 0;
XmTextSetString(widget, text);
}
}
XtFree(text);
fclose(fp);
return status;
}
// Write the contents of a text widget to a file.
Tt_status
CoEditor::_write_file(
Widget widget,
char *file
)
{
char *text;
int len;
FILE *fp;
Tt_status status;
if (!(fp = fopen(file, "w"))) {
perror(file);
return TT_ERR_FILE;
}
// Saving -- get text from the text widget.
text = XmTextGetString(widget);
len = (int) XmTextGetLastPosition(widget);
// Write it to file (check for error).
if (fwrite(text, sizeof(char), len, fp) != len) {
status = TT_ERR_FILE;
} else {
status = TT_OK;
// Make sure a newline terminates file.
if (text[len-1] != '\n') {
fputc('\n', fp);
}
}
fclose(fp);
return status;
}
Tt_status
CoEditor::_load(
unsigned char *contents,
int //len
)
{
_unload();
_read_file(_text, (char *) contents);
XtAddCallback(_text, XmNvalueChangedCallback,
(XtCallbackProc) 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 (_write_file(_text, _file) != TT_OK) {
return TT_DESKTOP_EIO;
}
_modifiedByMe = 0;
_modifiedByOther = 0;
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, myAltMediaType,
(unsigned char *)contents,
len, _file, myContext,
10 * timeOutFactor );
if (status != TT_OK) {
return status;
}
_modifiedByMe = 0;
_modifiedByOther = 0;
if (contents != 0) {
XtFree( contents );
}
}
return status;
}
Tt_status
CoEditor::_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
)
{
// No, we delete this whenever we destroy our shell.
// We hope that Xt does not call us
//delete this;
}
static Tt_callback_action
_ttdtQuitCB(
Tt_message msg,
Tt_pattern
)
{
Tt_status replyStatus;
switch (tt_message_state(msg)) {
case TT_HANDLED:
// The Quit occurred.
replyStatus = TT_OK;
break;
case TT_FAILED:
// The Quit did not occur. Find out why.
replyStatus = (Tt_status)tt_message_status( msg );
if (replyStatus == TT_OK) {
// No reason given! Assume canceled.
replyStatus = TT_DESKTOP_ECANCELED;
}
break;
default:
// Not in final state; our address space is probably handler
return TT_CALLBACK_CONTINUE;
}
// Wind down the counter so that tttk_block_while() will return.
int *pWaiting = (int *)tt_message_user( msg, 0 );
*pWaiting = 0;
// Save the reply status.
tt_message_user_set( msg, 1, (void *)replyStatus );
return TT_CALLBACK_PROCESSED;
}
static Tt_status
ttdt_Quit(
Tt_message toQuit,
const char *handler,
int silent,
int force,
XtAppContext context,
int ms_timeout
)
{
Tt_message msg = tttk_message_create( toQuit, TT_REQUEST, TT_SESSION,
handler, "Quit", _ttdtQuitCB );
tt_message_iarg_add( msg, TT_IN, Tttk_boolean, silent );
tt_message_iarg_add( msg, TT_IN, Tttk_boolean, force );
char *id = tt_message_id( toQuit );
tt_message_arg_add( msg, TT_IN, Tttk_message_id, id );
tt_free( id );
Tt_status status = tt_message_send( msg );
if (status != TT_OK) {
tt_message_destroy( msg );
return status;
}
int waiting = 1;
tt_message_user_set( msg, 0, &waiting );
status = tttk_block_while( context, &waiting, ms_timeout );
if (status != TT_OK) {
tttk_message_destroy( msg );
return status;
}
//
// _ttdtQuitCB() stored the reply status under key 1
//
void *replyStatus = tt_message_user( msg, 1 );
status = tt_ptr_error( replyStatus );
if (status != TT_OK) {
return status;
}
return (Tt_status)replyStatus;
}
//
// Returns:
// 0 canceled
// 1 destructor has been called!
//
int
CoEditor::_quit(
int silent,
int force
)
{
int choice = 0;
if (_modifiedByMe && (! silent)) {
if (force) {
choice = userChoice( myContext, _baseFrame,
"The text has unsaved changes.",
2, "Save, then Quit",
"Discard, then Quit" );
} else {
choice = userChoice( myContext, _baseFrame,
"The text has unsaved changes.",
3, "Save, then Quit",
"Discard, then Quit",
"Cancel" );
}
}
Tt_status status;
char *subContractor;
if (_subContract != 0) {
subContractor = (char *)tt_message_user( _subContract, 0 );
}
switch (choice) {
case 0: // "Save, then Quit"
if (_subContract == 0) {
// Destructor will _save() and reply to _contract
break;
}
// Synchronously quit outstanding actions
status = ttdt_Quit( _subContract, subContractor,
silent, force, myContext,
60 * timeOutFactor );
if ((! force) && (status == TT_DESKTOP_ECANCELED)) {
return 0;
}
if (status != TT_OK) {
_adviseUser( "ttdt_Quit()", status );
}
// Destructor will _save() and reply to _contract
break;
case 1: // "Discard, then Quit"
if (_subContract != 0) {
// Asynchronously quit outstanding actions
status = ttdt_Quit( _subContract, subContractor,
silent, force, myContext, 0 );
if ( (status != TT_OK)
&& (status != TT_DESKTOP_ETIMEDOUT))
{
_adviseUser( "ttdt_Quit()", status );
}
}
if (_contract != 0) {
tttk_message_fail( _contract, TT_DESKTOP_ECANCELED,
0, 1 );
_contract = 0;
}
_unload();
break;
case 2: // "Cancel"
return 0;
}
delete this;
XtDestroyWidget( shell );
return 1;
}
int
CoEditor::_quitAll(
int silent,
int force
)
{
for (int i = 0; i < MaxEditors; i++) {
if (editors[i] == 0) {
continue;
}
if (! editors[i]->_quit( silent, force )) {
return 0;
}
}
}
void
CoEditor::_wmProtocolCB_(
Widget ,
XtPointer coEditor,
XmAnyCallbackStruct *
)
{
((CoEditor *)coEditor)->_wmProtocolCB();
}
void
CoEditor::_wmProtocolCB()
{
if (! _quit()) {
return;
}
/*
if (numEditors > 1) {
// More windows; just destroy this one
XtDestroyWidget( shell );
delete this;
}
*/
}
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, XmNuserData, &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;
case QuitChildren:
if (_subContract == 0) {
return;
}
status = ttdt_Quit( _subContract,
(char *)tt_message_user( _subContract, 0 ),
0, 0, myContext, 60 * timeOutFactor );
if (status == TT_DESKTOP_ECANCELED) {
return;
}
if (status != TT_OK) {
_adviseUser( "ttdt_Quit()", status );
}
return;
}
if (status != TT_OK) {
_adviseUser( 0, 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, XmNuserData, &op, 0 );
Tt_status status = TT_OK;
switch (op) {
int len;
char *contents;
const char *mediaType;
Tt_message msg;
case SelText:
if (_subContract != 0) {
userChoice( myContext, _baseFrame,
"Already editing buffer in another editor",
1, "Okay" );
return;
}
//contents = _selection( &len );
contents = _contents( &len );
if (len <= 0) {
return;
}
msg = ttmedia_load( _contract, CoEditor::_mediaLoadMsgCB_,
this, TTME_EDIT, myAltMediaType,
(unsigned char *)contents, len, 0, 0, 1 );
if (contents != 0) {
XtFree( contents );
}
status = tt_ptr_error( msg );
if (status != TT_OK) {
break;
}
ttdt_subcontract_manage( msg, CoEditor::subContractCB_,
shell, this );
_subContract = msg;
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
)
{
char *contents = XmTextGetString(_text);
*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 );
}
sessPats = ttdt_session_join( 0, CoEditor::contractCB_,
session_shell, this, 1 );
}
_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,
void *coEditor,
Tt_message contract
)
{
return ((CoEditor *)coEditor)->_contractCB( msg, contract );
}
Tt_message
CoEditor::_contractCB(
Tt_message msg,
Tt_message contract
)
{
if ((contract != 0) && (contract != _contract)) {
tttk_message_fail( msg, TT_DESKTOP_ENOMSG, 0, 1 );
return 0;
}
char *opString = tt_message_op( msg );
Tttk_op op = tttk_string_op( opString );
tt_free( opString );
int silent = 0;
int force = 0;
int cancel = 0;
char *status;
Boolean sensitive = True;
switch (op) {
case TTDT_QUIT:
tt_message_arg_ival( msg, 0, &silent );
tt_message_arg_ival( msg, 1, &force );
if (contract == 0) {
if (! _quitAll( silent, force )) {
cancel = 1;
}
} else {
if (! _quit( silent, force )) {
cancel = 1;
}
}
if (cancel) {
tttk_message_fail( msg, TT_DESKTOP_ECANCELED, 0, 1 );
} else {
tt_message_reply( msg );
tttk_message_destroy( msg );
}
return 0;
case TTDT_GET_STATUS:
if (contract == 0) {
if (numEditors < maxBuffers) {
status = "Awaiting requests to open more "
"buffers";
} else {
status = "Already have maximum open buffers";
}
} else {
status = "Request in progress";
}
tt_message_arg_val_set( msg, 0, status );
tt_message_reply( msg );
tttk_message_destroy( msg );
return 0;
case TTDT_PAUSE:
sensitive = False;
case TTDT_RESUME:
if (contract == 0) {
int already = 1;
for (int i = 0; i < MaxEditors; i++) {
if (editors[i] == 0) {
continue;
}
if ( XtIsSensitive( editors[i]->shell )
!= sensitive)
{
already = 0;
XtSetSensitive( editors[i]->shell,
sensitive );
}
}
if (already) {
tt_message_status_set(msg,TT_DESKTOP_EALREADY);
}
} else {
if (XtIsSensitive( shell ) == sensitive) {
tt_message_status_set(msg,TT_DESKTOP_EALREADY);
} else {
XtSetSensitive( shell, sensitive );
}
}
tt_message_reply( msg );
tttk_message_destroy( msg );
return 0;
case TTDT_DO_COMMAND:
tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 1 );
return 0;
}
return msg;
}
Tt_message
CoEditor::subContractCB_(
Tt_message msg,
void *coEditor,
Tt_message subContract
)
{
return ((CoEditor *)coEditor)->_subContractCB( msg, subContract );
}
Tt_message
CoEditor::_subContractCB(
Tt_message msg,
Tt_message subContract
)
{
if ((subContract != 0) && (subContract != _subContract)) {
tttk_message_fail( msg, TT_DESKTOP_ENOMSG, 0, 1 );
return 0;
}
char *opString = tt_message_op( msg );
Tttk_op op = tttk_string_op( opString );
tt_free( opString );
char *subContractor;
Tt_status status;
switch (op) {
case TTDT_STATUS:
// Save the procid who is working on the subContract
subContractor = tt_message_sender( msg );
status = tt_ptr_error( subContractor );
if (status == TT_OK) {
tt_message_user_set( subContract, 0, subContractor );
}
tttk_message_destroy( msg );
msg = 0;
break;
}
return msg;
}
Tt_message
CoEditor::_mediaLoadMsgCB_(
Tt_message msg,
void *clientData,
Tttk_op op,
unsigned char *contents,
int len,
char *file
)
{
return ((CoEditor *)clientData)->_mediaLoadMsgCB( msg, op,
contents, len, file );
}
Tt_message
CoEditor::_mediaLoadMsgCB(
Tt_message msg,
Tttk_op op,
unsigned char *contents,
int len,
char *file
)
{
Tt_state state = tt_message_state(msg);
Tt_status status = (Tt_status)tt_message_status(msg);
if (state == TT_FAILED) {
if (status == TT_DESKTOP_ECANCELED) {
// XXX gently say so in window footer
} else {
_adviseUser( "_mediaLoadMsgCB()", status );
}
} else {
if (len > 0) {
XmTextSetString(_text, (char *) contents);
XtAddCallback(_text, XmNvalueChangedCallback,
(XtCallbackProc)CoEditor::_textUpdateCB_,
(caddr_t)this );
// OLIT equivalent: ReplaceBlockInTextBuffer
} else if (file != 0) {
// XXX slurp in file
}
}
if (op == TTME_DEPOSIT) {
tt_message_reply( msg );
}
if ( (msg == _subContract)
&& (state == TT_HANDLED || state == TT_FAILED))
{
_subContract = 0;
}
tttk_message_destroy( msg );
return 0;
}
void
CoEditor::_textUpdateCB_(
Widget coEditor,
XtPointer call_data,
XmTextVerifyCallbackStruct *
)
{
if (coEditor == 0) {
return;
}
((CoEditor *)call_data)->_textUpdateCB();
}
void
CoEditor::_textUpdateCB()
{
//Tt_status status;
if (! _modifiedByMe) {
_modifiedByMe = TRUE;
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, XmNmodifyVerifyCallback,
(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 {
tttk_message_fail( msg, status, 0, 0 );
}
break;
case TTDT_REVERT:
status = _revert();
if (status == TT_OK) {
tt_message_reply( msg );
} else {
tttk_message_fail( msg, status, 0, 0 );
}
break;
case TTDT_REVERTED:
if (! me) {
_modifiedByOther = 0;
}
break;
case TTDT_SAVED:
if (! me) {
_modifiedByOther = 0;
int choice = userChoice( myContext, _baseFrame,
"Another tool has saved "
"this file.", 2, "Ignore",
"Revert" );
switch (choice) {
case 1:
_revert();
break;
}
}
break;
case TTDT_MOVED:
case TTDT_DELETED:
printf( "CoEditor::_fileCB(): %s\n", tttk_op_string( op ));
break;
}
tttk_message_destroy( msg );
return 0;
}
void
CoEditor::_textModifyCB_(
Widget text,
XtPointer ,
XmTextVerifyCallbackStruct *mod
)
{
CoEditor *coEditor = 0;
XtVaGetValues( text, XmNuserData, &coEditor, 0 );
if (coEditor == 0) {
return;
}
coEditor->_textModifyCB( mod );
}
void
CoEditor::_textModifyCB(
XmTextVerifyCallbackStruct *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->doit = FALSE;
}
_modifiedByOther = 2;
}
void
CoEditor::_adviseUser(
const char *msg,
Tt_status status
)
{
char *s = tt_status_message( status );
char buf[ 1024 ];
if (msg != 0) {
sprintf( buf, "%s: %d: %s", msg, status, s );
} else {
sprintf( buf, "%d: %s", status, s );
}
tt_free( s );
userChoice( myContext, _baseFrame, buf, 1, "Okay" );
}