|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 36182 (0x8d56)
Types: TextFile
Names: »sxEntry.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦526ad3590⟧ »EUUGD11/gnu-31mar87/X.V10.R4.tar.Z«
└─⟦2109abc41⟧
└─⟦this⟧ »./X.V10R4/Toolkit/Sx/code/sxEntry.c«
/*
* $Source: /u1/Sx.new/code/RCS/sxEntry.c,v $
* $Header: sxEntry.c,v 1.1 86/12/03 16:09:57 swick Exp $
*/
#ifndef lint
static char *rcsid_sxEntry_c = "$Header: sxEntry.c,v 1.1 86/12/03 16:09:57 swick Exp $";
#endif lint
/*
* sxEntry.c --
*
* This file contains the code that implements text entry
* subwindows for the Sx package. Text entries are windows
* that display a label and allow the user to type in (and
* edit) a textual value to associate with the label.
*
* Copyright (C) 1986 Regents of the University of California.
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California makes
* no representations about the suitability of this software for
* any purpose. It is provided "as is" without express or
* implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: sxEntry.c,v 1.1 86/12/03 16:09:57 swick Exp $ SPRITE (Berkeley)";
#endif not lint
#include <sys/types.h>
#include <X/Xlib.h>
#include "sprite.h"
#include "char.h"
#include "mem.h"
#include "string.h"
#include "sx.h"
#include "sxInt.h"
/*
* A record of the following type is used to store information about
* each entry.
*/
typedef struct {
Window w; /* Window used by entry. */
char *label; /* Fixed label for entry (storage is
* dynamically allocated). If NULL, then
* no label is displayed. */
char *text; /* Pointer to client-owned space in which
* to store user type-in. */
int textSize; /* Number of bytes available for non-null
* characters at text (actual space is this
* + 1, to account for null terminator). */
FontInfo *fontPtr; /* Font to use for displaying entry. */
int x; /* X-location at which to display left edge
* of first character in text. */
int y; /* Y-location at which to display text.
* Recomputed each time window changes size. */
int foreground; /* Foreground color for display. */
int background; /* Background color for display. */
int caret; /* Index of character whose left edge is
* the caret position. Always >= 0. */
int caretX, caretY; /* Location (in pixels, in w) of upper-left
* corner of caret pixmap. */
int selectFirst; /* First character that's selected. -1
* means nothing's selected. */
int selectLast; /* Last character that's selected. -1
* means nothing's selected. */
int selectAnchor; /* The right mouse button adjusts one end
* of the selection; this gives the position
* of the end that's fixed. */
int flags; /* Miscellaneous flag values: see below. */
} Entry;
/*
* Flags for entries:
*
* FOCUS_WINDOW: 1 means that this window has the focus.
* CARET_OFF: 1 means that the caret has been turned off,
* pending other changes to the window.
*/
#define FOCUS_WINDOW 1
#define CARET_OFF 2
/*
* How much space to leave between label and left edge of window:
*/
#define LEFT_MARGIN 2
/*
* The cursor used when the pointer is in entry windows:
*/
#include "cursors/ptr"
static Cursor cursor = 0;
/*
* Bitmaps used to draw the caret:
*/
#include "caret.bits"
#include "caretMask.bits"
static Bitmap caretBitmap;
static Bitmap caretMaskBitmap;
/*
* Hash table used to map from X window ids to Entry structures:
*/
static XAssocTable *entryTable;
extern XAssocTable *XCreateAssocTable();
/*
* Forward references to things defined in this file:
*/
extern void EntryDelete();
extern void EntryDestroyProc();
extern void EntryDrawCaret();
extern void EntryEraseCaret();
extern void EntryExposeProc();
extern Boolean EntryFindChar();
extern void EntryInit();
extern void EntryInsert();
extern void EntryKeyProc();
extern void EntryMouseProc();
extern void EntryRedisplay();
extern void EntrySelChanged();
extern int EntrySelGet();
\f
/*
*----------------------------------------------------------------------
*
* Sx_EntryMake --
*
* Make a window into an entry subwindow. An entry is a label
* with space after it for typing in text. The text appears in
* a string area provided by the caller.
*
* Results:
* None.
*
* Side effects:
* From this moment on, the entire area of window will be
* used to display the entry. The contents of text will
* change spontaneously as the user invokes operations in
* the entry window.
*
*----------------------------------------------------------------------
*/
void
Sx_EntryMake(window, label, fontPtr, foreground, background, text, size)
Window window; /* Window to use for entry. If the window
* is already in use for an entry, this
* call can be used to change the text and
* other parameters (the window will be
* redisplayed). If the window isn't already
* an entry window, then it shouldn't yet be
* mapped (no redisplay will be done). */
char *label; /* Text to appear at left edge of window,
* labelling the entry. May be NULL. */
FontInfo *fontPtr; /* Font to use for displaying info in window,
* or NULL to use default font. */
int foreground; /* Color to use for displaying text. */
int background; /* Background color for window. */
char *text; /* Stuff that user types will be stored here,
* null-terminated. Caller must initialize. */
int size; /* Number of bytes of storage at text:
* determines longest string that will be
* accepted. */
{
register Entry *entryPtr;
Boolean redisplay;
EntryInit();
if (fontPtr == NULL) {
fontPtr = Sx_GetDefaultFont();
}
/*
* See if this window is already an entry window. If so, release
* the fields that will be reallocated. If not, allocate a new
* structure and do once-only initialization.
*/
entryPtr = (Entry *) XLookUpAssoc(entryTable, window);
if (entryPtr != NULL) {
redisplay = TRUE;
if (entryPtr->label != NULL) {
Mem_Free(entryPtr->label);
}
entryPtr->flags |= CARET_OFF;
} else {
redisplay = FALSE;
entryPtr = (Entry *) Mem_Alloc(sizeof(Entry));
entryPtr->w = window;
entryPtr->flags = CARET_OFF;
(void) Sx_HandlerCreate(window, ButtonPressed|ButtonReleased
|LeftDownMotion|RightDownMotion|EnterWindow|LeaveWindow,
EntryMouseProc, (ClientData) entryPtr);
(void) Sx_HandlerCreate(window, KeyPressed, EntryKeyProc,
(ClientData) entryPtr);
(void) Sx_HandlerCreate(window, ExposeWindow, EntryExposeProc,
(ClientData) entryPtr);
(void) Sx_HandlerCreate(window, SX_DESTROYED, EntryDestroyProc,
(ClientData) entryPtr);
XDefineCursor(window, cursor);
XMakeAssoc(entryTable, window, (caddr_t) entryPtr);
}
/*
* Reset the fields of the entry for the new parameters.
*/
if (label == NULL) {
entryPtr->label = NULL;
entryPtr->x = LEFT_MARGIN;
} else {
entryPtr->label = (char *) Mem_Alloc(String_Length(label) + 1);
String_Copy(label, entryPtr->label);
entryPtr->x = LEFT_MARGIN + XStringWidth(label, fontPtr, 0, 0);
}
entryPtr->text = text;
entryPtr->textSize = size - 1;
text[entryPtr->textSize] = 0; /* In case caller didn't terminate. */
entryPtr->fontPtr = fontPtr;
entryPtr->y = 0;
entryPtr->foreground = foreground;
entryPtr->background = background;
entryPtr->caret = 0;
entryPtr->selectFirst = -1;
entryPtr->selectLast = -1;
entryPtr->selectAnchor = -1;
if (redisplay) {
XPixSet(window, 0, 0, DisplayWidth(), DisplayHeight(),
entryPtr->background);
if (entryPtr->label != NULL) {
XText(window, LEFT_MARGIN, entryPtr->y, entryPtr->label,
String_Length(entryPtr->label), entryPtr->fontPtr->id,
entryPtr->foreground, entryPtr->background);
}
EntryRedisplay(entryPtr, 0, String_Length(entryPtr->text) - 1);
EntryDrawCaret(entryPtr);
}
}
\f
/*
*----------------------------------------------------------------------
*
* Sx_EntryCreate --
*
* Like Sx_EntryMake, except also create the window that will hold
* the entry.
*
* Results:
* The return value is the X id for a new window that behaves
* as an entry. It will have the given location and size in
* parent.
*
* Side effects:
* The area of the new window will be used to display the entry.
* The contents of text will change spontaneously as the user
* invokes operations in the entry window.
*
*----------------------------------------------------------------------
*/
Window
Sx_EntryCreate(parent, x, y, width, height, border, label, fontPtr, foreground,
background, text, size)
Window parent; /* Parent in which to create entry window. */
int x, y; /* Location of upper left corner of new
* window, in coords. of parent. */
int width, height; /* Dimensions of new window, in pixels. */
int border; /* Width of border for new window. */
char *label; /* Text to appear at left edge of window,
* labelling the entry. */
FontInfo *fontPtr; /* Font to use for displaying info in window,
* or NULL to use default font. */
int foreground; /* Color to use for displaying text. */
int background; /* Background color for window. */
char *text; /* Stuff that user types will be stored here,
* null-terminated. Caller must initialize. */
int size; /* Number of bytes of storage at text:
* determines longest string that will be
* accepted. */
{
Window w;
Pixmap borderPixmap;
if ((foreground == BlackPixel) || (border == 0)) {
borderPixmap = BlackPixmap;
} else if (foreground == WhitePixel) {
borderPixmap = WhitePixmap;
} else {
borderPixmap = XMakeTile(foreground);
if (borderPixmap == NULL) {
Sx_Panic("Sx_EntryCreate: couldn't create border pixmap.");
}
}
w = XCreateWindow(parent, x, y, width, height, border, borderPixmap,
WhitePixmap);
if (w == NULL) {
Sx_Panic("Sx_EntryCreate: couldn't create new window.");
}
if ((borderPixmap != BlackPixmap) && (borderPixmap != WhitePixmap)) {
XFreePixmap(borderPixmap);
}
Sx_EntryMake(w, label, fontPtr, foreground, background, text, size);
return w;
}
\f
/*
*----------------------------------------------------------------------
*
* EntryExposeProc --
*
* This procedure is called by the Sx dispatcher whenever an
* ExposeWindow event occurs for an Entry window.
*
* Results:
* None.
*
* Side effects:
* The entry gets redisplayed.
*
*----------------------------------------------------------------------
*/
static void
EntryExposeProc(entryPtr, eventPtr)
register Entry *entryPtr; /* Entry for which event occurred. */
XExposeEvent *eventPtr; /* Event that occurred. */
{
if (eventPtr->subwindow != NULL) {
return;
}
entryPtr->y = (eventPtr->height - entryPtr->fontPtr->height)/2;
XPixSet(entryPtr->w, 0, 0, DisplayWidth(), DisplayHeight(),
entryPtr->background);
if (entryPtr->label != NULL) {
XText(entryPtr->w, LEFT_MARGIN, entryPtr->y, entryPtr->label,
String_Length(entryPtr->label), entryPtr->fontPtr->id,
entryPtr->foreground, entryPtr->background);
}
EntryRedisplay(entryPtr, 0, String_Length(entryPtr->text) - 1);
EntryDrawCaret(entryPtr);
}
\f
/*
*----------------------------------------------------------------------
*
* EntryMouseProc --
*
* This procedure is invoked by the Sx dispatcher whenever the
* mouse enters or leaves an entry window, whenever a mouse button
* goes down or up, and whenever the mouse moves with a button
* down.
*
* Results:
* None.
*
* Side effects:
* The selection and caret get adjusted in response to button
* presses.
*
*----------------------------------------------------------------------
*/
static void
EntryMouseProc(entryPtr, eventPtr)
register Entry *entryPtr; /* Entry for which event occurred. */
register XButtonEvent *eventPtr; /* Event that occurred. */
{
int oldFirst, oldLast, tmp, index;
Boolean rightSide;
Boolean setCaret = FALSE;
static int button = -1;
static int lastX, lastY; /* Last place where left button
* was clicked. Used to detect
* multiple clicks. */
static Entry *lastEntryPtr; /* Last entry clicked in. Also used
* to detect multiple clicks. */
static int repeatCount = 0; /* Count of multiple clicks. */
if (eventPtr->subwindow != NULL) {
return;
}
/*
* When entering the window, turn on the caret. Turn it off
* again when leaving the window.
*/
if ((eventPtr->type == EnterWindow)
&& ((eventPtr->detail & 0x77) != VirtualCrossing)) {
entryPtr->flags |= FOCUS_WINDOW;
EntryDrawCaret(entryPtr);
return;
}
if ((eventPtr->type == LeaveWindow)
&& ((eventPtr->detail & 0x77) != VirtualCrossing)) {
entryPtr->flags &= ~FOCUS_WINDOW;
EntryEraseCaret(entryPtr);
return;
}
/*
* When the button goes up, release the grab we put on the mouse.
*/
if (eventPtr->type == ButtonReleased) {
if ((eventPtr->detail & 07) == button) {
Sx_UngrabMouse();
button = -1;
}
return;
}
/*
* When the first button goes down, put a grab on the mouse.
* Ignore mouse events if we didn't see the down event. Count
* successive clicks in the same place.
*/
if (eventPtr->type == ButtonPressed) {
button = eventPtr->detail & 07;
if (button == LeftButton) {
Sx_GrabMouse(entryPtr->w, cursor, ButtonReleased|LeftDownMotion);
if ((lastEntryPtr == entryPtr)
&& ((lastX + 1) >= eventPtr->x)
&& ((lastX - 1) <= eventPtr->x)
&& ((lastY + 1) >= eventPtr->y)
&& ((lastY - 1) <= eventPtr->y)) {
repeatCount += 1;
if (repeatCount > 2) {
repeatCount = 0;
}
} else {
repeatCount = 0;
}
lastX = eventPtr->x;
lastY = eventPtr->y;
lastEntryPtr = entryPtr;
} else if (button == RightButton) {
Sx_GrabMouse(entryPtr->w, cursor, ButtonReleased|RightDownMotion);
} else {
button = -1;
return;
}
} else {
if (button == -1) {
return;
}
}
/*
* A mouse button is down. If it's the left button, or if it's
* the right button and the selection isn't in this window, then
* start a new selection. Also set the caret unless the shift key
* is down. (It's a little easier to see what you're pointing to
* if the mouse doesn't have to be right on top of it to select it.
* Offset the hot spot to accomplish this).
*/
rightSide = EntryFindChar(entryPtr, eventPtr->x - 2, &index);
if (index < 0) {
return;
}
oldFirst = entryPtr->selectFirst;
oldLast = entryPtr->selectLast;
if (oldFirst < 0) {
Sx_SelectionSet(EntrySelGet, EntrySelChanged, (ClientData) entryPtr);
}
if ((oldFirst < 0) || (button == LeftButton)) {
entryPtr->selectFirst = entryPtr->selectLast = index;
entryPtr->selectAnchor = index;
if ((eventPtr->detail & ShiftMask) == 0) {
setCaret = TRUE;
}
goto setSelection;
} else if (button == RightButton) {
if (index >= entryPtr->selectAnchor) {
entryPtr->selectFirst = entryPtr->selectAnchor;
entryPtr->selectLast = index;
} else {
entryPtr->selectFirst = index;
entryPtr->selectLast = entryPtr->selectAnchor;
}
}
/*
* Depending on how many clicks there have been, round the selection up:
* 1st click: no rounding
* 2nd click: round to word boundary
* 3rd click: round to line boundary
*/
setSelection:
switch(repeatCount) {
case 1: {
static char wordMask[16] = {0, 0, 0, 0, 0, 0, 0xff, 0x3,
0xfe, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x3};
static char bit[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
register char c;
c = entryPtr->text[entryPtr->selectFirst];
if (!(c & 0200) && (wordMask[c>>3] & bit[c&07])) {
while (entryPtr->selectFirst > 0) {
entryPtr->selectFirst--;
c = entryPtr->text[entryPtr->selectFirst];
if ((c & 0200) || !(wordMask[c>>3] & bit[c&07])) {
entryPtr->selectFirst++;
break;
}
}
}
c = entryPtr->text[entryPtr->selectLast];
if ((c != 0) && !(c & 0200) && (wordMask[c>>3] & bit[c&07])) {
while (TRUE) {
entryPtr->selectLast++;
c = entryPtr->text[entryPtr->selectLast];
if ((c == 0) || (c & 0200) ||
!(wordMask[c>>3] & bit[c&07])) {
entryPtr->selectLast--;
break;
}
}
}
break;
}
case 2: {
entryPtr->selectFirst = 0;
entryPtr->selectLast = String_Length(entryPtr->text) - 1;
break;
}
}
/*
* Set the caret (if necessary) to the beginning or end of the
* selection, whichever is closer.
*/
if (setCaret) {
int newCaret, tmp;
tmp = (2*index) - (entryPtr->selectFirst + entryPtr->selectLast);
if ((tmp > 0) || ((tmp == 0) && rightSide)) {
newCaret = entryPtr->selectLast + 1;
} else {
newCaret = entryPtr->selectFirst;
}
if (newCaret != entryPtr->caret) {
EntryEraseCaret(entryPtr);
entryPtr->caret = newCaret;
}
}
/*
* Compute how much to redisplay. Don't redisplay the part of
* the selection that was selected before.
*/
if (oldFirst >= 0) {
if (oldFirst < entryPtr->selectFirst) {
tmp = oldLast;
if (tmp >= entryPtr->selectFirst) {
tmp = entryPtr->selectFirst-1;
}
EntryEraseCaret(entryPtr);
EntryRedisplay(entryPtr, oldFirst, tmp);
}
if (oldLast > entryPtr->selectLast) {
tmp = oldFirst;
if (tmp <= entryPtr->selectLast) {
tmp = entryPtr->selectLast + 1;
}
EntryEraseCaret(entryPtr);
EntryRedisplay(entryPtr, tmp, oldLast);
}
}
if (entryPtr->selectFirst >= 0) {
if (entryPtr->selectFirst < oldFirst) {
tmp = entryPtr->selectLast;
if (tmp >= oldFirst) {
tmp = oldFirst-1;
}
EntryEraseCaret(entryPtr);
EntryRedisplay(entryPtr, entryPtr->selectFirst, tmp);
}
if (entryPtr->selectLast > oldLast) {
tmp = entryPtr->selectFirst;
if (tmp <= oldLast) {
tmp = oldLast+1;
}
EntryEraseCaret(entryPtr);
EntryRedisplay(entryPtr, tmp, entryPtr->selectLast);
}
}
EntryDrawCaret(entryPtr);
}
\f
/*
*----------------------------------------------------------------------
*
* EntryKeyProc --
*
* This procedure is invoked by the Sx dispatcher whenever a
* key is pressed in an entry window.
*
* Results:
* None.
*
* Side effects:
* The text in the window is modified.
*
*----------------------------------------------------------------------
*/
static void
EntryKeyProc(entryPtr, eventPtr)
register Entry *entryPtr; /* Entry for which event occurred. */
XKeyEvent *eventPtr; /* Event that occurred. */
{
char *keyString;
char insert[2];
int nBytes;
if (eventPtr->subwindow != NULL) {
return;
}
/*
* Convert the weird raw key value to an ASCII string. For
* each character, insert it if it's printable. Process
* line-editing control characters specially, and throw
* away anything else.
*/
keyString = XLookupMapping(eventPtr, &nBytes);
if (nBytes == 0) {
return;
}
insert[1] = 0;
EntryEraseCaret(entryPtr);
for ( ; *keyString != 0; keyString++) {
if (Char_IsPrint(*keyString)) {
insert[0] = *keyString;
EntryInsert(entryPtr, insert, entryPtr->caret);
} else if (*keyString == '\03') {
/*
* Control-C: copy the selection to the insertion point.
*/
#define MAX_AT_ONCE 50
char selection[MAX_AT_ONCE+1], format[SX_FORMAT_SIZE];
int bytesThisTime, offset;
for (offset = 0; entryPtr->caret < entryPtr->textSize;
offset += bytesThisTime) {
bytesThisTime = Sx_SelectionGet("text", offset, MAX_AT_ONCE,
selection, format);
if ((bytesThisTime <= 0)
|| (String_Compare(format, "text") != 0)) {
break;
}
selection[bytesThisTime] = 0;
EntryInsert(entryPtr, selection, entryPtr->caret);
}
} else if (*keyString == '\04') {
/*
* Control-D: delete the selection, if it's in this window.
*/
if (entryPtr->selectFirst >= 0) {
EntryDelete(entryPtr, entryPtr->selectFirst,
entryPtr->selectLast);
}
} else if ((*keyString == '\b') || (*keyString == '\177')) {
/*
* Control-H or delete: erase a character.
*/
if (entryPtr->caret != 0) {
EntryDelete(entryPtr, entryPtr->caret-1, entryPtr->caret-1);
}
} else if (*keyString == '\25') {
/*
* Control-U: delete the whole line.
*/
if (entryPtr->caret != 0) {
EntryDelete(entryPtr, 0, entryPtr->caret-1);
}
} else if (*keyString == '\27') {
/*
* Control-W: delete the last word typed.
*/
int i;
Boolean seenNonSpace = FALSE;
for (i = entryPtr->caret-1; i >= 0; i--) {
if (Char_IsSpace(entryPtr->text[i])) {
if (seenNonSpace) {
break;
}
} else {
seenNonSpace = TRUE;
}
}
EntryDelete(entryPtr, i+1, entryPtr->caret-1);
}
}
EntryDrawCaret(entryPtr);
}
\f
/*
*----------------------------------------------------------------------
*
* EntryRedisplay --
*
* This procedure is called to redisplay part or all of the
* variable portion of an entry.
*
* Results:
* None.
*
* Side effects:
* The characters indexed first through last from the typed-in
* part of the entry are redisplayed. The entry's label is NOT
* redisplayed.
*
*----------------------------------------------------------------------
*/
static void
EntryRedisplay(entryPtr, first, last)
register Entry *entryPtr; /* What to redisplay. */
int first, last; /* Range of characters to redisplay. */
{
char savedChar;
int x, next;
/*
* Compute the starting location.
*/
savedChar = entryPtr->text[first];
entryPtr->text[first] = 0;
x = entryPtr->x + XStringWidth(entryPtr->text, entryPtr->fontPtr, 0, 0);
entryPtr->text[first] = savedChar;
/*
* Display the text in up to three chunks: one chunk that is
* to the left of the selected text, one chunk that is highlighted,
* and one chunk that is to the right of the selected text.
*/
if (entryPtr->selectFirst > first) {
next = entryPtr->selectFirst;
if (next > last) {
next = last+1;
}
XText(entryPtr->w, x, entryPtr->y, &entryPtr->text[first], next-first,
entryPtr->fontPtr->id, entryPtr->foreground,
entryPtr->background);
savedChar = entryPtr->text[next];
entryPtr->text[next] = 0;
x += XStringWidth(&entryPtr->text[first], entryPtr->fontPtr, 0, 0);
entryPtr->text[next] = savedChar;
first = next;
}
if (entryPtr->selectLast >= first) {
next = entryPtr->selectLast + 1;
if (next > last) {
next = last+1;
}
XText(entryPtr->w, x, entryPtr->y, &entryPtr->text[first], next-first,
entryPtr->fontPtr->id, entryPtr->background,
entryPtr->foreground);
savedChar = entryPtr->text[next];
entryPtr->text[next] = 0;
x += XStringWidth(&entryPtr->text[first], entryPtr->fontPtr, 0, 0);
entryPtr->text[next] = savedChar;
first = next;
}
if (first <= last) {
XText(entryPtr->w, x, entryPtr->y, &entryPtr->text[first],
last + 1 - first, entryPtr->fontPtr->id,
entryPtr->foreground, entryPtr->background);
}
}
\f
/*
*----------------------------------------------------------------------
*
* EntryInsert --
*
* Insert new characters into the middle of an entry, and
* redisplay them.
*
* Results:
* None.
*
* Side effects:
* The characters in string are inserted into the entry just
* before the "before"th character. The entry will be
* truncated if necessary to keep it from overflowing its
* allotted storage area.
*
*----------------------------------------------------------------------
*/
static void
EntryInsert(entryPtr, string, before)
register Entry *entryPtr; /* Entry to be modified. */
char *string; /* What to insert. */
int before; /* Which character to insert it in
* front of. */
{
int insertLength, suffixLength, spaceLeft;
register char *src, *dst;
char *insert;
/*
* This code is a bit tricky because of the space limitations on text.
* Compute how much space is left, then truncate the resulting new
* string if necessary to make it fit. This can involve chopping
* characters from the old string, or even ignoring some of the
* characters from the insert string.
*/
insert = &entryPtr->text[before];
suffixLength = String_Length(insert);
insertLength = String_Length(string);
spaceLeft = entryPtr->textSize - (before + insertLength + suffixLength);
if (spaceLeft < 0) {
suffixLength += spaceLeft;
spaceLeft = 0;
if (suffixLength < 0) {
insertLength += suffixLength;
suffixLength = 0;
if (insertLength == 0) {
return;
}
}
}
/*
* Move the tail of the current text to its new location, then
* copy the inserted text into the gap.
*/
dst = &entryPtr->text[entryPtr->textSize - spaceLeft];
*dst = 0;
dst--;
for (src = dst - insertLength; src >= insert; src--, dst--) {
*dst = *src;
}
String_NCopy(insertLength, string, insert);
/*
* Modify the selection and caret, if necessary, to keep their
* same positions relative to the text.
*/
if (entryPtr->selectFirst >= before) {
entryPtr->selectFirst += insertLength;
if (entryPtr->selectFirst >= entryPtr->textSize) {
entryPtr->selectFirst = entryPtr->selectLast = -1;
}
}
if (entryPtr->selectLast >= before) {
entryPtr->selectLast += insertLength;
if (entryPtr->selectLast >= entryPtr->textSize) {
entryPtr->selectLast = entryPtr->textSize - 1;
}
}
if (entryPtr->caret >= before) {
entryPtr->caret += insertLength;
if (entryPtr->caret > entryPtr->textSize) {
entryPtr->caret = entryPtr->textSize;
}
}
EntryRedisplay(entryPtr, before, before + insertLength + suffixLength);
}
\f
/*
*----------------------------------------------------------------------
*
* EntryDelete --
*
* Delete one or more characters from an entry.
*
* Results:
* None.
*
* Side effects:
* Characters indexed first to last are deleted from the text
* of entry. The screen is updated to reflect the change.
*
*----------------------------------------------------------------------
*/
static void
EntryDelete(entryPtr, first, last)
register Entry *entryPtr; /* Entry to be modified. */
int first, last; /* Range of characters to delete. */
{
int x;
/*
* Copy the tail of the string down over the deleted part, and
* update the selection and caret, if any.
*/
String_Copy(&entryPtr->text[last+1], &entryPtr->text[first]);
if (entryPtr->selectFirst >= first) {
if (entryPtr->selectFirst > last) {
entryPtr->selectFirst -= last+1-first;
} else {
entryPtr->selectFirst = first;
}
if (entryPtr->text[entryPtr->selectFirst] == 0) {
entryPtr->selectFirst = entryPtr->selectLast = -1;
}
}
if (entryPtr->selectLast >= first) {
if (entryPtr->selectLast > last) {
entryPtr->selectLast -= last+1-first;
} else {
entryPtr->selectLast = first-1;
}
if (entryPtr->selectLast < entryPtr->selectFirst) {
entryPtr->selectFirst = entryPtr->selectLast = -1;
}
}
if (entryPtr->caret > first) {
if (entryPtr->caret > last) {
entryPtr->caret -= last+1-first;
} else {
entryPtr->caret = first;
}
}
/*
* Redisplay the entry starting at the first byte deleted,
* and also clear the area where the tail of the string
* used to be displayed.
*/
EntryRedisplay(entryPtr, first, String_Length(entryPtr->text) - 1);
x = entryPtr->x + XStringWidth(entryPtr->text, entryPtr->fontPtr, 0, 0);
XPixSet(entryPtr->w, x, entryPtr->y, DisplayWidth(), DisplayHeight(),
entryPtr->background);
}
\f
/*
*----------------------------------------------------------------------
*
* EntryFindChar --
*
* Determine which character lies at a given position in an
* entry's window.
*
* Results:
* The word at *indexPtr is filled in with the index of the
* closest character in entry's text to the x-position given
* by x. The return value is TRUE if x is on the right side
* of the closest character, and FALSE if it's on the left.
* If entryPtr's text is empty, then -1 is stored at *indexPtr.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Boolean
EntryFindChar(entryPtr, x, indexPtr)
register Entry *entryPtr; /* Entry of interest. */
int x; /* Location in entryPtr's window. */
int *indexPtr; /* Where to store index of closest
* character to x. */
{
register char *p, c;
int curX, width;
register FontInfo *fontPtr = entryPtr->fontPtr;
for (curX = entryPtr->x, p = entryPtr->text, c = *p; c != 0; p++, c = *p) {
if (fontPtr->fixedwidth) {
width = fontPtr->width;
} else if ((c >= fontPtr->firstchar) && (c <= fontPtr->lastchar)) {
width = fontPtr->widths[c - fontPtr->firstchar];
} else {
width = 0;
}
curX += width;
if (curX > x) {
*indexPtr = p - entryPtr->text;
return ((curX - x) < (width/2));
}
}
*indexPtr = p - entryPtr->text - 1;
return TRUE;
}
\f
/*
*----------------------------------------------------------------------
*
* EntrySelGet --
*
* Called by the Sx selection package when someone wants to know
* what's selected.
*
* Results:
* See the documentation for Sx_SelectionGet.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static int
EntrySelGet(entryPtr, desiredFormat, firstByte, numBytes, selectionPtr,
formatPtr)
register Entry *entryPtr; /* Entry that contains selection. */
char *desiredFormat; /* Desired format of entry. This
* procedure can only handle text, so
* this parameter is ignored. */
int firstByte; /* Index of first desired byte. */
int numBytes; /* Max no. of bytes to return. */
char *selectionPtr; /* Store bytes of selection here. */
char *formatPtr; /* Store format of selection here. */
{
int bytesAvailable;
if (entryPtr->selectFirst < 0) {
return -1;
}
String_Copy("text", formatPtr);
bytesAvailable = entryPtr->selectLast + 1
- (entryPtr->selectFirst + firstByte);
if (numBytes > bytesAvailable) {
numBytes = bytesAvailable;
if (numBytes <= 0) {
return 0;
}
}
String_NCopy(numBytes, &entryPtr->text[entryPtr->selectFirst + firstByte],
selectionPtr);
return numBytes;
}
\f
/*
*----------------------------------------------------------------------
*
* EntrySelChanged --
*
* Called by Sx whenever the selection is changed away from
* an entry window.
*
* Results:
* None.
*
* Side effects:
* The selection is unhighlighted, and marked as not being in
* this window.
*
*----------------------------------------------------------------------
*/
static void
EntrySelChanged(entryPtr)
register Entry *entryPtr; /* Entry that lost the selection. */
{
int oldFirst, oldLast;
oldFirst = entryPtr->selectFirst;
oldLast = entryPtr->selectLast;
entryPtr->selectFirst = entryPtr->selectLast = -1;
if (oldFirst >= 0) {
EntryEraseCaret(entryPtr);
EntryRedisplay(entryPtr, oldFirst, oldLast);
EntryDrawCaret(entryPtr);
}
}
\f
/*
*----------------------------------------------------------------------
*
* EntryDrawCaret --
*
* Display the caret in an entry window. This procedure is
* typically called after anything happened that might have
* caused the caret to be erased. If it was erased, then
* this procedure redraws it.
*
* Results:
* None.
*
* Side effects:
* The caret is redrawn, unless it's already visible.
*
*----------------------------------------------------------------------
*/
void
EntryDrawCaret(entryPtr)
register Entry *entryPtr; /* Where to draw caret. */
{
char savedChar;
if ((entryPtr->flags & (CARET_OFF|FOCUS_WINDOW))
!= (CARET_OFF|FOCUS_WINDOW)) {
return;
}
entryPtr->flags &= ~CARET_OFF;
/*
* Compute where to draw the caret.
*/
savedChar = entryPtr->text[entryPtr->caret];
entryPtr->text[entryPtr->caret] = 0;
entryPtr->caretX = XStringWidth(entryPtr->text, entryPtr->fontPtr, 0, 0)
+ entryPtr->x - caret_x_hot;
entryPtr->caretY = entryPtr->y
+ (entryPtr->fontPtr->height - entryPtr->fontPtr->baseline)
- caret_y_hot;
entryPtr->text[entryPtr->caret] = savedChar;
/*
* White out a mask area, then blacken the caret area.
*/
XPixFill(entryPtr->w, entryPtr->caretX, entryPtr->caretY, caret_width,
caret_height, entryPtr->background, caretMaskBitmap, GXcopy,
AllPlanes);
XPixFill(entryPtr->w, entryPtr->caretX, entryPtr->caretY, caret_width,
caret_height, entryPtr->foreground, caretBitmap, GXcopy,
AllPlanes);
}
\f
/*
*----------------------------------------------------------------------
*
* EntryEraseCaret --
*
* Erase the caret from the given entry.
*
* Results:
* None.
*
* Side effects:
* The caret is no longer displayed in entryPtr's window. If
* it already wasn't displayed, then nothing additional
* happens.
*
*----------------------------------------------------------------------
*/
void
EntryEraseCaret(entryPtr)
register Entry *entryPtr;
{
int first;
if (entryPtr->flags & CARET_OFF) {
return;
}
entryPtr->flags |= CARET_OFF;
/*
* Erase the area of the caret.
*/
XPixSet(entryPtr->w, entryPtr->caretX, entryPtr->caretY,
caret_width, caret_height, entryPtr->background);
/*
* Display the characters on either side of the caret. The
* first and last positions in the text have to be handled
* specially.
*/
if ((entryPtr->caret == 0) && (entryPtr->label != NULL)) {
XText(entryPtr->w, LEFT_MARGIN, entryPtr->y, entryPtr->label,
String_Length(entryPtr->label), entryPtr->fontPtr->id,
entryPtr->foreground, entryPtr->background);
}
first = entryPtr->caret-1;
if (first < 0) {
first = 0;
}
if (entryPtr->text[entryPtr->caret] == 0) {
entryPtr->text[entryPtr->caret] = ' ';
EntryRedisplay(entryPtr, first, entryPtr->caret);
entryPtr->text[entryPtr->caret] = 0;
} else {
EntryRedisplay(entryPtr, first, entryPtr->caret);
}
}
\f
/*
*----------------------------------------------------------------------
*
* EntryInit --
*
* Initialize this module.
*
* Results:
* None.
*
* Side effects:
* Bitmaps and cursors get allocated.
*
*----------------------------------------------------------------------
*/
static void
EntryInit()
{
static Boolean initialized = FALSE;
if (initialized) {
return;
}
initialized = TRUE;
caretBitmap = XStoreBitmap(caret_width, caret_height, caret_bits);
caretMaskBitmap = XStoreBitmap(caretMask_width, caretMask_height,
caretMask_bits);
cursor = XCreateCursor(ptr_width, ptr_height, ptr_bits, ptr_bits,
ptr_x_hot, ptr_y_hot, BlackPixel, WhitePixel, GXcopy);
entryTable = XCreateAssocTable(8);
if ((cursor == 0) || (caretBitmap == 0) || (caretMaskBitmap == 0)
|| (entryTable == NULL)) {
Sx_Panic("EntryMake: couldn't initialize bitmaps and/or hash table.");
}
}
\f
/*
*----------------------------------------------------------------------
*
* EntryDestroyProc --
*
* This procedure is called by the Sx dispatcher just after
* an entry window has been deleted.
*
* Results:
* None.
*
* Side effects:
* The internal data structures associated with the entry
* are deleted.
*
*----------------------------------------------------------------------
*/
void
EntryDestroyProc(entryPtr)
Entry *entryPtr; /* Entry whose window died. */
{
if (entryPtr->selectFirst >= 0) {
entryPtr->selectFirst = entryPtr->selectLast = -1;
Sx_SelectionClear();
}
XDeleteAssoc(entryTable, entryPtr->w);
if (entryPtr->label != NULL) {
Mem_Free((Address) entryPtr->label);
}
Mem_Free((Address) entryPtr);
}