1881 lines
49 KiB
C
1881 lines
49 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
|
|
*/
|
|
/* $TOG: EditAreaData.c /main/6 1998/03/03 16:18:13 mgreess $ */
|
|
/**********************************<+>*************************************
|
|
***************************************************************************
|
|
**
|
|
** File: EditAreaData.c
|
|
**
|
|
** Project: DtEditor widget for editing services
|
|
**
|
|
** Description: Contains functions for getting and setting the data
|
|
** on which the editor operates.
|
|
** -----------
|
|
**
|
|
*******************************************************************
|
|
*
|
|
* (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of Novell, Inc.
|
|
* (c) Copyright 1996 Digital Equipment Corporation.
|
|
* (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company.
|
|
* (c) Copyright 1993, 1994, 1996 International Business Machines Corp.
|
|
* (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.
|
|
* (c) Copyright 1996 Novell, Inc.
|
|
* (c) Copyright 1996 FUJITSU LIMITED.
|
|
* (c) Copyright 1996 Hitachi.
|
|
*
|
|
********************************************************************
|
|
**
|
|
**
|
|
**************************************************************************
|
|
**********************************<+>*************************************/
|
|
#include "EditorP.h"
|
|
#include <X11/Xutil.h>
|
|
#include <Xm/TextP.h>
|
|
#include <unistd.h>
|
|
#include "DtWidgetI.h"
|
|
|
|
|
|
typedef enum _LoadActionType {
|
|
LOAD_DATA,
|
|
INSERT_DATA,
|
|
APPEND_DATA,
|
|
REPLACE_DATA
|
|
} LoadActionType;
|
|
|
|
static DtEditorErrorCode Check4EnoughMemory(
|
|
int numBytes);
|
|
|
|
static DtEditorErrorCode StripEmbeddedNulls(
|
|
char *stringData,
|
|
int *length);
|
|
|
|
static DtEditorErrorCode LoadFile(
|
|
Widget w,
|
|
char *fileName,
|
|
LoadActionType action,
|
|
XmTextPosition startReplace,
|
|
XmTextPosition endReplace );
|
|
|
|
#ifdef NEED_STRCASECMP
|
|
|
|
/*
|
|
* in case strcasecmp is not provided by the system here is one
|
|
* which does the trick
|
|
*/
|
|
static int
|
|
strcasecmp(s1, s2)
|
|
char *s1, *s2;
|
|
{
|
|
int c1, c2;
|
|
|
|
while (*s1 && *s2) {
|
|
c1 = isupper(*s1) ? tolower(*s1) : *s1;
|
|
c2 = isupper(*s2) ? tolower(*s2) : *s2;
|
|
if (c1 != c2)
|
|
return (1);
|
|
s1++;
|
|
s2++;
|
|
}
|
|
if (*s1 || *s2)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Check4EnoughMemory - estimates whether there is enough memory to malloc
|
|
* "numBytes" of memory. This routine doubles the amount needed because the
|
|
* routines that use it are putting data into the text widget & we must make
|
|
* sure the widget will have room, too.
|
|
*
|
|
* Returns DtEDITOR_NO_ERRORS
|
|
* DtEDITOR_ILLEGAL_SIZE
|
|
* DtEDITOR_INSUFFICIENT_MEMORY
|
|
*
|
|
*****************************************************************************/
|
|
static DtEditorErrorCode
|
|
Check4EnoughMemory(
|
|
int numBytes)
|
|
{
|
|
|
|
DtEditorErrorCode returnVal = DtEDITOR_ILLEGAL_SIZE;
|
|
|
|
if (numBytes > 0) {
|
|
char *tmpString = (char *)malloc((2 * numBytes) + (numBytes/10));
|
|
|
|
if(tmpString == (char *)NULL)
|
|
returnVal = DtEDITOR_INSUFFICIENT_MEMORY;
|
|
else {
|
|
returnVal = DtEDITOR_NO_ERRORS;
|
|
free(tmpString);
|
|
}
|
|
}
|
|
|
|
return( returnVal );
|
|
|
|
} /* end Check4EnoughMemory */
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* StripEmbeddedNulls - removes any embedded NULLs (\0) in a string of length
|
|
* 'length'. The removal occurs in place, with 'length' set to the
|
|
* new, stripped length. The resulting string is terminated with a
|
|
* trailing NULL.
|
|
*
|
|
* Returns DtEDITOR_NO_ERRORS - the string did not contain any embedded NULLs
|
|
* DtEDITOR_NULLS_REMOVED - the string did contain embedded
|
|
* NULLs that were removed.
|
|
*
|
|
*****************************************************************************/
|
|
static DtEditorErrorCode
|
|
StripEmbeddedNulls(
|
|
char *stringData,
|
|
int *length)
|
|
{
|
|
DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS;
|
|
|
|
if (strlen(stringData) != *length)
|
|
{
|
|
int firstNull;
|
|
|
|
returnVal = DtEDITOR_NULLS_REMOVED;
|
|
|
|
/*
|
|
* The file contains NULL characters, so we strip them out and
|
|
* report that we have done so.
|
|
*/
|
|
while((firstNull = strlen(stringData)) != *length)
|
|
{
|
|
int lastNull = firstNull;
|
|
|
|
while((lastNull + 1) < *length &&
|
|
stringData[lastNull + 1] == (char)'\0')
|
|
lastNull++;
|
|
|
|
memcpy(&stringData[firstNull], &stringData[lastNull + 1],
|
|
*length - lastNull);
|
|
*length -= 1 + lastNull - firstNull;
|
|
}
|
|
|
|
}
|
|
|
|
return( returnVal);
|
|
|
|
} /* end StripEmbeddedNulls */
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Retrieves the current location of the insert cursor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
XmTextPosition
|
|
DtEditorGetInsertionPosition(
|
|
Widget widget)
|
|
{
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
XmTextPosition result;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
result = XmTextGetInsertionPosition(M_text(editor));
|
|
|
|
_DtAppUnlock(app);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Retrieves the current location of the last character in the widget
|
|
*
|
|
*****************************************************************************/
|
|
|
|
XmTextPosition
|
|
DtEditorGetLastPosition(
|
|
Widget widget)
|
|
{
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
XmTextPosition result;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
result = XmTextGetLastPosition(M_text(editor));
|
|
|
|
_DtAppUnlock(app);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Changes the current location of the insert cursor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void
|
|
DtEditorSetInsertionPosition(
|
|
Widget widget,
|
|
XmTextPosition position)
|
|
{
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
XmTextSetInsertionPosition(M_text(editor), position);
|
|
|
|
_DtAppUnlock(app);
|
|
}
|
|
|
|
|
|
static DtEditorErrorCode
|
|
setStringValue(
|
|
DtEditorWidget editor,
|
|
char *data)
|
|
{
|
|
/*
|
|
* Tell _DtEditorModifyVerifyCB() that we're replacing the entire
|
|
* contents, so it doesn't try to save the current document in an
|
|
* undo structure for a later undo.
|
|
*/
|
|
M_loadingAllNewData(editor) = True;
|
|
|
|
XmTextSetString( M_text(editor), data );
|
|
|
|
/*
|
|
* If the _DtEditorModifyVerifyCB() did not get called, reset the
|
|
* things which usually get reset there. The modifyVerify callback
|
|
* will not get called if the contents are being set to a null string
|
|
* and the widget is already empty.
|
|
*/
|
|
if (M_loadingAllNewData(editor) == True) {
|
|
M_loadingAllNewData(editor) = False;
|
|
M_unreadChanges(editor) = False;
|
|
_DtEditorResetUndo(editor);
|
|
}
|
|
|
|
return( DtEDITOR_NO_ERRORS );
|
|
|
|
} /* end setStringValue */
|
|
|
|
|
|
static DtEditorErrorCode
|
|
setDataValue(
|
|
DtEditorWidget widget,
|
|
void *rawData,
|
|
int length)
|
|
{
|
|
DtEditorErrorCode status = DtEDITOR_NULL_ITEM, tmpError;
|
|
|
|
/*
|
|
* Validate input
|
|
*/
|
|
if (rawData != (void *)NULL)
|
|
{
|
|
|
|
/*
|
|
* Check to see if we have a valid buffer size & enough memory to
|
|
* load the buffer into the text widget. This is only an estimate
|
|
* of our needs.
|
|
* Check4EnoughMemory() returns DtEDITOR_NO_ERRORS,
|
|
* DtEDITOR_ILLEGAL_SIZE, or DtEDITOR_INSUFFICIENT_MEMORY.
|
|
*/
|
|
|
|
status = Check4EnoughMemory( length );
|
|
if (status == DtEDITOR_NO_ERRORS)
|
|
{
|
|
|
|
/*
|
|
* Convert the data buffer into a string & insert into the widget
|
|
*/
|
|
char *textData = (char *)XtMalloc(length + 1);
|
|
memcpy( textData, rawData, length );
|
|
textData[length] = '\0';
|
|
|
|
/*
|
|
* Strip out any embedded NULLs because the text widget will only
|
|
* accept data up to the first NULL.
|
|
*
|
|
* StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
|
|
* DtEDITOR_NULLS_REMOVED
|
|
*/
|
|
status = StripEmbeddedNulls( textData, &length );
|
|
|
|
/*
|
|
* Now, insert the converted string into the text widget
|
|
*/
|
|
tmpError = setStringValue( widget, textData );
|
|
if (tmpError != DtEDITOR_NO_ERRORS)
|
|
status = tmpError;
|
|
|
|
XtFree( (char *)textData );
|
|
}
|
|
}
|
|
|
|
return( status );
|
|
|
|
} /* end setDataValue */
|
|
|
|
|
|
static DtEditorErrorCode
|
|
setWcharValue(
|
|
DtEditorWidget editor,
|
|
wchar_t *data)
|
|
{
|
|
DtEditorErrorCode status;
|
|
wchar_t *tmp_wc;
|
|
int result, num_chars=0;
|
|
char *mb_value = (char *)NULL;
|
|
|
|
|
|
/*
|
|
* Convert the wide char string to a multi-byte string & stick it in
|
|
* the text widget.
|
|
*/
|
|
|
|
/*
|
|
* Determine how big the resulting mb string may be
|
|
*/
|
|
for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++)
|
|
tmp_wc++;
|
|
|
|
/*
|
|
* Check to see if we have enough memory to load the string
|
|
* into the text widget. This is only an estimate of our needs.
|
|
* status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or
|
|
* DtEDITOR_INSUFFICIENT_MEMORY.
|
|
*/
|
|
status = Check4EnoughMemory( (num_chars + 1) * MB_CUR_MAX );
|
|
if (status != DtEDITOR_NO_ERRORS) return status;
|
|
mb_value = XtMalloc( (unsigned)(num_chars + 1) * MB_CUR_MAX );
|
|
|
|
/*
|
|
* Convert the wchar string
|
|
* If wcstombs fails it returns (size_t) -1, so pass in empty
|
|
* string.
|
|
*/
|
|
result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX );
|
|
if (result == (size_t)-1)
|
|
result = 0;
|
|
|
|
/*
|
|
* wcstombs doesn't guarantee string is NULL terminated
|
|
*/
|
|
mb_value[result] = 0;
|
|
|
|
status = setStringValue( editor, mb_value );
|
|
|
|
XtFree(mb_value);
|
|
|
|
return( status );
|
|
|
|
} /* end setWcharValue */
|
|
|
|
|
|
static DtEditorErrorCode
|
|
insertStringValue(
|
|
DtEditorWidget editor,
|
|
char *data,
|
|
LoadActionType typeOfInsert,
|
|
XmTextPosition beginInsert,
|
|
XmTextPosition endInsert)
|
|
{
|
|
int numInserted;
|
|
|
|
switch( typeOfInsert )
|
|
{
|
|
case INSERT_DATA:
|
|
{
|
|
beginInsert = endInsert = XmTextGetInsertionPosition( M_text(editor) );
|
|
break;
|
|
}
|
|
|
|
case APPEND_DATA:
|
|
{
|
|
beginInsert = endInsert = XmTextGetLastPosition( M_text(editor) );
|
|
break;
|
|
}
|
|
|
|
case REPLACE_DATA:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
}
|
|
} /* end switch */
|
|
|
|
/*
|
|
* Insert/Replace/Append the data and move the insertion cursor to
|
|
* the end of the inserted data.
|
|
*/
|
|
numInserted = _DtEditor_CountCharacters( data, strlen(data) );
|
|
XmTextReplace(M_text(editor), beginInsert, endInsert, data);
|
|
XmTextSetInsertionPosition( M_text(editor),
|
|
(XmTextPosition)(beginInsert + numInserted) );
|
|
|
|
return( DtEDITOR_NO_ERRORS );
|
|
} /* insertStringValue */
|
|
|
|
static DtEditorErrorCode
|
|
insertDataValue(
|
|
DtEditorWidget widget,
|
|
void *rawData,
|
|
int length,
|
|
LoadActionType typeOfInsert,
|
|
XmTextPosition beginInsert,
|
|
XmTextPosition endInsert)
|
|
{
|
|
DtEditorErrorCode status = DtEDITOR_NULL_ITEM, loadError;
|
|
|
|
/*
|
|
* Validate input
|
|
*/
|
|
if (rawData != (void *) NULL)
|
|
{
|
|
|
|
/*
|
|
* Check to see if we have a valid buffer size & enough memory to
|
|
* insert the buffer into the text widget. This is only an estimate
|
|
* of our needs.
|
|
* status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or
|
|
* DtEDITOR_INSUFFICIENT_MEMORY.
|
|
*/
|
|
status = Check4EnoughMemory( length );
|
|
if (status == DtEDITOR_NO_ERRORS)
|
|
{
|
|
|
|
/*
|
|
* Convert the data buffer into a string & insert into the widget
|
|
*/
|
|
char *textData = (char *)XtMalloc(length + 1);
|
|
memcpy( textData, rawData, length );
|
|
textData[length] = '\0';
|
|
|
|
/*
|
|
* Strip out any embedded NULLs because the text widget will only
|
|
* accept data up to the first NULL.
|
|
*
|
|
* StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
|
|
* DtEDITOR_NULLS_REMOVED
|
|
*/
|
|
status = StripEmbeddedNulls( textData, &length );
|
|
|
|
/*
|
|
* Now, insert the converted string into the text widget
|
|
*/
|
|
loadError = insertStringValue( widget, textData, typeOfInsert,
|
|
beginInsert, endInsert );
|
|
if (loadError != DtEDITOR_NO_ERRORS)
|
|
status = loadError;
|
|
|
|
XtFree( (char *)textData );
|
|
}
|
|
|
|
}
|
|
|
|
return( status );
|
|
|
|
} /* insertDataValue */
|
|
|
|
|
|
static DtEditorErrorCode
|
|
insertWcharValue(
|
|
DtEditorWidget editor,
|
|
wchar_t *data,
|
|
LoadActionType typeOfInsert,
|
|
XmTextPosition beginInsert,
|
|
XmTextPosition endInsert)
|
|
{
|
|
|
|
wchar_t *tmp_wc;
|
|
int result, num_chars=0;
|
|
char *mb_value = (char *)NULL;
|
|
DtEditorErrorCode status;
|
|
|
|
|
|
/*
|
|
* Convert the wide char string to a multi-byte string & insert it into
|
|
* the text widget.
|
|
*/
|
|
|
|
/*
|
|
* Determine how big the resulting mb string may be
|
|
*/
|
|
for (num_chars = 0, tmp_wc = data; *tmp_wc != (wchar_t)0L; num_chars++)
|
|
tmp_wc++;
|
|
|
|
/*
|
|
* Check to see if we have enough memory to insert the string
|
|
* into the text widget. This is only an estimate of our needs.
|
|
* status will be set to DtEDITOR_NO_ERRORS, DtEDITOR_ILLEGAL_SIZE, or
|
|
* DtEDITOR_INSUFFICIENT_MEMORY.
|
|
*/
|
|
|
|
status = Check4EnoughMemory( (num_chars + 1) * MB_CUR_MAX );
|
|
if(status != DtEDITOR_NO_ERRORS) return status;
|
|
mb_value = XtMalloc( (unsigned)(num_chars + 1) * MB_CUR_MAX );
|
|
|
|
/*
|
|
* Convert the wchar string.
|
|
* If wcstombs fails it returns (size_t) -1, so pass in empty
|
|
* string.
|
|
*/
|
|
result = wcstombs( mb_value, data, (num_chars + 1) * MB_CUR_MAX );
|
|
if (result == (size_t)-1)
|
|
result = 0;
|
|
|
|
/*
|
|
* wcstombs doesn't guarantee string is NULL terminated
|
|
*/
|
|
mb_value[result] = 0;
|
|
|
|
status = insertStringValue( editor, mb_value, typeOfInsert,
|
|
beginInsert, endInsert );
|
|
XtFree( mb_value );
|
|
|
|
return( status );
|
|
|
|
} /* insertWcharValue */
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorSetContents - sets the contents of the DtEditor widget.
|
|
*
|
|
* Inputs: widget to set the contents
|
|
*
|
|
* a data structure containing the data to put into the
|
|
* widget. Depending upon the type of data being set, this
|
|
* structure will contain various fields:
|
|
*
|
|
* string - \0-terminated string of characters
|
|
* data - the data, the size of the data
|
|
*
|
|
* Returns 0 - contents were set sucessfully
|
|
* !0 - an error occurred while setting the contents
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorSetContents(
|
|
Widget widget,
|
|
DtEditorContentRec *data )
|
|
{
|
|
DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
switch( data->type )
|
|
{
|
|
case DtEDITOR_TEXT:
|
|
{
|
|
error = setStringValue ( editor, data->value.string );
|
|
break;
|
|
}
|
|
case DtEDITOR_DATA:
|
|
{
|
|
error = setDataValue ( editor, data->value.data.buf,
|
|
data->value.data.length);
|
|
break;
|
|
}
|
|
case DtEDITOR_WCHAR:
|
|
{
|
|
error = setWcharValue ( editor, data->value.wchar );
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
error = DtEDITOR_INVALID_TYPE;
|
|
}
|
|
} /* end switch */
|
|
|
|
/*
|
|
* Update the current-line-display in the status line
|
|
*/
|
|
if (error == DtEDITOR_NO_ERRORS)
|
|
_DtEditorUpdateLineDisplay(editor, 1, False );
|
|
|
|
_DtAppUnlock(app);
|
|
return( error );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorSetContentsFromFile - read a data file, putting the contents
|
|
* into a DtEditor widget.
|
|
*
|
|
* Inputs: widget to load the file into
|
|
*
|
|
* to indicate the type of contents loaded from the file:
|
|
* string - a \0-terminated string of characters
|
|
* data - untyped data
|
|
*
|
|
* filename - name of the file to read
|
|
*
|
|
* Returns 0 - contents were loaded sucessfully
|
|
* !0 - an error occurred while loading the contents
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorSetContentsFromFile(
|
|
Widget widget,
|
|
char *fileName)
|
|
{
|
|
DtEditorErrorCode result;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
result = LoadFile(widget, fileName, LOAD_DATA, 0, 0);
|
|
|
|
_DtAppUnlock(app);
|
|
return result;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorAppend - append data to the contents of the DtEditor widget.
|
|
*
|
|
* Inputs: widget to add to the contents
|
|
*
|
|
* a data structure containing the data to append to the
|
|
* widget. Depending upon the type of data being set, this
|
|
* structure will contain various fields:
|
|
*
|
|
* string - \0-terminated string of characters
|
|
* data - the data, the size of the data
|
|
*
|
|
* Returns 0 - contents were set sucessfully
|
|
* !0 - an error occurred while setting the contents
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorAppend(
|
|
Widget widget,
|
|
DtEditorContentRec *data )
|
|
{
|
|
DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
switch( data->type )
|
|
{
|
|
case DtEDITOR_TEXT:
|
|
{
|
|
error = insertStringValue ( editor, data->value.string,
|
|
APPEND_DATA, 0, 0 );
|
|
break;
|
|
}
|
|
case DtEDITOR_DATA:
|
|
{
|
|
error = insertDataValue ( editor, data->value.data.buf,
|
|
data->value.data.length, APPEND_DATA, 0,0);
|
|
break;
|
|
}
|
|
case DtEDITOR_WCHAR:
|
|
{
|
|
error = insertWcharValue ( editor, data->value.wchar,
|
|
APPEND_DATA, 0, 0 );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
error = DtEDITOR_INVALID_TYPE;
|
|
}
|
|
} /* end switch */
|
|
|
|
_DtAppUnlock(app);
|
|
return( error );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorAppendFromFile - read a data file, appending the contents
|
|
* into a DtEditor widget.
|
|
*
|
|
* Inputs: widget to append the file to
|
|
*
|
|
* to indicate the type of contents appended from the file:
|
|
* string - a \0-terminated string of characters
|
|
* data - untyped data
|
|
*
|
|
* filename - name of the file to read
|
|
*
|
|
* Returns 0 - contents were appended sucessfully
|
|
* !0 - an error occurred while appending the contents
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorAppendFromFile(
|
|
Widget widget,
|
|
char *fileName)
|
|
{
|
|
DtEditorErrorCode result;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
result = LoadFile(widget, fileName, APPEND_DATA, 0, 0);
|
|
|
|
_DtAppUnlock(app);
|
|
return result;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorInsert - insert data into the contents of the DtEditor widget.
|
|
*
|
|
* Inputs: widget to add to the contents
|
|
*
|
|
* a data structure containing the data to insert into the
|
|
* widget. Depending upon the type of data being set, this
|
|
* structure will contain various fields:
|
|
*
|
|
* string - \0-terminated string of characters
|
|
* data - the data, the size of the data
|
|
*
|
|
* Returns 0 - contents were set sucessfully
|
|
* !0 - an error occurred while setting the contents
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorInsert(
|
|
Widget widget,
|
|
DtEditorContentRec *data )
|
|
{
|
|
DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
switch( data->type )
|
|
{
|
|
case DtEDITOR_TEXT:
|
|
{
|
|
error = insertStringValue ( editor, data->value.string,
|
|
INSERT_DATA, 0, 0 );
|
|
break;
|
|
}
|
|
case DtEDITOR_DATA:
|
|
{
|
|
error = insertDataValue ( editor, data->value.data.buf,
|
|
data->value.data.length, INSERT_DATA, 0,0);
|
|
break;
|
|
}
|
|
case DtEDITOR_WCHAR:
|
|
{
|
|
error = insertWcharValue ( editor, data->value.wchar,
|
|
INSERT_DATA, 0, 0 );
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
error = DtEDITOR_INVALID_TYPE;
|
|
}
|
|
} /* end switch */
|
|
|
|
_DtAppUnlock(app);
|
|
return( error );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorInsertFromFile - read a data file, inserting the contents
|
|
* into a DtEditor widget.
|
|
*
|
|
* Inputs: widget to insert the file to
|
|
*
|
|
* to indicate the type of contents inserted from the file:
|
|
* string - a \0-terminated string of characters
|
|
* data - untyped data
|
|
*
|
|
* filename - name of the file to read
|
|
*
|
|
* Returns 0 - contents were inserted sucessfully
|
|
* !0 - an error occurred while inserting the contents
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorInsertFromFile(
|
|
Widget widget,
|
|
char *fileName)
|
|
{
|
|
DtEditorErrorCode result;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
result = LoadFile(widget, fileName, INSERT_DATA, 0, 0);
|
|
|
|
_DtAppUnlock(app);
|
|
return result;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorReplace - replace a specified portion of the contents of the
|
|
* DtEditor widget with the supplied data.
|
|
*
|
|
* Inputs: widget to replace a portion of its contents
|
|
*
|
|
* starting character position of the portion to replace
|
|
*
|
|
* ending character position of the portion to replace
|
|
*
|
|
* a data structure containing the data to replace some data
|
|
* in the widget. Depending upon the type of data being set,
|
|
* this structure will contain various fields:
|
|
*
|
|
* string - \0-terminated string of characters
|
|
* data - the data, the size of the data
|
|
*
|
|
*
|
|
* Returns 0 - the portion was replaced sucessfully
|
|
* !0 - an error occurred while replacing the portion
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorReplace(
|
|
Widget widget,
|
|
XmTextPosition startPos,
|
|
XmTextPosition endPos,
|
|
DtEditorContentRec *data)
|
|
{
|
|
DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
XmTextWidget tw;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
tw = (XmTextWidget) M_text(editor);
|
|
|
|
if( startPos < 0 )
|
|
startPos = 0;
|
|
if( startPos > tw->text.last_position )
|
|
startPos = tw->text.last_position;
|
|
|
|
if( endPos < 0 )
|
|
endPos = 0;
|
|
if( endPos > tw->text.last_position )
|
|
endPos = tw->text.last_position;
|
|
|
|
if( startPos > endPos )
|
|
{
|
|
error = DtEDITOR_INVALID_RANGE;
|
|
}
|
|
else
|
|
{
|
|
switch( data->type )
|
|
{
|
|
case DtEDITOR_TEXT:
|
|
{
|
|
error = insertStringValue ( editor, data->value.string,
|
|
REPLACE_DATA, startPos, endPos );
|
|
break;
|
|
}
|
|
case DtEDITOR_DATA:
|
|
{
|
|
error = insertDataValue ( editor, data->value.data.buf,
|
|
data->value.data.length, REPLACE_DATA,
|
|
startPos, endPos );
|
|
break;
|
|
}
|
|
case DtEDITOR_WCHAR:
|
|
{
|
|
error = insertWcharValue ( editor, data->value.wchar,
|
|
REPLACE_DATA, startPos, endPos );
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
error = DtEDITOR_INVALID_TYPE;
|
|
}
|
|
} /* end switch */
|
|
|
|
}
|
|
|
|
_DtAppUnlock(app);
|
|
return( error );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorReplaceFromFile - read a data file, using the contents to replace
|
|
* a specified portion of the contntes of a
|
|
* DtEditor widget.
|
|
*
|
|
* Inputs: widget to insert the file to
|
|
*
|
|
* starting character position of the portion to replace
|
|
*
|
|
* ending character position of the portion to replace
|
|
*
|
|
* to indicate the type of contents inserted from the file:
|
|
* string - a \0-terminated string of characters
|
|
* data - untyped data
|
|
*
|
|
* filename - local name of the file to read
|
|
*
|
|
* Returns 0 - contents were inserted sucessfully
|
|
* !0 - an error occurred while inserting the contents
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorReplaceFromFile(
|
|
Widget widget,
|
|
XmTextPosition startPos,
|
|
XmTextPosition endPos,
|
|
char *fileName)
|
|
{
|
|
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
XmTextWidget tw;
|
|
DtEditorErrorCode result;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
tw = (XmTextWidget) M_text(editor);
|
|
|
|
if( startPos < 0)
|
|
startPos = 0;
|
|
if( startPos > tw->text.last_position )
|
|
startPos = tw->text.last_position;
|
|
|
|
if( endPos < 0 )
|
|
endPos = 0;
|
|
if( endPos > tw->text.last_position )
|
|
endPos = tw->text.last_position;
|
|
|
|
if(startPos > endPos)
|
|
{
|
|
result = DtEDITOR_INVALID_RANGE;
|
|
}
|
|
else
|
|
{
|
|
result = LoadFile(widget, fileName, REPLACE_DATA, startPos, endPos);
|
|
}
|
|
|
|
_DtAppUnlock(app);
|
|
return result;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* _DtEditorValidateFileAccess - check to see if file exists, whether we
|
|
* can get to it, and whether it is readable
|
|
* or writable.
|
|
*
|
|
* Note: does not check whether files for reading are read only.
|
|
*
|
|
* Inputs: filename - name of the local file to read
|
|
* flag indicating whether we want to read or write
|
|
* the file.
|
|
*
|
|
* Returns 0 file exists & we have read or write permissions.
|
|
*
|
|
* >0 if file cannot be read from/written to.
|
|
* errno is set to one of the following values:
|
|
*
|
|
* General errors:
|
|
* DtEDITOR_INVALID_FILENAME - 0 length filename
|
|
* DtEDITOR_NONEXISTENT_FILE - file does not exist
|
|
* (Note: this may not be considered an error when saving
|
|
* to a file. The file may just need to be created.)
|
|
* DtEDITOR_NO_FILE_ACCESS - cannot stat existing file
|
|
* DtEDITOR_DIRECTORY - file is a directory
|
|
* DtEDITOR_CHAR_SPECIAL_FILE - file is a device special file
|
|
* DtEDITOR_BLOCK_MODE_FILE - file is a block mode file
|
|
*
|
|
* additional READ_ACCESS errors:
|
|
* DtEDITOR_UNREADABLE_FILE -
|
|
*
|
|
* additional WRITE_ACCESS errors:
|
|
* DtEDITOR_UNWRITABLE_FILE -
|
|
* file or directory is write protected for
|
|
* another reason
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
_DtEditorValidateFileAccess(
|
|
char *fileName,
|
|
int accessType )
|
|
{
|
|
struct stat statbuf; /* Information on a file. */
|
|
|
|
DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME;
|
|
|
|
/*
|
|
* First, make sure we were given a name
|
|
*/
|
|
if (fileName && *fileName )
|
|
{
|
|
|
|
/*
|
|
* Does the file already exist?
|
|
*/
|
|
if ( access(fileName, F_OK) != 0 )
|
|
error = DtEDITOR_NONEXISTENT_FILE;
|
|
else
|
|
{
|
|
error = DtEDITOR_NO_ERRORS;
|
|
|
|
/*
|
|
* The file exists, so lets do some type checking
|
|
*/
|
|
|
|
if( stat(fileName, &statbuf) != 0 )
|
|
error = DtEDITOR_NO_FILE_ACCESS;
|
|
else
|
|
{
|
|
|
|
/* if its a directory - can't save */
|
|
if( (statbuf.st_mode & S_IFMT) == S_IFDIR )
|
|
{
|
|
error = DtEDITOR_DIRECTORY;
|
|
return( error );
|
|
}
|
|
|
|
/* if its a character special device - can't save */
|
|
if( (statbuf.st_mode & S_IFMT) == S_IFCHR )
|
|
{
|
|
error = DtEDITOR_CHAR_SPECIAL_FILE;
|
|
return( error );
|
|
}
|
|
|
|
/* if its a block mode device - can't save */
|
|
if((statbuf.st_mode & S_IFMT) == S_IFBLK)
|
|
{
|
|
error = DtEDITOR_BLOCK_MODE_FILE;
|
|
return( error );
|
|
}
|
|
|
|
/*
|
|
* We now know that it's a regular file so check to whether we
|
|
* can read or write to it, as appropriate.
|
|
*/
|
|
|
|
switch( accessType )
|
|
{
|
|
case READ_ACCESS:
|
|
{
|
|
|
|
if( access(fileName, R_OK) != 0 )
|
|
error = DtEDITOR_UNREADABLE_FILE;
|
|
|
|
break;
|
|
}
|
|
|
|
case WRITE_ACCESS:
|
|
{
|
|
|
|
if( access(fileName, W_OK) == 0 )
|
|
{
|
|
/*
|
|
* Can write to it.
|
|
*/
|
|
error = DtEDITOR_WRITABLE_FILE;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Can't write to it.
|
|
*/
|
|
error = DtEDITOR_UNWRITABLE_FILE;
|
|
|
|
} /* end no write permission */
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
|
|
} /* end switch */
|
|
} /* end stat suceeded */
|
|
|
|
} /* end file exists */
|
|
|
|
} /* end filename passed in */
|
|
|
|
return( error );
|
|
|
|
} /* end _DtEditorValidateFileAccess */
|
|
|
|
|
|
/************************************************************************
|
|
*
|
|
* LoadFile - Check if file exists, whether we can get to it, etc.
|
|
* If so, type and read its contents.
|
|
*
|
|
* Inputs: widget to set, add, or insert contents of file into
|
|
*
|
|
* name of file to read
|
|
*
|
|
* type of file (NULL). This will be set by LoadFile
|
|
*
|
|
* action to perform with the data (load, append, insert,
|
|
* replace a portion, attach)
|
|
*
|
|
* The following information will be used if the file
|
|
* contents will replace a portion of the widget's contents:
|
|
*
|
|
* starting character position of the portion to replace
|
|
*
|
|
* ending character position of the portion to replace
|
|
*
|
|
* Returns: DtEDITOR_NO_ERRORS - file was read sucessfully
|
|
* DtEDITOR_READ_ONLY_FILE - file was read sucessfully but
|
|
* is read only
|
|
* DtEDITOR_DIRECTORY - the file is a directory
|
|
* DtEDITOR_CHAR_SPECIAL_FILE - the file is a character
|
|
* special device
|
|
* DtEDITOR_BLOCK_MODE_FILE - the file is a block mode device
|
|
* DtEDITOR_NONEXISTENT_FILE - file does not exist
|
|
* DtEDITOR_NULLS_REMOVED - file contained embedded NULLs
|
|
* that were removed
|
|
* DtEDITOR_INSUFFICIENT_MEMORY - unable to allocate
|
|
* enough memory for contents of file
|
|
*
|
|
************************************************************************/
|
|
|
|
static DtEditorErrorCode
|
|
LoadFile(
|
|
Widget w,
|
|
char *fileName,
|
|
LoadActionType action,
|
|
XmTextPosition startReplace,
|
|
XmTextPosition endReplace )
|
|
|
|
{
|
|
DtEditorContentRec cr; /* Structure for passing data to widget */
|
|
struct stat statbuf; /* Information on a file. */
|
|
int file_length; /* Length of file. */
|
|
FILE *fp = NULL; /* Pointer to open file */
|
|
DtEditorErrorCode returnVal = DtEDITOR_NONEXISTENT_FILE;
|
|
/* Error accessing file & reading contents */
|
|
DtEditorErrorCode loadError=DtEDITOR_NO_ERRORS;
|
|
/* Error from placing bits into text widget */
|
|
|
|
/*
|
|
* First, make sure we were given a name
|
|
*/
|
|
|
|
if (fileName && *fileName )
|
|
{
|
|
|
|
/*
|
|
* Can we read the file?
|
|
*/
|
|
returnVal = _DtEditorValidateFileAccess( fileName, READ_ACCESS );
|
|
|
|
if( returnVal == DtEDITOR_NO_ERRORS )
|
|
{
|
|
/*
|
|
* Open the file for reading. If we can read/write, then we're
|
|
* cool, otherwise we might need to tell the user that the
|
|
* file's read-only, or that we can't even read from it.
|
|
*/
|
|
if( (fp = fopen(fileName, "r+")) == NULL )
|
|
{
|
|
/*
|
|
* We can't update (read/write) the file so try opening read-
|
|
* only
|
|
*/
|
|
if( (fp = fopen(fileName, "r")) == NULL )
|
|
{
|
|
/*
|
|
* We can't read from the file.
|
|
*/
|
|
return ( DtEDITOR_UNREADABLE_FILE );
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Tell the application that the file's read-only.
|
|
* Becareful not to overwrite this value with one of the calls
|
|
* to set the widget's contents.
|
|
*/
|
|
returnVal = DtEDITOR_READ_ONLY_FILE;
|
|
}
|
|
|
|
} /* end open for read/write */
|
|
|
|
} /* end try to read the file */
|
|
|
|
|
|
} /* end if no filename */
|
|
|
|
/* If a file is open, get the bytes */
|
|
if ( fp )
|
|
{
|
|
|
|
stat( fileName, &statbuf );
|
|
file_length = statbuf.st_size;
|
|
|
|
/*
|
|
* Check to see if we have enough memory to load the file contents
|
|
* into the text widget. This is only an estimate of our needs.
|
|
* Check4EnoughMemory() returns DtEDITOR_NO_ERRORS,
|
|
* DtEDITOR_ILLEGAL_SIZE, or DtEDITOR_INSUFFICIENT_MEMORY.
|
|
*/
|
|
loadError = Check4EnoughMemory( file_length );
|
|
if (loadError == DtEDITOR_INSUFFICIENT_MEMORY)
|
|
returnVal = loadError;
|
|
else {
|
|
|
|
/*
|
|
* Read the file contents (with room for null) & convert to a
|
|
* string. We want to use a string because the
|
|
* DtEditorSetContents/Append/Insert/... functions create another
|
|
* copy of the data before actually putting it into the widget.
|
|
*/
|
|
char *file_string = (char*) XtMalloc(file_length + 1);
|
|
file_length = fread(file_string, sizeof(char), file_length, fp);
|
|
file_string[file_length] = '\0';
|
|
|
|
/*
|
|
* Strip out any embedded NULLs because the text widget will only
|
|
* accept data up to the first NULL.
|
|
*
|
|
* StripEmbeddedNulls() returns DtEDITOR_NO_ERRORS or
|
|
* DtEDITOR_NULLS_REMOVED
|
|
*/
|
|
loadError = StripEmbeddedNulls( file_string, &file_length );
|
|
if ( loadError != DtEDITOR_NO_ERRORS )
|
|
returnVal = loadError;
|
|
|
|
/*
|
|
* Insert it as a string, otherwise the following DtEditor*()
|
|
* functions will make another copy of the data.
|
|
*/
|
|
cr.type = DtEDITOR_TEXT;
|
|
cr.value.string = file_string;
|
|
|
|
|
|
/*
|
|
* Load, insert, append, or attach the file, as specified
|
|
*/
|
|
switch( action )
|
|
{
|
|
case LOAD_DATA:
|
|
{
|
|
loadError = DtEditorSetContents ( w, &cr );
|
|
break;
|
|
}
|
|
|
|
case INSERT_DATA:
|
|
{
|
|
loadError = DtEditorInsert ( w, &cr );
|
|
break;
|
|
}
|
|
|
|
case APPEND_DATA:
|
|
{
|
|
loadError = DtEditorAppend ( w, &cr );
|
|
break;
|
|
}
|
|
|
|
case REPLACE_DATA:
|
|
{
|
|
loadError = DtEditorReplace(w, startReplace, endReplace, &cr);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
}
|
|
} /* end switch */
|
|
|
|
if ( loadError != DtEDITOR_NO_ERRORS )
|
|
returnVal = loadError;
|
|
|
|
/*
|
|
* The file is loaded, clean up.
|
|
*/
|
|
XtFree( file_string );
|
|
|
|
} /* end there is enough memory */
|
|
|
|
/* Close the file */
|
|
fclose(fp);
|
|
|
|
} /* end if a file is open */
|
|
|
|
return( returnVal );
|
|
|
|
} /* end LoadFile */
|
|
|
|
|
|
static char *
|
|
StringAdd(
|
|
char *destination,
|
|
char *source,
|
|
int number)
|
|
{
|
|
memcpy(destination, source, number);
|
|
destination[number] = (char)'\0';
|
|
destination += number;
|
|
return destination;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* CopySubstring - copies out a portion of the text, optionally
|
|
* adding newlines at any and all wordwrap-caused
|
|
* "virtual" lines.
|
|
*
|
|
* Inputs: widget from which we get the data to write;
|
|
* startPos determines the first character to write out;
|
|
* endPos determines the last character to write out;
|
|
* buf is the character buffer into which we write. It
|
|
* is assumed to be large enough - be careful.
|
|
* addNewlines specifies whether to add '/n' to "virtual" lines.
|
|
* Returns Nuthin'
|
|
*
|
|
*
|
|
***************************************************************************/
|
|
|
|
static char *
|
|
CopySubstring(
|
|
XmTextWidget widget,
|
|
XmTextPosition startPos,
|
|
XmTextPosition endPos,
|
|
char *buf,
|
|
Boolean addNewlines)
|
|
{
|
|
XmTextLineTable line_table = widget->text.line_table;
|
|
int currLine, firstLine;
|
|
char *pString, *pCurrChar, *pLastChar;
|
|
int numToCopy;
|
|
|
|
if(startPos < 0)
|
|
startPos = 0;
|
|
if(startPos > widget->text.last_position)
|
|
startPos = widget->text.last_position;
|
|
|
|
if(endPos < 0)
|
|
endPos = 0;
|
|
if(endPos > widget->text.last_position)
|
|
endPos = widget->text.last_position;
|
|
|
|
if(startPos > endPos)
|
|
return buf;
|
|
|
|
pString = XmTextGetString((Widget)widget);
|
|
|
|
if(addNewlines == False)
|
|
{
|
|
pCurrChar = _DtEditorGetPointer(pString, startPos);
|
|
pLastChar = _DtEditorGetPointer(pString, endPos);
|
|
numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX);
|
|
|
|
buf = StringAdd(buf, pCurrChar, numToCopy);
|
|
}
|
|
else
|
|
{
|
|
int *mb_str_loc, total, z, siz;
|
|
char *bptr;
|
|
|
|
mb_str_loc = (int *) XtMalloc(sizeof(int) * ((endPos-startPos)+1));
|
|
if (NULL == mb_str_loc)
|
|
{
|
|
/* Should figure out some way to pass back an error code. */
|
|
buf = CopySubstring(widget, startPos, endPos, buf, False);
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* mb_str_loc[] is being used to replace the call
|
|
* to _DtEditorGetPointer. That function used
|
|
* mbtowc() to count the number of chars between the
|
|
* beginning of pString and startChar. The problem
|
|
* was that it sat in a loop and was also called for
|
|
* every line, so it was SLOW. Now, we count once
|
|
* and store the results in mb_str_loc[].
|
|
*/
|
|
|
|
/* Because startPos may not always == 0: */
|
|
/* mb_str_loc[0] = startPos */
|
|
/* mb_str_loc[endPos - startPos] = endPos */
|
|
/* */
|
|
/* So when accessing items, dereference off of */
|
|
/* startPos. */
|
|
|
|
mb_str_loc[0] = 0;
|
|
for(total=0, bptr=pString, z=1;
|
|
z <= (endPos - startPos); bptr += siz, z++)
|
|
{
|
|
if (MB_CUR_MAX > 1)
|
|
{
|
|
if ( (siz = mblen(bptr, MB_CUR_MAX)) < 0)
|
|
{
|
|
siz = 1;
|
|
total += 1;
|
|
}
|
|
else
|
|
total += siz;
|
|
}
|
|
else
|
|
{
|
|
siz = 1;
|
|
total += 1;
|
|
}
|
|
|
|
mb_str_loc[z] = total;
|
|
}
|
|
|
|
|
|
firstLine = currLine = _DtEditorGetLineIndex(widget, startPos);
|
|
do
|
|
{
|
|
if(startPos > (XmTextPosition)line_table[currLine].start_pos)
|
|
pCurrChar = pString + mb_str_loc[0];
|
|
else
|
|
{
|
|
z = line_table[currLine].start_pos;
|
|
pCurrChar = pString +
|
|
mb_str_loc[z - startPos];
|
|
}
|
|
|
|
if(addNewlines == True && currLine > firstLine &&
|
|
line_table[currLine].virt_line != 0)
|
|
{
|
|
buf[0] = (char)'\n';
|
|
buf[1] = (char)'\0';
|
|
buf++;
|
|
}
|
|
|
|
if(currLine >= (widget->text.total_lines - 1))
|
|
pLastChar = pString +
|
|
mb_str_loc[endPos - startPos];
|
|
else if((XmTextPosition)line_table[currLine + 1].start_pos <= endPos)
|
|
{
|
|
z = line_table[currLine+1].start_pos - 1;
|
|
pLastChar = pString +
|
|
mb_str_loc[z - startPos];
|
|
}
|
|
else
|
|
pLastChar = pString +
|
|
mb_str_loc[endPos - startPos];
|
|
|
|
numToCopy = pLastChar - pCurrChar + mblen(pLastChar, MB_CUR_MAX);
|
|
|
|
buf = StringAdd(buf, pCurrChar, numToCopy);
|
|
|
|
currLine++;
|
|
} while(currLine < widget->text.total_lines &&
|
|
(XmTextPosition)line_table[currLine].start_pos <= endPos);
|
|
XtFree((char*)mb_str_loc);
|
|
}
|
|
|
|
XtFree(pString);
|
|
return buf;
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
* _DtEditorCopyDataOut - Writes the entire text editor buffer contents to
|
|
* the specified character array.
|
|
*
|
|
* Inputs: tw, to supply the data.
|
|
* buf, specifying the array to which to write the data.
|
|
*
|
|
*************************************************************************/
|
|
|
|
static char *
|
|
_DtEditorCopyDataOut(
|
|
XmTextWidget tw,
|
|
char *buf,
|
|
Boolean addNewlines)
|
|
{
|
|
buf = CopySubstring(tw, 0, tw->text.last_position, buf, addNewlines);
|
|
|
|
return buf;
|
|
}
|
|
|
|
static DtEditorErrorCode
|
|
getStringValue(
|
|
DtEditorWidget editor,
|
|
char **buf,
|
|
Boolean insertNewlines)
|
|
{
|
|
XmTextWidget tw = (XmTextWidget) M_text(editor);
|
|
int bufSize;
|
|
char *lastChar;
|
|
DtEditorErrorCode returnVal = DtEDITOR_NO_ERRORS;
|
|
|
|
/*
|
|
* Calculate the size of the buffer we need for the data.
|
|
* 1. Start with MB_CUR_MAX for each char in the text.
|
|
* 3. Add in 1 char for each line, if we have to insert newlines.
|
|
* 4. Add 1 for a terminating NULL.
|
|
*/
|
|
bufSize = tw->text.last_position * MB_CUR_MAX;
|
|
if(insertNewlines == True)
|
|
bufSize += tw->text.total_lines;
|
|
bufSize += 1;
|
|
|
|
returnVal = Check4EnoughMemory(bufSize);
|
|
if (DtEDITOR_NO_ERRORS != returnVal) return returnVal;
|
|
|
|
*buf = (char *) XtMalloc(bufSize);
|
|
lastChar = _DtEditorCopyDataOut(tw, *buf, insertNewlines);
|
|
|
|
return returnVal;
|
|
} /* end getStringValue */
|
|
|
|
|
|
static DtEditorErrorCode
|
|
getDataValue(
|
|
DtEditorWidget editor,
|
|
void **buf,
|
|
unsigned int *size,
|
|
Boolean insertNewlines)
|
|
{
|
|
DtEditorErrorCode error;
|
|
|
|
error = getStringValue(editor, (char **)buf, insertNewlines);
|
|
*size = strlen( *buf ); /* remember, strlen doesn't count \0 at end */
|
|
|
|
return( error );
|
|
|
|
} /* end getDataValue */
|
|
|
|
|
|
static DtEditorErrorCode
|
|
getWcharValue(
|
|
DtEditorWidget editor,
|
|
wchar_t **data,
|
|
Boolean insertNewlines)
|
|
{
|
|
DtEditorErrorCode error;
|
|
char *mb_value;
|
|
wchar_t *pWchar_value;
|
|
int num_char, result;
|
|
size_t nbytes;
|
|
|
|
error = getStringValue(editor, &mb_value, insertNewlines);
|
|
|
|
if (error == DtEDITOR_NO_ERRORS)
|
|
{
|
|
/*
|
|
* Allocate space for the wide character string
|
|
*/
|
|
num_char = _DtEditor_CountCharacters(mb_value, strlen(mb_value)) + 1;
|
|
nbytes = (size_t) num_char * sizeof(wchar_t);
|
|
|
|
error = Check4EnoughMemory(nbytes);
|
|
if (DtEDITOR_NO_ERRORS != error) return error;
|
|
pWchar_value = (wchar_t*) XtMalloc(nbytes);
|
|
|
|
/*
|
|
* Convert the multi-byte string to wide character
|
|
*/
|
|
result = mbstowcs(pWchar_value, mb_value, num_char*sizeof(wchar_t) );
|
|
if (result < 0) pWchar_value[0] = 0L;
|
|
*data = pWchar_value;
|
|
|
|
XtFree( mb_value );
|
|
}
|
|
|
|
return( error );
|
|
} /* end getWcharValue */
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorGetContents - gets the contents of the DtEditor widget.
|
|
*
|
|
* Inputs: widget to retrieve the contents
|
|
*
|
|
* pointer to a data structure indicating how the retrieved
|
|
* data should be formatted. Depending upon the type of format,
|
|
* this structure will contain various fields:
|
|
* string - a NULL pointer (char *) to hold the data
|
|
* a new container will be created.
|
|
* data - void pointer to hold the data, unsigned int for the
|
|
* size of the data,
|
|
* a Boolean indicating whether Newline characters should be
|
|
* inserted at the end of each line, in string format.
|
|
* a Boolean indicating whether the the unsaved changes
|
|
* flag should be cleared. There may be times when an
|
|
* application will want to request a copy of the contents
|
|
* without effecting whether DtEditorCheckForUnsavedChanges
|
|
* reports there are unsaved changes.
|
|
*
|
|
* Returns 0 - contents were retrieved sucessfully
|
|
* !0 - an error occurred while retrieving the contents
|
|
*
|
|
* The structure passed in will be set according to the
|
|
* requested format:
|
|
* string - a \0-terminated string of characters with
|
|
* optional Newlines
|
|
* container - handle to a Bento container
|
|
* data - the data, the size of the data
|
|
*
|
|
* The application is responsible for free'ing any data in the
|
|
* above structure.
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorGetContents(
|
|
Widget widget,
|
|
DtEditorContentRec *data,
|
|
Boolean hardCarriageReturns,
|
|
Boolean markContentsAsSaved )
|
|
{
|
|
DtEditorErrorCode error = DtEDITOR_INVALID_TYPE;
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
switch( data->type )
|
|
{
|
|
case DtEDITOR_TEXT:
|
|
{
|
|
error = getStringValue( editor, &(data->value.string),
|
|
hardCarriageReturns );
|
|
break;
|
|
}
|
|
|
|
case DtEDITOR_DATA:
|
|
{
|
|
error = getDataValue( editor, &(data->value.data.buf),
|
|
&(data->value.data.length),
|
|
hardCarriageReturns );
|
|
break;
|
|
}
|
|
|
|
case DtEDITOR_WCHAR:
|
|
{
|
|
error = getWcharValue( editor, &(data->value.wchar),
|
|
hardCarriageReturns );
|
|
break;
|
|
}
|
|
|
|
default :
|
|
{
|
|
error = DtEDITOR_INVALID_TYPE;
|
|
}
|
|
} /* end switch */
|
|
|
|
/*
|
|
* If there were no errors, mark there are now no unsaved changes (unless
|
|
* we were told not to).
|
|
*/
|
|
if ( error == DtEDITOR_NO_ERRORS && markContentsAsSaved == True )
|
|
M_unreadChanges( editor ) = False;
|
|
|
|
_DtAppUnlock(app);
|
|
return( error );
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* DtEditorSaveContentsToFile - saves the contents of the DtEditor
|
|
* widget to a disc file as string/data
|
|
* or a CDE Document (Bento container).
|
|
*
|
|
* Inputs: widget to retrieve the contents
|
|
*
|
|
* filename - name of the file to read
|
|
* a Boolean indicating whether the file should be
|
|
* overwritten if it currently exists.
|
|
* a Boolean indicating whether Newline characters should be
|
|
* inserted at the end of each line (string format only).
|
|
* a Boolean indicating whether the the unsaved changes
|
|
* flag should be cleared. There may be times when an
|
|
* application will want to request a copy of the contents
|
|
* without effecting whether DtEditorCheckForUnsavedChanges
|
|
* reports there are unsaved changes.
|
|
*
|
|
* Returns DtEDITOR_NO_ERRORS - contents were saved sucessfully
|
|
* DtEDITOR_UNWRITABLE_FILE - file is write protected
|
|
* DtEDITOR_WRITABLE_FILE - file exists and the
|
|
* overwriteIfExists parameter is False.
|
|
* DtEDITOR_SAVE_FAILED - write to the file failed; check
|
|
* disk space, etc.
|
|
* OR any errors from DtEditorGetContents
|
|
*
|
|
***************************************************************************/
|
|
|
|
extern DtEditorErrorCode
|
|
DtEditorSaveContentsToFile(
|
|
Widget widget,
|
|
char *fileName,
|
|
Boolean overwriteIfExists,
|
|
Boolean hardCarriageReturns,
|
|
Boolean markContentsAsSaved )
|
|
{
|
|
|
|
FILE *pFile;
|
|
DtEditorContentRec cr; /* Structure for retrieving contents of widget */
|
|
DtEditorWidget editor = (DtEditorWidget) widget;
|
|
|
|
DtEditorErrorCode error = DtEDITOR_INVALID_FILENAME;
|
|
_DtWidgetToAppContext(widget);
|
|
_DtAppLock(app);
|
|
|
|
/*
|
|
* First, make sure we were given a name
|
|
*/
|
|
|
|
if (fileName && *fileName )
|
|
{
|
|
|
|
/*
|
|
* Can we save to the file?
|
|
*/
|
|
error = _DtEditorValidateFileAccess( fileName, WRITE_ACCESS );
|
|
|
|
if( error == DtEDITOR_NO_ERRORS ||
|
|
error == DtEDITOR_NONEXISTENT_FILE ||
|
|
error == DtEDITOR_WRITABLE_FILE )
|
|
{
|
|
/*
|
|
* Don't overwrite an existing file if we've been told not to
|
|
*/
|
|
if( error == DtEDITOR_WRITABLE_FILE && overwriteIfExists == False )
|
|
{
|
|
_DtAppUnlock(app);
|
|
return( error );
|
|
}
|
|
|
|
/*
|
|
* Open the file for writing
|
|
*/
|
|
if ( (pFile = fopen(fileName, "w")) == NULL )
|
|
{
|
|
_DtAppUnlock(app);
|
|
return( DtEDITOR_UNWRITABLE_FILE );
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Save the unsaved changes flag so we can restore it if the write
|
|
* to the file fails.
|
|
*/
|
|
Boolean saved_state = M_unreadChanges( editor );
|
|
|
|
/*
|
|
* Now, get the contents of the widget and write it to the file,
|
|
* depending upon the format requested.
|
|
*/
|
|
|
|
cr.type = DtEDITOR_DATA;
|
|
error = DtEditorGetContents( widget, &cr, hardCarriageReturns,
|
|
markContentsAsSaved );
|
|
|
|
if ( error == DtEDITOR_NO_ERRORS )
|
|
{
|
|
|
|
/*
|
|
* Write it to the file
|
|
*/
|
|
size_t size_written = fwrite( cr.value.data.buf, 1,
|
|
cr.value.data.length, pFile );
|
|
|
|
if( cr.value.data.length != size_written )
|
|
error = DtEDITOR_SAVE_FAILED;
|
|
|
|
XtFree( cr.value.data.buf );
|
|
}
|
|
|
|
fclose(pFile);
|
|
|
|
if( error == DtEDITOR_SAVE_FAILED )
|
|
{
|
|
/*
|
|
* Restore the unsaved changes flag since the save failed
|
|
*/
|
|
M_unreadChanges( editor ) = saved_state;
|
|
}
|
|
|
|
} /* end file is writable */
|
|
|
|
} /* end filename is valid */
|
|
|
|
}
|
|
_DtAppUnlock(app);
|
|
return( error );
|
|
|
|
} /* end DtEditorSaveContentsToFile */
|
|
|
|
|
|
/*
|
|
* _DtEditorGetPointer returns a pointer to the _character_
|
|
* numbered by startChar within the string pString.
|
|
* It accounts for possible multibyte chars.
|
|
*/
|
|
char *
|
|
_DtEditorGetPointer(
|
|
char *pString,
|
|
int startChar)
|
|
{
|
|
char *bptr;
|
|
int curChar, char_size;
|
|
|
|
if(MB_CUR_MAX > 1)
|
|
{
|
|
for(bptr = pString, curChar = 0;
|
|
curChar < startChar && *bptr != (char)'\0';
|
|
curChar++, bptr += char_size)
|
|
{
|
|
if ( (char_size = mblen(bptr, MB_CUR_MAX)) < 0)
|
|
char_size = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bptr = pString + startChar;
|
|
}
|
|
return bptr;
|
|
} /* end _DtEditorGetPointer */
|
|
|