campo-sirio/xi/xitext.c

1420 lines
36 KiB
C
Raw Normal View History

/*******************************************************************************
* Copyright 1991-1995 by ORCA Software, Inc. *
* *
* All rights reserved. May not be reproduced or distributed, in printed or *
* electronic form, without permission of ORCA Software, Inc. May not be *
* distributed as object code, separately or linked with other object modules, *
* without permission. *
*******************************************************************************/
#define XI_INTERNAL
#include "xi.h"
#include "xitext.h"
#include "xiutils.h"
#include "xilm.h"
#include <ctype.h>
#if XIWS == MTFWS
#include <X11/Xatom.h>
#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>
#endif
/* Error codes 30200-30206 */
#define TXT_REDRAW_ATR (TXT_ATR_RJUST | TXT_ATR_ENABLED | TXT_ATR_BORDER | TXT_ATR_VISIBLE)
#define TXT_ACTIVE_ATR (TXT_ATR_ENABLED | TXT_ATR_VISIBLE)
#define BS 8
#if XVT_OS == XVT_OS_CTOS
#define BORDER_WIDTH_X ((npctos_env == CHSERVICE) ? 8 : 1)
#define BORDER_WIDTH_Y ((npctos_env == CHSERVICE) ? 0 : 1)
#define BORDER_SPACE_X ((npctos_env == CHSERVICE) ? 0 : 1)
#define BORDER_SPACE_Y ((npctos_env == CHSERVICE) ? 0 : 1)
#define CHR_LEFTBRACKET "["
#define CHR_RIGHTBRACKET "]"
#else
#if XIWS != WMWS
#define BORDER_WIDTH_X 1
#define BORDER_WIDTH_Y 1
#define BORDER_SPACE_X 1
#define BORDER_SPACE_Y 1
#else
#define BORDER_WIDTH_X 8
#define BORDER_WIDTH_Y 0
#define BORDER_SPACE_X 0
#define BORDER_SPACE_Y 0
#define CHR_LEFTBRACKET "["
#define CHR_RIGHTBRACKET "]"
#endif
#endif
#ifndef NO_PRIMARY_SELECTION
#if XIWS == MTFWS
/* statics used for implementing cut, copy & paste with the PRIMARY selection */
static char *primesel_text = NULL; /* text for primary selection */
static int primesel_len; /* length of primary selection */
static Widget primesel_widget = NULL; /* widget owning primary selection */
/* deliver_primesel - this functions delivers the value of the primary
selection when another application or something else in this application
requests it
*/
static Boolean
deliver_primesel(Widget widget, Atom *selection, Atom *target,
Atom *type, XtPointer *value, unsigned long *length, int *format)
{
static Atom xa_targets = 0;
XrmValue source, dest;
/* printf("deliver_primesel: ENTERED, widget %d, selection %d, target %d\n",
widget, *selection, *target); */
/* get value for Atom "TARGETS" */
if (xa_targets == 0)
{
source.size = strlen("TARGETS") + 1;
source.addr = "TARGETS";
dest.size = sizeof(Atom);
dest.addr = (XtPointer) &xa_targets;
(void) XtConvertAndStore(primesel_widget, XtRString, &source, XtRAtom,
&dest);
}
/* handle request for PRIMARY selection as a string */
if (*target == XA_STRING)
{
*type = XA_STRING;
*value = (XtPointer) XtNewString(primesel_text);
*length = primesel_len;
*format = 8;
/* printf("deliver_primesel: RETURNING primary selection '%s'\n",
primesel_text); */
return(TRUE);
}
/* handle request for TARGETS */
if (*target == xa_targets)
{
/* printf("deliver_primesel: RETURNING targets \n"); */
*type = XA_ATOM;
*value = (XtPointer) XtNew(Atom);
*(Atom *) *value = XA_STRING;
*length = 1;
*format = 32;
return(TRUE);
}
/* anything else return FALSE */
/* printf("deliver_primesel: RETURNING FALSE\n"); */
return(FALSE);
}
void clean_primesel()
{
if (primesel_text)
xvt_mem_free(primesel_text);
}
/*
lose_primesel - this function is called by the Intrinsics when
the primary selection is lost to another application
*/
static void
lose_primesel(Widget widget, Atom *selection)
{
/* printf("lose_primesel: ENTERED, widget %d, selection %d\n", */
/* widget, *selection); */
primesel_widget = NULL;
if (primesel_text)
xvt_mem_free(primesel_text);
primesel_text = NULL;
}
void xi_widgetDestroyCallBack(Widget w, XtPointer closure, XtPointer data)
{
/* need to define a local Atom bcos lose_primesel expects it */
Atom garbageAtom;
lose_primesel(w, &garbageAtom);
}
/*
set_primary_selection - this function sets or clears the primary selection
*/
static void
set_primary_selection(WINDOW win, char *txt, int len)
{
Widget widget;
Time time;
/* printf("set_primary_selection: ENTERED, win %d, len %d\n", win, len); */
/* check if clearing selection */
if (len == 0)
{
#if 0
/* The following clears the selection if an insertion point is set. */
if (primesel_widget != NULL)
{
/* printf("set_primary_selection: clearing selection\n"); */
XtDisownSelection(primesel_widget, XA_PRIMARY,
XtLastTimestampProcessed(XtDisplay(primesel_widget)));
xvt_mem_free(primesel_text);
primesel_text = NULL;
primesel_widget = NULL;
}
#endif
return;
}
/* allocate space and save text string */
primesel_text = xvt_mem_alloc(len + 1);
/* xvt_errmsg_sig_if not NULL "out of memory" */
strncpy(primesel_text, txt, len);
primesel_text[len] = '\0';
primesel_len = len;
/* get widget and selection time */
widget = (Widget) xvt_vobj_get_attr(win, ATTR_X_WIDGET);
time = XtLastTimestampProcessed(XtDisplay(widget));
/* try to take ownership of the primary selection */
/* printf("set_primary_selection: before XtOwnSelection\n"); */
/* printf(" widget %d, text '%s', len %d, time %d\n",
widget, primesel_text, primesel_len, time); */
if (XtOwnSelection(widget, XA_PRIMARY, time, deliver_primesel,
lose_primesel, NULL))
{
/* printf("set_primary_selection: XtOwnSelection SUCCESSFUL\n"); */
XtAddCallback(widget, XtNdestroyCallback, xi_widgetDestroyCallBack,
(XtPointer)NULL);
primesel_widget = widget;
}
else
{
/* printf("set_primary_selection: XtOwnSelection FAILED\n"); */
xvt_mem_free(primesel_text);
primesel_text = NULL;
}
}
#endif
#endif
#if defined(NO_PRIMARY_SELECTION) || XIWS != MTFWS
static void
set_primary_selection(WINDOW win, char *txt, int len)
{
NOREF(win);
NOREF(txt);
NOREF(len);
}
#endif
#if XIWS == PMWS
#define recalc_hit_test(a)
#else
static void
recalc_hit_test(TXT_DATA *txt)
{
int i;
char *s;
short *h;
if (! txt->hit_test)
txt->hit_test = (short *)xi_tree_malloc(txt->text_size * sizeof(short), txt);
xi_set_xvt_font(txt->win, txt->font, FALSE);
for (i = 0, s = txt->text, h = txt->hit_test; i < txt->len;
i++, s++, h++)
*h = (short)xi_get_text_width(txt->win, s, 1, txt->attrib);
}
#endif
static int
stx_get_text_width(TXT_DATA *txt, short *hit_test, char *text, int len)
{
#if XIWS == PMWS
NOREF(hit_test);
return xi_get_text_width(txt->win, text, len, txt->attrib);
#else
int c, r;
short *s;
NOREF(txt);
NOREF(text);
r = 0;
for (c = 0, s = hit_test; c < len; ++c, ++s)
r += *s;
return r;
#endif
}
static DRAW_CTOOLS * near
get_txt_ctools(TXT_DATA *txt, DRAW_CTOOLS *ct, FONT_OBJ *font,
BOOLEAN do_border, BOOLEAN inverted)
{
COLOR fore_color, back_color;
if (inverted)
{
fore_color = txt->back_color;
if (! txt->hilight_color && (txt->well || txt->platform))
fore_color = xi_get_pref(XI_PREF_COLOR_CTRL);
back_color = txt->fore_color;
}
else
{
back_color = txt->back_color;
if (! txt->hilight_color && (txt->well || txt->platform))
back_color = xi_get_pref(XI_PREF_COLOR_CTRL);
fore_color = txt->fore_color;
}
xvt_app_get_default_ctools(ct);
ct->pen.color = ct->fore_color = fore_color;
ct->back_color = ct->brush.color = back_color;
ct->pen.width = do_border ? 1 : 0;
#if XI_IS_CH
CTOS_IS_CH;
ct->pen.pat = PAT_HOLLOW;
CTOS_END;
#endif
#if XI_IS_NOT_CH
CTOS_IS_PM;
ct->pen.pat = do_border ? PAT_SOLID : PAT_HOLLOW;
CTOS_END;
#endif
ct->opaque_text = FALSE; // Minchia!
*font = *txt->font;
ct->mode = M_COPY;
return(ct);
}
static RCT * near
get_txt_clip_rect(TXT_DATA *txt, RCT *rct)
{
#if XI_IS_CH
BOOLEAN do_border;
CTOS_IS_CH;
do_border = (BOOLEAN)(txt->attrib & XI_ATR_BORDER);
CTOS_END;
#endif
*rct = txt->rct;
#if XI_IS_NOT_CH
CTOS_IS_PM;
xi_inflate_rect(rct, -2);
CTOS_END;
#endif
if (txt->inside_only)
{
rct->top -= BORDER_SPACE_Y;
rct->bottom += BORDER_SPACE_Y;
}
#if XI_IS_CH
CTOS_IS_CH;
if (do_border)
{
rct->right -= 8;
rct->left += 8;
}
CTOS_END;
#endif
return(rct);
}
static void near
get_txt_positions(TXT_DATA *txt, int *ip1, int *ip2, int *clipleft,
int *clipright)
{
RCT rct;
xi_set_xvt_font(txt->win, txt->font, FALSE);
get_txt_clip_rect(txt, &rct);
xvt_errmsg_sig_if(!(txt->selstart <= txt->len), NULL_WIN, SEV_FATAL, ERR_ASSERT_4,"30201",
30201, "Internal TEXT error");
*ip1 = txt->string_xpos + stx_get_text_width(txt, txt->hit_test, txt->text,
txt->selstart);
*ip2 = *ip1 + stx_get_text_width(txt, txt->hit_test + txt->selstart,
txt->text + txt->selstart, txt->selstop - txt->selstart);
*clipleft = rct.left;
*clipright = rct.right;
}
void
txt_display_caret(TXT_DATA *txt)
{
int clipleft, clipright, caretpos1, caretpos2;
int height1, height2, pos1, pos2;
if (!txt->hasfocus)
return;
get_txt_positions(txt, &caretpos1, &caretpos2, &clipleft, &clipright);
if (caretpos1 == caretpos2 && caretpos1 <= clipright &&
caretpos2 >= clipleft)
{
#if XIWS == MTFWS
{
/* ugly hack for XVT/XM so that caret will get displayed. */
RCT rct;
DRAW_CTOOLS new_ctools;
FONT_OBJ new_font;
xi_set_draw_ctools(txt->win, get_txt_ctools(txt, &new_ctools,
&new_font, FALSE, FALSE));
xi_set_xvt_font(txt->win, &new_font, FALSE);
get_txt_clip_rect(txt, &rct);
rct.right++;
xi_set_clip(txt->win, &rct);
if (txt->attrib & TXT_ATR_RJUST)
xi_draw_text_attrib(txt->win, txt->string_xpos, txt->pix_baseline, txt->text,
txt->len - txt->scroll_pos, txt->attrib);
else
xi_draw_text_attrib(txt->win, rct.left, txt->pix_baseline,
txt->text + txt->scroll_pos, -1, txt->attrib);
}
#endif
height1 = txt->rct.bottom - txt->rct.top - 2;
if ( height1 < 1 )
height1 = 1;
height2 = txt->ascent + txt->descent;
height1 = min(height1, height2);
pos1 = txt->pix_baseline + txt->descent;
pos2 = txt->rct.bottom - 1;
pos1 = min(pos1, pos2);
xi_caret_on(txt->win, caretpos1, pos1, height1);
}
else
xi_caret_off(txt->win);
}
static void near
recalc_string_xpos(TXT_DATA *txt)
{
RCT rct;
int wid;
xi_set_xvt_font(txt->win, txt->font, FALSE);
get_txt_clip_rect(txt, &rct);
if (txt->scroll_pos > txt->len)
txt->scroll_pos = txt->len;
xvt_errmsg_sig_if(!(txt->selstart <= txt->len), NULL_WIN, SEV_FATAL, ERR_ASSERT_4,"30202",
30202, "Internal TEXT error");
if (txt->attrib & TXT_ATR_RJUST)
{
wid = stx_get_text_width(txt, txt->hit_test,
txt->text, txt->len - txt->scroll_pos);
txt->string_xpos = rct.right - wid;
}
else
{
/* left justified */
wid = stx_get_text_width(txt, txt->hit_test, txt->text, txt->scroll_pos);
txt->string_xpos = rct.left - wid;
}
}
void
txt_hide_caret(TXT_DATA *txt)
{
xi_caret_off(txt->win);
}
void
txt_redraw(TXT_DATA *txt, BOOLEAN inside_only)
{
DRAW_CTOOLS new_ctools;
FONT_OBJ new_font;
RCT rct;
RCT clip_r;
BOOLEAN do_border;
int ip1, ip2, clipleft, clipright;
long tattrib = txt->attrib;
#if XI_IS_CH
RCT r;
#endif
#if XI_IS_CH
NOREF(clip_r);
#endif
txt_hide_caret(txt);
do_border = ((tattrib & (TXT_ATR_BORDER | TXT_ATR_VISIBLE)) ==
(TXT_ATR_BORDER | TXT_ATR_VISIBLE));
get_txt_positions(txt, &ip1, &ip2, &clipleft, &clipright);
rct = txt->rct;
if (inside_only)
xi_inflate_rect(&rct, -1);
xi_set_draw_ctools(txt->win, get_txt_ctools(txt, &new_ctools, &new_font,
(BOOLEAN)(do_border && !inside_only), FALSE));
xi_set_xvt_font(txt->win, &new_font, FALSE);
#if XI_IS_CH
{
char *buf;
int len, i;
CTOS_IS_CH;
r = rct;
if (do_border)
{
r.right -= 8;
r.left += 8;
}
len = (r.right - r.left) / 8 + 1;
/* optimize here? reallocating with every re-draw? CH only? */
/* optimize by not setting DRAW_CTOOLS so often */
buf = xi_tree_malloc(len + 1, NULL);
for (i = 0; i < len; ++i)
buf[i] = ' ';
buf[len] = '\0';
get_txt_clip_rect(txt, &rct);
xi_set_clip(txt->win, &rct);
xi_draw_text(txt->win, r.left, txt->pix_baseline, buf, -1);
xi_tree_free(buf);
CTOS_END;
}
#endif /* CH */
#if XI_IS_NOT_CH
CTOS_IS_PM;
if (inside_only)
{
clip_r = rct;
if (txt->well || txt->platform)
xi_inflate_rect(&clip_r, -1);
xi_draw_rect(txt->win, &clip_r);
}
else
{
xi_draw_rect(txt->win, &rct);
clip_r = rct;
}
#if (XIWS == PMWS) && (XVT_OS != XVT_OS_CTOS)
{
CBRUSH hollow_cbrush;
RCT rct;
DRAW_CTOOLS t;
WINDOW win = txt->win;
/* Bad bug with XVT/PM, needs this before can draw COLOR_LTGRAY */
xvt_app_get_default_ctools(&t);
xvt_dwin_set_draw_ctools(win, &t);
hollow_cbrush.color = COLOR_WHITE;
hollow_cbrush.pat = PAT_HOLLOW;
xvt_dwin_set_cbrush(win, &hollow_cbrush);
rct.top = 0;
rct.left = 0;
rct.bottom = 0;
rct.right = 0;
xvt_dwin_draw_rect(win, &rct);
xi_set_draw_ctools(win, get_txt_ctools(txt, &new_ctools, &new_font,
do_border && !inside_only, FALSE));
xi_set_xvt_font(txt->win, &new_font, FALSE);
}
#endif /* PM AND NOT CTOS */
if (! inside_only && (txt->well || txt->platform))
{
RCT r;
r = rct;
xi_inflate_rect(&r, -1);
xi_draw_3d_rect(txt->win, &r, txt->well, 1, txt->hilight_color, txt->back_color, txt->shadow_color);
}
if (tattrib & TXT_ATR_FOCUSBORDER)
{
if (! inside_only)
{
CPEN cpen;
RCT r;
cpen = black_cpen;
r = rct;
xi_set_cpen(txt->win, &cpen);
xi_set_draw_mode(txt->win, M_COPY);
xi_set_cbrush(txt->win, &hollow_cbrush);
xi_set_clip(txt->win, NULL);
xi_draw_rect(txt->win, &r);
}
}
CTOS_END;
#endif /* NOT CH */
if ((tattrib & TXT_ATR_VISIBLE) == 0)
return;
get_txt_clip_rect(txt, &rct);
xi_set_clip(txt->win, &rct);
if (tattrib & TXT_ATR_RJUST)
xi_draw_text_attrib(txt->win, txt->string_xpos, txt->pix_baseline, txt->text,
txt->len - txt->scroll_pos, txt->attrib);
else
xi_draw_text_attrib(txt->win, rct.left, txt->pix_baseline,
txt->text + txt->scroll_pos, -1, txt->attrib);
if (txt->selstart != txt->selstop)
{
xi_set_draw_ctools(txt->win, get_txt_ctools(txt, &new_ctools, &new_font,
(BOOLEAN)(do_border && !inside_only), TRUE));
xi_set_xvt_font(txt->win, &new_font, FALSE);
#if XIWS == PMWS
{
RCT r;
r.top = txt->pix_baseline - txt->ascent;
r.left = ip1;
r.bottom = txt->pix_baseline + txt->descent;
r.right = rct.left + xi_xvt_get_text_width(txt->win, txt->text + txt->selstart, txt->selstop - txt->selstart);
r.right = ip2;
xi_draw_rect(txt->win, &r);
}
#endif
/* display inverted selection */
xi_draw_text_attrib(txt->win, ip1, txt->pix_baseline, txt->text + txt->selstart,
txt->selstop - txt->selstart, txt->attrib);
}
txt_display_caret(txt);
xi_set_clip(txt->win, NULL);
}
/*
This function performs a binary search to locate the nearest
character to the mouse, including characters that are not
visible because they are outside the bounding box of the control.
*/
static int near
hit_test(TXT_DATA *txt, int x, int y)
{
int lstop, start, stop, wid;
int curtextpos;
NOREF(y);
xi_set_xvt_font(txt->win, txt->font, FALSE);
if (x <= txt->string_xpos)
return 0;
if (x >= txt->string_xpos + stx_get_text_width(txt, txt->hit_test,
txt->text, txt->len))
return(txt->len);
lstop = txt->len;
start = 0;
stop = txt->len / 2;
curtextpos = txt->string_xpos;
while (stop > start)
{
wid = stx_get_text_width(txt, txt->hit_test + start,
txt->text + start, stop - start);
if (x <= curtextpos + wid /* && x >= curtextpos */) {
if (stop - start == 1)
break;
/* subdivide this range */
lstop = stop;
stop = start + (stop - start) / 2;
}
else
{
/* use other range */
start = stop;
stop = lstop;
curtextpos += wid;
}
}
/*
Now that the bisection has hit a character, determine which
side of the character is closer.
*/
if (x - curtextpos > curtextpos + wid - x)
return(stop);
else
return(start);
}
TXT_DATA *
txt_reset(TXT_DATA *txt)
{
int leading, ascent, descent;
xi_set_xvt_font(txt->win, txt->font, FALSE);
xi_get_font_metrics(txt->win, &leading, &ascent, &descent);
txt->leading = (short)leading;
txt->ascent = (short)ascent;
txt->descent = (short)descent;
txt->select_start = txt->selstart = txt->selstop =
txt->scroll_pos = txt->flags = 0;
set_primary_selection(txt->win, "", 0);
txt->len = strlen(txt->text);
txt->hasfocus = FALSE;
txt->state = TXT_STATE_NULL;
#if XIWS == WMWS
txt->pix_baseline = txt->rct.top + BORDER_SPACE_Y + leading +
ascent + descent;
#else
txt->pix_baseline = txt->rct.top + leading + ascent;
#endif
if (! txt->inside_only)
{
txt->pix_baseline += BORDER_WIDTH_Y;
}
if (txt->attrib & XI_ATR_VCENTER)
{
txt->pix_baseline +=
(txt->rct.bottom - txt->rct.top - leading - ascent -
descent) / 2;
}
recalc_hit_test(txt);
recalc_string_xpos(txt);
if (! xi_half_baked(txt->win))
txt_redraw(txt, FALSE);
return txt;
}
static void near
invert_selection(TXT_DATA *txt, BOOLEAN invert)
{
int ip1, ip2, clipleft, clipright;
DRAW_CTOOLS new_ctools;
FONT_OBJ new_font;
RCT rct;
txt_hide_caret(txt);
get_txt_positions(txt, &ip1, &ip2, &clipleft, &clipright);
xi_set_draw_ctools(txt->win, get_txt_ctools(txt, &new_ctools, &new_font,
FALSE, invert));
xi_set_xvt_font(txt->win, &new_font, FALSE);
get_txt_clip_rect(txt, &rct);
xi_set_clip(txt->win, &rct);
/* display inverted selection */
/* display inverted selection */
xi_draw_text_attrib(txt->win, ip1, txt->pix_baseline, txt->text + txt->selstart,
txt->selstop - txt->selstart, txt->attrib);
xi_set_clip(txt->win, NULL);
}
static void near
deselect(TXT_DATA *txt)
{
if (txt->selstart != txt->selstop)
invert_selection(txt, FALSE);
txt->selstart = txt->selstop;
txt->flags |= TXT_FLAG_SELECTION;
set_primary_selection(txt->win, "", 0);
}
static void near
make_ip_viewable(TXT_DATA *txt, BOOLEAN redraw)
{
int ip1, ip2, clipleft, clipright, pos;
BOOLEAN rjust = (txt->attrib & TXT_ATR_RJUST) != 0;
int oldxpos = txt->string_xpos;
RCT rct;
if ((txt->attrib & TXT_ATR_AUTOSCROLL) == 0)
return;
xi_set_xvt_font(txt->win, txt->font, FALSE);
get_txt_positions(txt, &ip1, &ip2, &clipleft, &clipright);
pos = (txt->selstart == txt->select_start) ? ip2 : ip1;
while (pos < clipleft || pos >= clipright)
{
if (pos < clipleft && rjust || pos >= clipright && !rjust)
{
if (txt->scroll_pos >= txt->len)
break;
/* increase scroll_pos */
txt->scroll_pos++;
}
else
if (pos < clipleft && !rjust || pos >= clipright && rjust)
{
if (txt->scroll_pos <= 0)
break;
/* decrease scroll_pos */
txt->scroll_pos--;
}
recalc_string_xpos(txt);
get_txt_positions(txt, &ip1, &ip2, &clipleft, &clipright);
pos = (txt->selstart == txt->select_start) ? ip2 : ip1;
}
if (oldxpos != txt->string_xpos)
{
txt->flags |= TXT_FLAG_SCROLL;
if (redraw)
{
xvt_dwin_update(txt->win);
get_txt_clip_rect(txt, &rct);
if (txt->parent_obj)
{
xi_set_update_obj(txt->parent_obj);
if (txt->parent_obj->type == XIT_LIST)
lm_text_scrolling(txt->parent_obj);
}
xi_scroll_rect(txt->win, &rct, txt->string_xpos - oldxpos, 0);
/* txt_redraw(txt, TRUE); */
}
}
}
static void near
delete_selection(TXT_DATA *txt)
{
int offset = txt->selstop - txt->selstart;
int i;
if (offset == 0)
return;
/*
Make redraw more efficient later
*/
for (i = txt->selstop; txt->text[i] != 0; i++)
txt->text[i - offset] = txt->text[i];
txt->text[i - offset] = 0;
txt->len = strlen(txt->text);
recalc_hit_test(txt);
txt->select_start = txt->selstop = txt->selstart;
set_primary_selection(txt->win, "", 0);
recalc_string_xpos(txt);
make_ip_viewable(txt, FALSE);
txt_redraw(txt, TRUE);
txt->flags |= (TXT_FLAG_TEXT | TXT_FLAG_SELECTION);
}
static void near
delete_char_idx(TXT_DATA *txt, int idx)
{
int offset = txt->selstop - txt->selstart;
int i;
if (idx < 0 || idx >= txt->len || offset != 0)
return;
/*
Make redraw more efficient later
*/
for (i = idx; txt->text[i + 1] != 0; i++)
txt->text[i] = txt->text[i + 1];
txt->text[i] = 0;
txt->len--;
recalc_hit_test(txt);
if (txt->selstart > idx)
txt->select_start = txt->selstop = txt->selstart = txt->selstart - 1;
recalc_string_xpos(txt);
make_ip_viewable(txt, FALSE);
txt_redraw(txt, TRUE);
txt->flags |= TXT_FLAG_TEXT;
set_primary_selection(txt->win, &txt->text[txt->selstart],
txt->selstop - txt->selstart);
}
static void near
delete_char_next(TXT_DATA *txt)
{
delete_char_idx(txt, txt->selstart);
}
static void near
delete_char_prev(TXT_DATA *txt)
{
delete_char_idx(txt, txt->selstart - 1);
}
static BOOLEAN near
char_is_displayable(int c)
{
return((c >= ' ' && c <= 255) && (c != K_DEL));
}
static BOOLEAN near
wordbrk(int c)
{
return(strchr(" .,`~!@#$%^&*()-=+|[]{}\'\";:<>/?", c) != NULL);
}
static void near
txt_set_sel_internal(TXT_DATA *txt, int ip1, int ip2,
BOOLEAN autoscroll, BOOLEAN set_primary_sel)
{
int oldp1, oldp2, dummy;
int selstart, selstop, old_selstart, old_selstop;
int p1, p2;
RCT rct;
ip2 = clip(ip2, 0, txt->len);
ip1 = clip(ip1, 0, txt->len);
if (txt->selstart != ip1 && txt->selstop != ip2 && txt->selstart != ip2)
{
/* both changed at once -- reset the selection start point */
txt->select_start = ip1;
}
get_txt_clip_rect(txt, &rct);
xi_set_clip(txt->win, &rct);
old_selstart = txt->selstart;
old_selstop = txt->selstop;
get_txt_positions(txt, &oldp1, &oldp2, &dummy, &dummy);
selstart = txt->selstart = ip1;
selstop = txt->selstop = ip2;
get_txt_positions(txt, &p1, &p2, &dummy, &dummy);
if (txt->hasfocus)
{
DRAW_CTOOLS new_ctools;
FONT_OBJ new_font;
/* only show selection if it has focus */
if (p1 >= oldp2 || p2 <= oldp1)
{
RCT r;
txt_hide_caret(txt);
#if XVTWS == WMWS
NOREF(r);
#endif
r.top = txt->pix_baseline - txt->ascent - txt->leading;
r.bottom = txt->pix_baseline + txt->descent;
xi_set_draw_ctools(txt->win, get_txt_ctools(txt, &new_ctools,
&new_font, FALSE, FALSE));
xi_set_xvt_font(txt->win, &new_font, FALSE);
/* clear old inverted selection */
if (txt->attrib & TXT_ATR_RJUST)
{
#if XIWS == WINWS
r.left = oldp1;
r.right = r.left + stx_get_text_width(txt,
txt->hit_test + old_selstart, txt->text + old_selstart,
old_selstop - old_selstart);
xi_rect_intersect(&r, &r, &rct);
xi_set_clip(txt->win, &r);
#endif
xi_draw_text_attrib(txt->win, oldp1, txt->pix_baseline,
txt->text + old_selstart,
old_selstop - old_selstart, txt->attrib);
}
else
{
#if XIWS == WINWS
r.left = oldp1;
r.right = r.left + stx_get_text_width(txt,
txt->hit_test + old_selstart, txt->text + old_selstart,
old_selstop - old_selstart);
xi_rect_intersect(&r, &r, &rct);
xi_set_clip(txt->win, &r);
#endif
xi_draw_text_attrib(txt->win, oldp1, txt->pix_baseline, txt->text + old_selstart,
old_selstop - old_selstart, txt->attrib);
}
xi_set_draw_ctools(txt->win, get_txt_ctools(txt, &new_ctools,
&new_font, FALSE, TRUE));
xi_set_xvt_font(txt->win, &new_font, FALSE);
/* display inverted selection */
#if XIWS == WINWS
r.left = p1;
r.right = r.left + stx_get_text_width(txt,
txt->hit_test + txt->selstart, txt->text + txt->selstart,
txt->selstop - txt->selstart);
xi_rect_intersect(&r, &r, &rct);
xi_set_clip(txt->win, &r);
#endif
xi_draw_text_attrib(txt->win, p1, txt->pix_baseline,
txt->text + txt->selstart,
txt->selstop - txt->selstart, txt->attrib);
}
else
{
if (p1 != oldp1)
{
BOOLEAN invert = p1 < oldp1;
#if XIWS == WINWS
RCT r;
#endif
order_ints(&p1, &oldp1);
order_ints(&selstart, &old_selstart);
txt_hide_caret(txt);
#if XIWS == WINWS
r.top = txt->pix_baseline - txt->ascent - txt->leading;
r.bottom = txt->pix_baseline + txt->descent;
r.left = p1;
r.right = r.left + stx_get_text_width(txt,
txt->hit_test + selstart, txt->text + selstart,
old_selstart - selstart);
xi_rect_intersect(&r, &r, &rct);
xi_set_clip(txt->win, &r);
#endif
xi_set_draw_ctools(txt->win, get_txt_ctools(txt, &new_ctools,
&new_font, FALSE, invert));
xi_set_xvt_font(txt->win, &new_font, FALSE);
/* display inverted selection */
xi_draw_text_attrib(txt->win, p1, txt->pix_baseline,
txt->text + selstart, old_selstart - selstart,
txt->attrib);
}
if (p2 != oldp2)
{
BOOLEAN invert = p2 > oldp2;
order_ints(&p2, &oldp2);
order_ints(&selstop, &old_selstop);
txt_hide_caret(txt);
xi_set_draw_ctools(txt->win, get_txt_ctools(txt, &new_ctools,
&new_font, FALSE, invert));
xi_set_xvt_font(txt->win, &new_font, FALSE);
/* display inverted selection */
if (txt->attrib & TXT_ATR_RJUST)
{
#if XIWS == WINWS
RCT r;
r.top = txt->pix_baseline - txt->ascent - txt->leading;
r.bottom = txt->pix_baseline + txt->descent;
r.left = p2;
r.right = r.left + stx_get_text_width(txt,
txt->hit_test + selstop, txt->text + selstop,
old_selstop - selstop);
xi_rect_intersect(&r, &r, &rct);
xi_set_clip(txt->win, &r);
#endif
xi_draw_text_attrib(txt->win, p2, txt->pix_baseline, txt->text + selstop,
old_selstop - selstop, txt->attrib);
}
else
{
#if XIWS == WINWS
RCT r;
r.top = txt->pix_baseline - txt->ascent - txt->leading;
r.bottom = txt->pix_baseline + txt->descent;
r.left = p2;
r.right = r.left + stx_get_text_width(txt,
txt->hit_test + selstop, txt->text + selstop,
old_selstop - selstop);
xi_rect_intersect(&r, &r, &rct);
xi_set_clip(txt->win, &r);
#endif
xi_draw_text_attrib(txt->win, p2, txt->pix_baseline,
txt->text + selstop, old_selstop - selstop,
txt->attrib);
}
}
}
if (autoscroll)
make_ip_viewable(txt, TRUE);
txt_display_caret(txt);
}
xi_set_clip(txt->win, NULL);
if (set_primary_sel)
set_primary_selection(txt->win, &txt->text[txt->selstart],
txt->selstop - txt->selstart);
}
static void near
insert_char(TXT_DATA *txt, int c)
{
int i;
xvt_errmsg_sig_if(!(txt->selstart == txt->selstop), NULL_WIN, SEV_FATAL, ERR_ASSERT_4,"30203",
30203, "Internal error");
if (txt->len >= txt->text_size - 1)
{
/* beep(); */
txt->flags |= TXT_FLAG_OVF;
return;
}
for (i = txt->len; i >= txt->selstart; i--)
txt->text[i+1] = txt->text[i];
txt->text[txt->selstart] = (char)c;
txt->len++;
recalc_hit_test(txt);
txt->selstart++;
txt->selstop++;
recalc_string_xpos(txt);
make_ip_viewable(txt, FALSE);
txt_redraw(txt, TRUE);
txt->flags |= TXT_FLAG_TEXT;
set_primary_selection(txt->win, &txt->text[txt->selstart],
txt->selstop - txt->selstart);
}
/*
Process incoming events. Return TRUE if event is consumed.
*/
BOOLEAN
txt_event(TXT_DATA *txt, EVENT *ep, BOOLEAN gaining_focus)
{
int hit, hit1, hit2;
int c, pos, oldpos;
static PNT last_pos = {
-1, -1 };
EVENT_TYPE et;
BOOLEAN retval = TRUE;
/* This exists to avoid a compiler bug on the Mac/MPW */
BOOLEAN check_txt = ( txt != NULL );
et = ep->type;
xvt_errmsg_sig_if(!(check_txt), NULL_WIN, SEV_FATAL, ERR_ASSERT_4,"30206",
30206, "Invalid TXT passed to txt_event");
if ((txt->attrib & TXT_ACTIVE_ATR) != TXT_ACTIVE_ATR &&
et != E_UPDATE)
return(FALSE);
switch (et)
{
case E_UPDATE:
txt_redraw(txt, FALSE);
break;
case E_CHAR:
if (txt->attrib & TXT_ATR_READONLY)
{
retval = FALSE;
break;
}
c = ep->v.chr.ch;
if (!char_is_displayable(c))
{
switch (c)
{
case K_LEFT:
case K_RIGHT:
case K_LHOME:
case K_LEND:
case K_WLEFT:
case K_WRIGHT:
#if (XIWS == WMWS)
ep->v.chr.shift = FALSE;
#endif
if (txt->selstart == txt->selstop)
txt->select_start = txt->selstart;
if (abs(txt->selstart - txt->select_start) >
abs(txt->selstop - txt->select_start))
pos = txt->selstart;
else
pos = txt->selstop;
oldpos = pos;
switch(c)
{
case K_LEFT:
pos = clip(pos - 1, 0, txt->len);
break;
case K_RIGHT:
pos = clip(pos + 1, 0, txt->len);
break;
case K_LHOME:
pos = 0;
break;
case K_LEND:
pos = txt->len;
break;
case K_WLEFT:
do
{
if (pos > 0)
pos--;
} while (pos > 0 && !wordbrk(txt->text[pos]));
break;
case K_WRIGHT:
do
{
if (pos < txt->len)
pos++;
} while (pos < txt->len &&
!wordbrk(txt->text[pos]));
break;
}
if (ep->v.chr.shift)
{
/* change current selection */
if (pos > txt->select_start)
txt_set_sel_internal(txt, txt->select_start,
pos, TRUE, TRUE);
else
txt_set_sel_internal(txt, pos,
txt->select_start, TRUE, TRUE);
}
else
{
/* move caret */
deselect(txt);
txt->select_start = txt->selstop = txt->selstart
= pos;
make_ip_viewable(txt, TRUE);
txt_display_caret(txt);
}
if (pos != oldpos)
txt->flags |= TXT_FLAG_SELECTION;
break;
case K_CLEAR:
case K_DEL:
if (txt->selstart != txt->selstop)
delete_selection(txt);
else
delete_char_next(txt);
break;
case BS:
if (txt->selstart != txt->selstop)
delete_selection(txt);
else
delete_char_prev(txt);
break;
default:
retval = FALSE; /* event not consumed */
break;
}
break;
}
/* if we get to here then char is displayable */
if (txt->selstart != txt->selstop)
delete_selection(txt);
insert_char(txt, c);
if ( txt->auto_tab && txt->len >= txt->text_size - 1 )
{
EVENT ev;
MEMCLEAR(ev);
ev.type = E_CHAR;
ev.v.chr.ch = '\t';
xi_event( txt->win, &ev );
}
break;
case E_MOUSE_DOWN:
case E_MOUSE_DBL:
if (ep->v.mouse.button != 0)
{
retval = FALSE;
break;
}
if (txt->attrib & TXT_ATR_READONLY)
{
retval = FALSE;
break;
}
last_pos.h = last_pos.v = -1;
hit = hit_test(txt, ep->v.mouse.where.h, ep->v.mouse.where.v);
if (ep->v.mouse.shift)
{
/* alter old selection */
if (abs(txt->selstart - hit) > abs(txt->selstop - hit))
txt->select_start = txt->selstart;
else
txt->select_start = txt->selstop;
}
else
{
txt->select_start = hit;
}
/*
If XI_PREF_AUTOSEL_ON_MOUSE is set to true, don't fall through,
so that a mouse down on an AUTOSELECT field selects the entire
field. Also, untrap the mouse to the object, so that mouse
moves and mouse up events do not go to the object.
*/
if (gaining_focus && (txt->attrib & TXT_ATR_AUTOSELECT) && xi_get_pref(XI_PREF_AUTOSEL_ON_MOUSE))
{
XI_OBJ *itf;
XI_ITF_DATA *itf_data;
itf = xi_get_itf(txt->win);
itf_data = itf->v.itf;
itf_data->trap_obj = NULL;
/*
Inform the XI module that another module is taking
responsibility for trapping the mouse to an object,
explicitly.
*/
itf_data->trap_explicit = TRUE;
break;
}
/*
Set the state to select characters or select words.
*/
txt->state = (et == E_MOUSE_DOWN) ? TXT_STATE_SELC :
TXT_STATE_SELW;
if (et == E_MOUSE_DOWN && txt->timer_set)
{
txt_set_sel_internal(txt, 0, 32000, FALSE, TRUE);
txt->state = TXT_STATE_NULL;
xvt_timer_destroy(txt->timer_id);
txt->timer_id = 0;
txt->timer_set = FALSE;
break;
}
if (xi_get_pref(XI_PREF_TRIPLE_CLICK_TIME))
{
if (et == E_MOUSE_DBL)
{
txt->timer_id = xvt_timer_create(txt->win,
xi_get_pref(XI_PREF_TRIPLE_CLICK_TIME));
txt->timer_set = TRUE;
}
}
et = E_MOUSE_MOVE;
/*************
fall through to E_MOUSE_MOVE
*************/
case E_MOUSE_MOVE:
#if 0
if (last_pos.h == ep->v.mouse.where.h &&
last_pos.v == ep->v.mouse.where.v) {
RCT rct;
/*
The mouse did not move from last position.
This event should be discarded unless we have dragged
off the edge of the edit field.
*/
get_txt_clip_rect(txt, &rct);
if (xvt_rect_has_point(&rct, last_pos))
break;
}
#endif
last_pos = ep->v.mouse.where;
if (txt->state != TXT_STATE_SELW && txt->state != TXT_STATE_SELC)
break;
hit = hit_test(txt, ep->v.mouse.where.h, ep->v.mouse.where.v);
hit1 = hit;
hit2 = txt->select_start;
order_ints(&hit1, &hit2);
if (txt->state == TXT_STATE_SELW)
{
/*
select from select_start to here, rounding both ends
to an even word boundary
*/
while (hit1 > 0 && !wordbrk(txt->text[hit1-1]))
hit1--;
while (hit2 < txt->len && !wordbrk(txt->text[hit2]))
hit2++;
}
txt_set_sel_internal(txt, hit1, hit2, TRUE, FALSE);
break;
case E_TIMER:
if (txt->timer_id == ep->v.timer.id)
{
xvt_timer_destroy(txt->timer_id);
txt->timer_id = 0;
txt->timer_set = FALSE;
}
break;
case E_MOUSE_UP:
txt->state = TXT_STATE_NULL;
set_primary_selection(txt->win, &txt->text[txt->selstart],
txt->selstop - txt->selstart);
break;
case E_COMMAND:
/* todo process clipboard events */
retval = FALSE;
break;
}
return(retval);
}
unsigned long
txt_get_attrib(TXT_DATA *txt)
{
return(txt->attrib);
}
void
txt_get_sel(TXT_DATA *txt, int *c1, int *c2)
{
*c1 = txt->selstart;
*c2 = txt->selstop;
}
RCT *
txt_get_rect(TXT_DATA *txt, RCT *rct)
{
*rct = txt->rct;
return(rct);
}
RCT *
txt_set_rect(TXT_DATA *txt, RCT *rct)
{
txt->rct = *rct;
return(rct);
}
void
txt_get_text(TXT_DATA *txt, char *s, int len)
{
tgstrncpy(s, txt->text, len);
}
void
txt_set_attrib(TXT_DATA *txt, unsigned long attrib)
{
BOOLEAN do_redraw;
do_redraw = (((txt->attrib ^ attrib) & TXT_REDRAW_ATR) != 0);
txt->attrib = attrib;
if (do_redraw)
txt_redraw(txt, FALSE);
}
/* turns on/off caret if selstart == selstop */
void
txt_caret(TXT_DATA *txt, BOOLEAN caret_state)
{
xvt_errmsg_sig_if(!((txt->attrib & TXT_ACTIVE_ATR) == TXT_ACTIVE_ATR || !caret_state), NULL_WIN, SEV_FATAL, ERR_ASSERT_4,"30205",
30205, "txt_caret called with disabled field");
if (txt->hasfocus && !caret_state || !txt->hasfocus && caret_state)
{
if (caret_state == TRUE && txt->selstart != txt->selstop)
{
invert_selection(txt, TRUE);
}
#if XIWS != WMWS
if (caret_state && (txt->attrib & TXT_ATR_FOCUSBORDER))
{
CPEN cpen;
RCT r;
cpen = black_cpen;
r = txt->rct;
xi_set_cpen(txt->win, &cpen);
xi_set_draw_mode(txt->win, M_COPY);
xi_set_cbrush(txt->win, &hollow_cbrush);
xi_draw_rect(txt->win, &r);
}
#endif
}
txt->hasfocus = caret_state;
txt_display_caret(txt);
}
void
txt_set_sel(TXT_DATA *txt, int ip1, int ip2)
{
txt_set_sel_internal(txt, ip1, ip2, FALSE, FALSE);
}
void
txt_set_text(TXT_DATA *txt, char *s)
{
txt->select_start = txt->selstart = txt->selstop = txt->scroll_pos = 0;
txt->state = TXT_STATE_NULL;
if (s)
{
tgstrncpy(txt->text, s, txt->text_size);
txt->len = strlen(txt->text);
recalc_hit_test(txt);
recalc_string_xpos(txt);
txt_redraw(txt, TRUE);
set_primary_selection(txt->win, &txt->text[txt->selstart],
txt->selstop - txt->selstart);
}
}