campo-sirio/xi/xitext.c
alex dabe04eb31 eliminato calcolo errato della baseline in Windows
git-svn-id: svn://10.65.10.50/trunk@5771 c028cbd2-c16b-5b4b-a496-9718f37d4682
1997-12-18 17:39:50 +00:00

3373 lines
89 KiB
C
Executable File

/*******************************************************************************
* Copyright 1991-1996 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 "xilm.h"
#include "xilmst.h"
#include "xiutils.h"
#include "xistx.h"
#define is_word_char(ch) (ch != ' ' && ch != '\n')
#define SB_DELTA 3
#define GRAN 32
#define NEW_LEN( a ) (((((a) - 1 ) / GRAN ) + 1 ) * GRAN)
/*
each XI_TEXT can be in an editing state, or a display only state. at times,
other modules need to request the state of the wrap. an instance is when the
lm or stx modules needs to set state of insertion points.
more than one XI_TEXT can be in an editing state. an example of this is where
more than one window contain controls using XI_TEXT, each window has a control
that has focus within that window.
when other modules need to interact with an XI_TEXT, such as setting the
selection, they need to inquire of the XI_TEXT if it is currently in an editing
state. there is no provision, and results are undefined, if another module
calls functions, or uses macros that are only valid for an XI_TEXT that is in
the editing state.
save_text contains a pointer to a copy of an XI_TEXT. this is used to
save the state of an XI_TEXT before modifying when editing. this allows
us to optimize drawing after any editing action.
note for the programmer maintaining this module: save_text must never be
assumed to contain any allocated data in between calls to this module. in
other words, any function that calls save_text_state must call
free_save_text_state before returning.
calls to this module for a particular editing XI_TEXT may be interspersed with
other calls for another editing XI_TEXT. don't assume any state in this module
beyond the scope of one of the calls into the module.
*/
static XI_TEXT *save_text = NULL;
static void calc_delta_y( XI_TEXT * text );
static void save_text_state( XI_TEXT * text );
static void display_if_necessary( XI_TEXT * text, BOOLEAN recalc_delta_y );
static void xi_text_caret_off( XI_TEXT * text );
static void xi_text_caret_on( XI_TEXT * text );
static void calc_delta_x( XI_TEXT * text );
static void xi_text_hit_test( XI_TEXT * text, XinEvent * ep, int *ip );
/*
sets range, position, and proportion of the scroll bar. only called if
there is a scroll bar.
*/
static void
xi_text_sb_set( XI_TEXT * text )
{
if ( xi_itf_in_event_destroy( text->itf ) )
return;
if ( text->nbr_lines == 0 )
{
XinScrollBarSet( text->sb_win, XinScrollBarTypeEither, 0, 1000, 0, 0 );
}
else
{
double prop,
pos;
prop = ( double ) text->max_lines_to_draw / ( double ) text->nbr_lines * 1000;
if ( prop > 1000 )
prop = 1000;
pos = ( double ) text->delta_y / ( double ) text->nbr_lines * 1000;
XinScrollBarSet( text->sb_win, XinScrollBarTypeEither, 0, 1000, ( int ) prop, ( int ) pos );
}
}
/*
the process of wrapping text consists of breaking the string into segments.
this is the function that adds a segment to the line_breaks data structure.
(the line_breaks data structure contains the segments of wrapped text.
only called by xi_text_wrap_internal, and only if multi-line.
*/
static void
add_seg( XI_TEXT * text, int seg_starting_pos )
{
XI_TEXT_LINE_BREAK *lb;
++text->nbr_lines;
if ( text->line_breaks )
text->line_breaks = ( XI_TEXT_LINE_BREAK * ) xi_tree_realloc( text->line_breaks,
sizeof( XI_TEXT_LINE_BREAK ) * text->nbr_lines );
else
text->line_breaks = ( XI_TEXT_LINE_BREAK * ) xi_tree_malloc( sizeof( XI_TEXT_LINE_BREAK ) * text->nbr_lines,
text );
lb = &text->line_breaks[text->nbr_lines - 1];
lb->line_break = seg_starting_pos;
lb->ip1 = lb->ip2 = -1;
lb->active_ip = -2;
}
/*
creates an xi_text. initially, the xi_text is not editing text. to start
editing text, xi_text_editing_start must be called.
win is the window into which the xi_text will be placed.
pix_width is the physical pixel width.
font is the desired font for the xi_text.
parent is a parent pointer for xi_tree memory. the XI_TEXT is allocated with
this as its parent.
if multi_line is true, the xi_text is a multi_line edit control. if false,
the xi_text is a single line edit control.
*/
XI_TEXT *
xi_text_construct( XinWindow win, int pix_width, XinFont * font,
void *parent, BOOLEAN multi_line, int cid,
BOOLEAN scrollbar_always_visible )
{
XI_TEXT *text;
text = xi_tree_malloc( sizeof( XI_TEXT ), parent );
text->win = win;
text->itf = xi_get_itf( win );
text->font = font;
text->pix_width = pix_width;
text->multi_line = multi_line;
text->cid = cid;
text->scrollbar_always_visible = scrollbar_always_visible;
text->sb_win = XI_NULL_WINDOW;
text->delta_y = 0;
text->delta_x = 0;
text->editing = FALSE;
text->visible = TRUE;
return text;
}
/*
this function does the actual wrapping of text. it is only used for a
multi_line edit control.
*/
static void
xi_text_wrap_internal( XI_TEXT * text )
{
int char_width;
int cur_seg,
cur_seg_starting_pos,
avg_line_len,
seg_len;
XinFont *font;
int internal_pix_width = text->internal_pix_width;
char *string = text->string;
if ( ! string )
return;
font = text->font;
if ( text->line_breaks )
{
xi_tree_free( text->line_breaks );
text->line_breaks = NULL;
}
text->nbr_lines = 0;
xi_get_font_metrics_font( font, &text->leading, &text->ascent, &text->descent, &char_width );
text->font_height = text->ascent + text->descent + text->leading;
avg_line_len = internal_pix_width / char_width;
if ( avg_line_len == 0 )
avg_line_len = 1;
cur_seg_starting_pos = 0;
cur_seg = 0;
/* find all segments in text */
while ( string[ cur_seg_starting_pos ] != '\0' )
{
int this_seg_len,
possible_word_break,
cur_seg_len,
word_break,
cnt;
BOOLEAN continue_loop;
cur_seg_len = strlen( &string[cur_seg_starting_pos] );
/* if there is a new line in the string, will the line fit in the text
* space */
while ( TRUE )
{
continue_loop = FALSE;
for ( cnt = 0; cnt < cur_seg_len; ++cnt )
{
if ( string[cur_seg_starting_pos + cnt] == '\n' )
{
seg_len = XinFontTextWidthGet( font,
&string[cur_seg_starting_pos],
cnt );
if ( seg_len <= internal_pix_width )
{
add_seg( text, cur_seg_starting_pos );
cur_seg_starting_pos += cnt + 1;
cur_seg_len = strlen( &string[cur_seg_starting_pos] );
continue_loop = TRUE;
break;
}
else
{
continue_loop = FALSE;
break;
}
}
}
if ( !continue_loop )
break;
}
this_seg_len = min( avg_line_len, cur_seg_len );
if ( this_seg_len == 0 )
this_seg_len = 1;
/* find character that is beyond internal_pix_width */
while ( TRUE )
{
seg_len = XinFontTextWidthGet( font,
&string[cur_seg_starting_pos],
this_seg_len );
if ( seg_len > internal_pix_width )
break;
if ( seg_len <= internal_pix_width &&
string[cur_seg_starting_pos + this_seg_len] == '\0' )
{
/* this is the last segment of the string */
add_seg( text, cur_seg_starting_pos );
return;
}
++this_seg_len;
}
/* add the first segment */
add_seg( text, cur_seg_starting_pos );
/* find character that is just below internal_pix_width */
while ( this_seg_len > 1 )
{
--this_seg_len;
seg_len = XinFontTextWidthGet( font,
&string[cur_seg_starting_pos],
this_seg_len );
if ( seg_len < internal_pix_width )
break;
}
/* find possible word break */
possible_word_break = this_seg_len;
while ( possible_word_break > 0 )
{
if ( !is_word_char( string[cur_seg_starting_pos + possible_word_break] ) )
break;
--possible_word_break;
}
/* if there is no possible word break, then break at this_seg_len */
if ( possible_word_break == 0 )
word_break = this_seg_len;
else
word_break = possible_word_break;
/* move forward so that word break is on a word */
while ( !is_word_char( string[cur_seg_starting_pos + word_break] ) )
++word_break;
++cur_seg;
cur_seg_starting_pos += word_break;
}
/* If the text is empty, there will be no segments and nbr_lines will still
be zero. This is not a good state for the text object. RGM */
if ( text->nbr_lines == 0 )
add_seg( text, 0 );
}
/* Returns len, or len truncated to the available space in the buffer */
static int
reallocate_text( XI_TEXT * text, int len, BOOLEAN setting_buffer_size )
{
int llen, new_len;
llen = len;
if ( ! setting_buffer_size )
{
llen = text->buffer_size;
if ( (!text->var_len_text) && len > text->buffer_size )
len = text->buffer_size;
}
if ( llen < text->min_buffer_size )
llen = text->min_buffer_size;
if ( text->var_len_text )
new_len = NEW_LEN( llen );
else
new_len = llen;
if ( new_len != text->allocated_length )
{
if ( text->string )
text->string = ( char * ) xi_tree_realloc( text->string, new_len );
else
text->string = ( char * ) xi_tree_malloc( new_len, text );
}
text->allocated_length = new_len;
text->buffer_size = llen;
return len;
}
/*
this function sets the text of an xi_text.
*/
void
xi_text_set( XI_TEXT * text, char *string )
{
int len;
XinFont *font;
BOOLEAN restart = FALSE;
if ( text->string )
if ( strcmp( text->string, string ) == 0 &&
text->initialized )
return;
text->initialized = TRUE;
if ( xi_text_editing_is( text ) )
{
xi_text_editing_stop( text );
restart = TRUE;
}
font = text->font;
XinWindowFontMap( text->win, font );
len = reallocate_text( text, strlen( string ) + 1, FALSE );
if ( text->scrollbar )
{
if ( !text->sb_width )
text->sb_width = ( int ) XinMetricGet( XinMetricVerticalScrollBarWidth );
text->internal_pix_width = text->pix_width - text->sb_width - SB_DELTA;
}
else
text->internal_pix_width = text->pix_width;
memcpy( text->string, string, len );
text->string[len - 1] = '\0';
if ( text->multi_line )
xi_text_wrap_internal( text );
if ( text->sb_win )
{
if ( text->multi_line )
calc_delta_y( text );
else
calc_delta_x( text );
xi_text_sb_set( text );
}
if ( restart )
{
xi_text_editing_start( text );
if ( xi_get_attrib( text->parent_obj ) & XI_ATR_AUTOSELECT )
xi_text_selection_set_internal( text, 0, SHRT_MAX,
0, TRUE, TRUE );
}
}
/*
This function should be called after a cell request that reallocated the buffer.
*/
void xi_text_reinitialize( XI_TEXT* text )
{
int len;
BOOLEAN restart = FALSE;
text->initialized = TRUE;
if ( xi_text_editing_is( text ) )
{
xi_text_editing_stop( text );
restart = TRUE;
}
len = strlen( text->string );
text->allocated_length = len;
if ( text->scrollbar )
{
if ( !text->sb_width )
text->sb_width = ( int ) XinMetricGet( XinMetricVerticalScrollBarWidth );
text->internal_pix_width = text->pix_width - text->sb_width - SB_DELTA;
}
else
text->internal_pix_width = text->pix_width;
if ( text->multi_line )
xi_text_wrap_internal( text );
if ( text->sb_win )
xi_text_sb_set( text );
if ( restart )
{
xi_text_editing_start( text );
if ( xi_get_attrib( text->parent_obj ) & XI_ATR_AUTOSELECT )
xi_text_selection_set_internal( text, 0, SHRT_MAX,
0, TRUE, TRUE );
}
}
/*
this function wraps the text of an xi_text.
*/
void
xi_text_wrap( XI_TEXT * text )
{
XinFont *font;
BOOLEAN restart = FALSE;
if ( xi_text_editing_is( text ) )
{
xi_text_editing_stop( text );
restart = TRUE;
}
font = text->font;
XinWindowFontMap( text->win, font );
if ( text->scrollbar )
{
if ( !text->sb_width )
text->sb_width = ( int ) XinMetricGet( XinMetricVerticalScrollBarWidth );
text->internal_pix_width = text->pix_width - text->sb_width - SB_DELTA;
}
else
text->internal_pix_width = text->pix_width;
if ( text->multi_line )
xi_text_wrap_internal( text );
if ( text->sb_win )
xi_text_sb_set( text );
if ( restart )
xi_text_editing_start( text );
}
/*
this function sets the text of an xi_text. if the pix_width has changed from
the last time this function was called, then wrapping is performed even if the
text is the same. set_font is used by the list. when setting lots of text,
the list knows that the font for the xi_text currently being set is the same
font as the xi_text last set. this eliminates a call to the windowing system
to set the font.
*/
void
xi_text_pix_width_and_text_set( XI_TEXT * text, char *string, int pix_width, BOOLEAN set_font )
{
int len;
XinFont *font;
BOOLEAN restart = FALSE;
if ( text->string )
if ( text->pix_width == pix_width &&
strcmp( text->string, string ) == 0 &&
text->initialized )
return;
text->initialized = TRUE;
if ( xi_text_editing_is( text ) )
{
xi_text_editing_stop( text );
restart = TRUE;
}
font = text->font;
if ( set_font )
XinWindowFontMap( text->win, font );
len = reallocate_text( text, strlen( string ) + 1, FALSE );
text->pix_width = pix_width;
if ( text->scrollbar )
{
if ( !text->sb_width )
text->sb_width = ( int ) XinMetricGet( XinMetricVerticalScrollBarWidth );
text->internal_pix_width = text->pix_width - text->sb_width - SB_DELTA;
}
else
text->internal_pix_width = text->pix_width;
memcpy( text->string, string, len );
text->string[ len - 1 ] = '\0';
if ( text->multi_line )
xi_text_wrap_internal( text );
if ( text->sb_win )
xi_text_sb_set( text );
if ( restart )
{
xi_text_editing_start( text );
if ( xi_get_attrib( text->parent_obj ) & XI_ATR_AUTOSELECT )
xi_text_selection_set_internal( text, 0, SHRT_MAX,
0, TRUE, TRUE );
}
}
/*
this function sets the pix width of an xi_text.
*/
void
xi_text_pix_width_set( XI_TEXT * text, int pix_width )
{
XinFont *font;
BOOLEAN restart = FALSE;
if ( text->string )
if ( text->pix_width == pix_width &&
text->initialized )
return;
text->initialized = TRUE;
if ( xi_text_editing_is( text ) )
{
xi_text_editing_stop( text );
restart = TRUE;
}
font = text->font;
XinWindowFontMap( text->win, font );
text->pix_width = pix_width;
if ( text->scrollbar )
{
if ( !text->sb_width )
text->sb_width = ( int ) XinMetricGet( XinMetricVerticalScrollBarWidth );
text->internal_pix_width = text->pix_width - text->sb_width - SB_DELTA;
}
else
text->internal_pix_width = text->pix_width;
if ( text->multi_line )
xi_text_wrap_internal( text );
if ( text->sb_win )
xi_text_sb_set( text );
if ( restart )
xi_text_editing_start( text );
}
void
xi_text_destruct( XI_TEXT * text )
{
if ( text->sb_win )
if ( !xi_itf_in_event_destroy( text->itf ) )
XinWindowDestroy( text->sb_win );
xi_tree_free( text );
}
/*
draws the rectangle to the right of text on any line. after drawing the
text (in opaque mode), there often is still a small amount of space to the
right of the text, and to the left of the right edge of the edit control.
this function fills that space with the background color.
for a right justified field, this function draws the rectangle to the left
of the text and to the right of the left edge of the edit control.
*/
static void
draw_end_rect( XI_TEXT * text, char *str, int len, int left, int right,
int baseline, XinDrawTools * save_dt, int delta_x )
{
int x;
XinRect r;
XinDrawTools dt;
x = XinFontTextWidthGet( text->font, str, len );
r.top = baseline - text->leading - text->ascent;
r.bottom = baseline + text->descent;
if ( text->right_justify )
{
r.left = left;
r.right = right - x + delta_x;
}
else
{
r.left = left + x - delta_x;
r.right = right;
}
if ( r.left < r.right )
{
dt = *save_dt;
dt.brush.fore_color = text->back_color_in_use;
dt.brush.pattern = XinBrushSolid;
dt.pen.pattern = XinPenHollow;
dt.pen.width = 1;
dt.pen.fore_color = XI_COLOR_WHITE;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( text->win, &dt );
xi_draw_rect( text->win, &r );
XinWindowDrawToolsSet( text->win, save_dt );
}
}
static void
free_save_text_state( void )
{
xi_tree_free( save_text );
save_text = NULL;
}
static char *
alloc_password_string( int len, void *parent )
{
int cnt;
char *str;
str = xi_tree_malloc( len + 1, parent );
for ( cnt = 0; cnt < len; ++cnt )
str[cnt] = '#';
str[len] = '\0';
return str;
}
/*
this function draws the XI_TEXT. if save_text is set, then this function only
displays differences between save_text and text.
*/
static void
xi_text_draw_internal( XI_TEXT * text, XinColor color, XinColor back_color, BOOLEAN do_carets )
{
XinRect rct;
char *s;
char *sws;
int cnt,
nbr_lines,
baseline,
line_to_draw,
top_of_rect;
XinWindow win = text->win;
XI_TEXT_LINE_BREAK *lb;
XinDrawTools save_dt;
BOOLEAN turn_caret_on = FALSE;
if ( do_carets && xi_text_editing_is( text ) )
{
xi_text_caret_off( text );
turn_caret_on = TRUE;
}
if ( color )
{
text->fore_color_in_use = color;
text->back_color_in_use = back_color;
}
else
{
text->fore_color_in_use = text->fore_color;
text->back_color_in_use = text->back_color;
}
XinWindowFontMap( text->win, text->font );
rct = text->prect;
rct.right = rct.left + text->internal_pix_width;
xi_rect_intersect( &rct, &rct, &text->clip_rect );
xi_set_clip( win, &rct );
XinWindowDrawToolsGet( win, &save_dt );
if ( text->multi_line )
{
if ( text->max_lines_to_draw )
nbr_lines = min( xi_text_nbr_lines_get( text ), text->max_lines_to_draw );
else
nbr_lines = xi_text_nbr_lines_get( text );
if ( text->delta_y + nbr_lines > text->nbr_lines )
nbr_lines -= ( text->delta_y + nbr_lines - text->nbr_lines );
}
s = text->string;
if ( save_text )
sws = save_text->string;
if ( text->multi_line )
{
for ( cnt = 0, line_to_draw = text->delta_y;
cnt < nbr_lines; ++cnt, ++line_to_draw )
{
int len = -1;
XinDrawTools dt;
char *str;
BOOLEAN dont_draw = FALSE;
lb = &text->line_breaks[line_to_draw];
if ( line_to_draw < text->nbr_lines - 1 )
len = xi_text_line_break_get( text, line_to_draw + 1 ) -
xi_text_line_break_get( text, line_to_draw );
//#if XIWS == XIWS_WM
// baseline = rct.top + 8;
//#else
baseline = rct.top + text->leading + text->ascent;
// #endif
str = &s[xi_text_line_break_get( text, line_to_draw )];
if ( len == -1 )
len = strlen( str );
if ( ( &s[xi_text_line_break_get( text, line_to_draw )] )[len - 1] == '\n' )
len--;
if ( save_text && line_to_draw < save_text->nbr_lines )
{
int sw_len = -1;
char *sw_str;
if ( line_to_draw < save_text->nbr_lines - 1 )
sw_len = xi_text_line_break_get( save_text, line_to_draw + 1 ) - xi_text_line_break_get( save_text, line_to_draw );
sw_str = &sws[xi_text_line_break_get( save_text, line_to_draw )];
if ( sw_len == -1 )
sw_len = strlen( sw_str );
if ( ( &sws[xi_text_line_break_get( save_text, line_to_draw )] )[sw_len - 1] == '\n' )
sw_len--;
if ( ( sw_len == len ) &&
( strncmp( str, sw_str, len ) == 0 ) &&
( text->line_breaks[line_to_draw].ip1 == save_text->line_breaks[line_to_draw].ip1 ) &&
( text->line_breaks[line_to_draw].ip2 == save_text->line_breaks[line_to_draw].ip2 ) )
dont_draw = TRUE;
/* This piece of code causes problems with scrolling. Why is it here?
* if ( save_text->line_breaks[line_to_draw].ip1 != -1 &&
* save_text->line_breaks[line_to_draw].ip1 ==
* save_text->line_breaks[line_to_draw].ip2 ) dont_draw = FALSE; */
}
if ( !dont_draw )
{
dt = save_dt;
dt.text_fore_color = text->fore_color_in_use;
dt.text_back_color = text->back_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( text->win, &dt );
if ( lb->ip1 != lb->ip2 )
{
if ( lb->ip1 )
xi_draw_text( win, text->font, rct.left, baseline,
str, lb->ip1 );
dt = save_dt;
dt.text_fore_color = text->back_color_in_use;
dt.text_back_color = text->fore_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( win, &dt );
if ( lb->ip2 < len )
{
int x;
x = XinFontTextWidthGet( text->font, str, lb->ip1 );
xi_draw_text( win, text->font, rct.left + x, baseline,
&str[lb->ip1], lb->ip2 - lb->ip1 );
dt = save_dt;
dt.text_fore_color = text->fore_color_in_use;
dt.text_back_color = text->back_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( text->win, &dt );
x = XinFontTextWidthGet( text->font, str, lb->ip2 );
xi_draw_text( win, text->font, rct.left + x, baseline,
&str[lb->ip2], len - lb->ip2 );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, 0 );
XinWindowDrawToolsSet( win, &save_dt );
}
else
{
int x;
dt = save_dt;
dt.text_fore_color = text->back_color_in_use;
dt.text_back_color = text->fore_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( win, &dt );
x = XinFontTextWidthGet( text->font, str, lb->ip1 );
xi_draw_text( win, text->font, rct.left + x, baseline,
&str[lb->ip1], len - lb->ip1 );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, 0 );
XinWindowDrawToolsSet( text->win, &save_dt );
}
}
else
{
xi_draw_text( win, text->font, rct.left, baseline,
&s[xi_text_line_break_get( text, line_to_draw )], len );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, 0 );
}
}
#if XIWS == XIWS_WM
rct.top += text->font_height;
#else
rct.top += xi_text_font_height_get( text );
#endif
}
}
if ( !text->multi_line )
{
char *str;
char *sw_str;
int len,
sw_len;
BOOLEAN dont_draw;
len = strlen( text->string );
if ( text->password )
str = alloc_password_string( len, text );
else
str = text->string;
//#if XIWS == XI_WMWS
// baseline = rct.top + 8;
//#else
baseline = rct.top + text->leading + text->ascent;
//#endif
dont_draw = FALSE;
if ( save_text )
{
sw_len = strlen( save_text->string );
if ( save_text->password )
sw_str = alloc_password_string( sw_len, save_text );
else
sw_str = save_text->string;
if ( ( sw_len == len ) &&
( strncmp( str, sw_str, len ) == 0 ) &&
( ( ( text->ip1 == save_text->ip1 ) && ( text->ip2 == save_text->ip2 ) ) ||
( ( text->ip1 == text->ip2 ) && ( save_text->ip1 == save_text->ip2 ) ) )
)
dont_draw = TRUE;
}
if ( !dont_draw )
{
int delta_x_pix;
XinDrawTools dt;
int text_pix_len = XinFontTextWidthGet( text->font, str, -1 );
dt = save_dt;
dt.text_fore_color = text->fore_color_in_use;
dt.text_back_color = text->back_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( text->win, &dt );
if ( text->right_justify )
{
delta_x_pix = XinFontTextWidthGet( text->font,
&str[len - text->delta_x], text->delta_x );
if ( text->ip1 != text->ip2 )
{
if ( text->ip1 )
xi_draw_text( win, text->font,
rct.right - text_pix_len + delta_x_pix, baseline, str,
text->ip1 );
dt = save_dt;
dt.text_fore_color = text->back_color_in_use;
dt.text_back_color = text->fore_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( win, &dt );
if ( text->ip2 < len )
{
int x;
x = XinFontTextWidthGet( text->font, &str[text->ip1], strlen( &str[text->ip1] ) );
xi_draw_text( win, text->font, rct.right - x + delta_x_pix, baseline,
&str[text->ip1], text->ip2 - text->ip1 );
dt = save_dt;
dt.text_fore_color = text->fore_color_in_use;
dt.text_back_color = text->back_color_in_use;
XinWindowDrawToolsSet( win, &dt );
x = XinFontTextWidthGet( text->font, &str[text->ip2], strlen( &str[text->ip2] ) );
xi_draw_text( win, text->font, rct.right - x + delta_x_pix, baseline,
&str[text->ip2], len - text->ip2 );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, delta_x_pix );
XinWindowDrawToolsSet( win, &save_dt );
}
else
{
int x;
int len_in_pix = XinFontTextWidthGet( text->font, str, -1 );
dt = save_dt;
dt.text_fore_color = text->back_color_in_use;
dt.text_back_color = text->fore_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( win, &dt );
x = XinFontTextWidthGet( text->font, str, text->ip1 );
xi_draw_text( win, text->font, rct.right - len_in_pix + x + delta_x_pix, baseline,
&str[text->ip1], len - text->ip1 );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, delta_x_pix );
XinWindowDrawToolsSet( text->win, &save_dt );
}
}
else
{
int x;
x = XinFontTextWidthGet( text->font, str, -1 );
xi_draw_text( win, text->font, rct.right - x + delta_x_pix, baseline,
str, len );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, delta_x_pix );
}
}
else
{
delta_x_pix = XinFontTextWidthGet( text->font, str, text->delta_x );
if ( text->ip1 != text->ip2 )
{
if ( text->ip1 )
xi_draw_text( win, text->font, rct.left - delta_x_pix, baseline,
str, text->ip1 );
dt = save_dt;
dt.text_fore_color = text->back_color_in_use;
dt.text_back_color = text->fore_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( win, &dt );
if ( text->ip2 < len )
{
int x;
x = XinFontTextWidthGet( text->font, str, text->ip1 );
xi_draw_text( win, text->font, rct.left + x - delta_x_pix, baseline,
&str[text->ip1], text->ip2 - text->ip1 );
dt = save_dt;
dt.text_fore_color = text->fore_color_in_use;
dt.text_back_color = text->back_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( win, &dt );
x = XinFontTextWidthGet( text->font, str, text->ip2 );
xi_draw_text( win, text->font, rct.left + x - delta_x_pix, baseline,
&str[text->ip2], len - text->ip2 );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, delta_x_pix );
XinWindowDrawToolsSet( win, &save_dt );
}
else
{
int x;
dt = save_dt;
dt.text_fore_color = text->back_color_in_use;
dt.text_back_color = text->fore_color_in_use;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( win, &dt );
x = XinFontTextWidthGet( text->font, str, text->ip1 );
xi_draw_text( win, text->font, rct.left + x - delta_x_pix, baseline,
&str[text->ip1], len - text->ip1 );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, delta_x_pix );
XinWindowDrawToolsSet( text->win, &save_dt );
}
}
else
{
xi_draw_text( win, text->font, rct.left - delta_x_pix, baseline,
str, len );
draw_end_rect( text, str, len, rct.left, rct.right,
baseline, &save_dt, delta_x_pix );
}
}
}
if ( text->password )
{
xi_tree_free( str );
if ( save_text )
xi_tree_free( sw_str );
}
}
if ( text->multi_line )
{
top_of_rect = text->prect.top + nbr_lines * text->font_height;
if ( top_of_rect < text->prect.bottom )
{
XinRect r;
XinDrawTools dt;
r.top = top_of_rect;
r.left = text->prect.left;
r.bottom = text->prect.bottom;
r.right = text->prect.left + text->internal_pix_width;
dt = save_dt;
dt.brush.fore_color = text->back_color_in_use;
dt.brush.pattern = XinBrushSolid;
dt.pen.pattern = XinPenHollow;
dt.pen.width = 1;
dt.pen.fore_color = XI_COLOR_WHITE;
dt.opaque_text = TRUE;
XinWindowDrawToolsSet( text->win, &dt );
xi_draw_rect( text->win, &r );
}
}
#if 0
/* draw line to right of text if there is a scrollbar */
if ( text->scrollbar )
{
XinPoint p;
XinDrawTools dt;
dt = save_dt;
dt.pen.pattern = XinPenSolid;
dt.pen.width = 1;
dt.pen.fore_color = text->fore_color_in_use;
XinWindowDrawToolsSet( text->win, &dt );
p.h = text->prect.left + text->internal_pix_width;
p.v = text->prect.top;
xi_move_to( text->win, p );
p.v = text->prect.bottom;
xi_draw_line( text->win, p );
}
#endif
/* If there is a scrollbar, fill the space between the text and the sb, and
* the sb's space if we aren't editing. */
if ( text->scrollbar )
{
XinRect rect;
XinDrawTools dt;
dt = save_dt;
dt.pen.pattern = XinPenSolid;
dt.pen.width = 1;
dt.pen.fore_color = text->back_color_in_use;
dt.brush.fore_color = text->back_color_in_use;
dt.brush.pattern = XinBrushSolid;
XinWindowDrawToolsSet( text->win, &dt );
rect = text->prect;
rect.left = text->prect.left + text->internal_pix_width;
if ( text->sb_win )
rect.right = rect.right - text->sb_width;
xi_rect_intersect( &rect, &rect, &text->clip_rect );
xi_set_clip( win, &rect );
xi_draw_rect( text->win, &rect );
}
XinWindowDrawToolsSet( text->win, &save_dt );
if ( turn_caret_on )
xi_text_caret_on( text );
}
void
xi_text_draw( XI_TEXT * text, XinColor color, XinColor back_color, BOOLEAN update )
{
xi_text_draw_internal( text, color, back_color, ( BOOLEAN ) ! update );
}
/*
there are two sets of state for insertion points. the state that this module
initially modifies when editing is the ip1 and ip2 in the xi_text structure.
from this, for convenience when drawing the edit control, and for convenience
when turning on the caret as appropriate, we calculate insertion points for
every line in the line_breaks array. this function is used by recalc_line_ips,
which does calculation of insertion points for every line in the line_breaks
array.
this function is only used if the xi_text is multi_line.
*/
static void
recalc_single_ip( XI_TEXT * text, int ip, BOOLEAN starting, int active_ip )
{
int line_cnt;
int total_cnt,
old_total_cnt;
total_cnt = 0;
for ( line_cnt = 0; line_cnt < text->nbr_lines; ++line_cnt )
{
int line_len;
if ( line_cnt < text->nbr_lines - 1 )
line_len = text->line_breaks[line_cnt + 1].line_break -
text->line_breaks[line_cnt].line_break;
else
line_len = strlen( &text->string[text->line_breaks[line_cnt].line_break] );
old_total_cnt = total_cnt;
total_cnt += line_len;
if ( ip < total_cnt || ( line_cnt == text->nbr_lines - 1 && ip <= total_cnt ) )
{
int this_line_cnt;
this_line_cnt = ip - old_total_cnt;
if ( starting )
text->line_breaks[line_cnt].ip1 = this_line_cnt;
else
text->line_breaks[line_cnt].ip2 = this_line_cnt;
if ( ip == active_ip )
text->line_breaks[line_cnt].active_ip = this_line_cnt;
return;
}
}
}
/*
there are two sets of state for insertion points. the state that this module
initially modifies when editing is the ip1 and ip2 in the XI_TEXT structure.
from this, for convenience when drawing the edit control, and for convenience
when turning on the caret as appropriate, we calculate insertion points for
every line in the line_breaks array. this function does the calculation of
insertion points for the line_breaks array.
for each line in the line_breaks array:
- if ip1 and ip2 both == -1, then the caret is not on this line, and no text
on this line is selected.
- if ip1 and ip2 are both != -1, and they are not equal, then the text on this
line from ip1 to ip2 is selected.
- if ip1 and ip2 are both != -1, and they are equal, then the caret is placed
at the indicated position in ip1.
- it is invalid for either ip1 or ip2 to be -1 where the other is not.
this function is used only if the xi_text is multi_line.
*/
static void
recalc_line_ips( XI_TEXT * text )
{
int cnt;
XI_TEXT_LINE_BREAK *lb;
enum
{
state_before_sel,
state_in_sel,
state_after_sel
} state;
int active_ip;
active_ip = ( text->selection_start_ip == text->ip1 ) ? text->ip2 : text->ip1;
for ( cnt = 0, lb = text->line_breaks; cnt < text->nbr_lines; ++cnt, ++lb )
{
lb->ip1 = lb->ip2 = -1;
lb->active_ip = -2;
}
recalc_single_ip( text, text->ip1, TRUE, active_ip );
recalc_single_ip( text, text->ip2, FALSE, active_ip );
state = state_before_sel;
for ( cnt = 0, lb = text->line_breaks; cnt < text->nbr_lines; ++cnt, ++lb )
{
if ( lb->ip1 == -1 && lb->ip2 != -1 )
{
lb->ip1 = 0;
state = state_after_sel;
}
if ( lb->ip1 != -1 && lb->ip2 == -1 )
{
/* there is no way that lb->ip1 can not be -1, and lb->ip2 is -1 for the
* last line, hence the following line will always work. */
lb->ip2 = text->line_breaks[cnt + 1].line_break - lb->line_break;
state = state_in_sel;
}
if ( lb->ip1 == -1 && lb->ip2 == -1 && state == state_in_sel )
{
lb->ip1 = 0;
if ( cnt < text->nbr_lines - 1 )
lb->ip2 = text->line_breaks[cnt + 1].line_break - lb->line_break;
else
lb->ip2 = strlen( &text->string[text->line_breaks[cnt].line_break] );
}
}
}
/*
this function sets the selection. if do_carets is false, then the caret is
not turned on after setting selection. this happens when editing is stopped.
*/
void
xi_text_selection_set_internal( XI_TEXT * text, int ip1, int ip2,
int selection_start_ip, BOOLEAN do_carets, BOOLEAN map_font )
{
int len;
if ( ! text->string )
return;
if ( ip1 > ip2 )
{
int temp;
temp = ip1;
ip1 = ip2;
ip2 = temp;
}
text->ip1 = ip1;
len = strlen( text->string );
if ( ip2 > len )
ip2 = len;
text->ip2 = ip2;
text->selection_start_ip = selection_start_ip;
if ( !text->editing && do_carets )
return;
if ( map_font )
XinWindowFontMap( text->win, text->font );
if ( do_carets )
xi_text_caret_off( text );
if ( text->multi_line )
{
xi_text_wrap_internal( text );
recalc_line_ips( text );
calc_delta_y( text );
}
else
calc_delta_x( text );
if ( text->sb_win )
xi_text_sb_set( text );
xi_text_draw_internal( text, 0L, 0L, FALSE );
if ( do_carets )
xi_text_caret_on( text );
}
void
xi_text_selection_set( XI_TEXT * text, int ip1, int ip2 )
{
xi_text_selection_set_internal( text, ip1, ip2, ip2, TRUE, TRUE );
}
static void
xi_text_caret_off( XI_TEXT * text )
{
XinWindowCaretOff( text->win );
}
static void
xi_text_caret_on( XI_TEXT * text )
{
int cnt,
caret_base;
XI_TEXT_LINE_BREAK *lb;
int nbr_lines,
caret_line;
if ( text->multi_line )
{
if ( text->max_lines_to_draw )
nbr_lines = min( xi_text_nbr_lines_get( text ), text->max_lines_to_draw );
else
nbr_lines = xi_text_nbr_lines_get( text );
if ( text->delta_y + nbr_lines > text->nbr_lines )
nbr_lines -= ( text->delta_y + nbr_lines - text->nbr_lines );
caret_base = text->prect.top + text->font_height;
for ( cnt = 0, caret_line = text->delta_y;
cnt < nbr_lines;
++cnt, ++caret_line )
{
int ip;
BOOLEAN set_caret = FALSE;
lb = &text->line_breaks[caret_line];
if ( lb->ip1 == lb->active_ip )
{
ip = lb->ip1;
set_caret = TRUE;
}
if ( lb->ip2 == lb->active_ip )
{
ip = lb->ip2;
set_caret = TRUE;
}
if ( set_caret )
{
char *str;
int x;
XinRect rect;
str = &text->string[lb->line_break];
x = XinFontTextWidthGet( text->font, str, ip );
rect = text->prect;
rect.right = rect.left + text->internal_pix_width;
xi_rect_intersect( &rect, &rect, &text->clip_rect );
xi_caret_on( text->win, text->prect.left + x, caret_base,
text->font_height, &rect );
return;
}
caret_base += text->font_height;
}
}
else
{
int caret_base = text->prect.top + text->font_height;
int ip = ( text->selection_start_ip == text->ip1 ) ? text->ip2 : text->ip1;
int x;
char *pwc = "#";
int pwc_len;
if ( text->password )
{
pwc_len = XinFontTextWidthGet( text->font, pwc, 1 );
x = pwc_len * ip;
}
else
x = XinFontTextWidthGet( text->font, text->string, ip );
if ( text->right_justify )
{
int len_in_pix = XinFontTextWidthGet( text->font, text->string, -1 );
int len = strlen( text->string );
int delta_x_pix = XinFontTextWidthGet( text->font,
&text->string[len - text->delta_x], text->delta_x );
int hpos = text->prect.right - len_in_pix + delta_x_pix + x;
XinRect r1 = text->prect;
XinRect r2 = text->clip_rect;
XinRect r3;
/* only turn on caret if caret is in intersection of prect and clipping
* rect */
if ( xi_rect_intersect( &r3, &r1, &r2 ) )
{
int caret_height = text->font_height;
if ( hpos >= r3.left && hpos <= r3.right )
{
if ( caret_base > r3.bottom )
{
int delta = caret_base - r3.bottom;
caret_base -= delta;
caret_height -= delta;
}
xi_caret_on( text->win, hpos,
caret_base, caret_height, &r3 );
}
}
}
else
{
int delta_x_pix;
XinRect r1 = text->prect;
XinRect r2 = text->clip_rect;
XinRect r3;
int hpos;
if ( text->password )
delta_x_pix = pwc_len * text->delta_x;
else
delta_x_pix = XinFontTextWidthGet( text->font, text->string, text->delta_x );
hpos = text->prect.left + x - delta_x_pix;
/* only turn on caret if caret is in intersection of prect and clipping
* rect */
if ( xi_rect_intersect( &r3, &r1, &r2 ) )
{
int caret_height = text->font_height;
if ( hpos >= r3.left && hpos <= r3.right )
{
xi_caret_on( text->win, hpos, caret_base,
caret_height, &r3 );
}
}
}
}
}
void
xi_text_editing_start( XI_TEXT * text )
{
if ( text->editing == TRUE )
return;
text->editing = TRUE;
if ( text->scrollbar && !text->scrollbar_always_visible )
{
XinWindowDef Def;
XinRect rct;
MEMCLEAR( Def );
Def.control_id = text->cid;
Def.type = XinWindowTypeVerticalScrollBar;
rct.top = text->prect.top;
rct.bottom = text->prect.bottom;
rct.right = text->prect.left + text->internal_pix_width + SB_DELTA + text->sb_width;
rct.left = text->prect.left + text->internal_pix_width + SB_DELTA;
Def.p_rect = &rct;
Def.title = "";
Def.visible = TRUE;
Def.enabled = TRUE;
Def.parent = text->win;
if ( text->sb_win != XI_NULL_WINDOW )
XinWindowDestroy( text->sb_win );
text->sb_win = XinWindowCreate( &Def );
}
if ( text->multi_line )
{
xi_text_wrap_internal( text );
recalc_line_ips( text );
calc_delta_y( text );
}
else
calc_delta_x( text );
if ( text->sb_win )
xi_text_sb_set( text );
xi_text_draw_internal( text, 0L, 0L, FALSE );
xi_text_caret_on( text );
}
void
xi_text_selection_get( XI_TEXT * text, int *c1, int *c2 )
{
*c1 = text->ip1;
*c2 = text->ip2;
}
void
xi_text_selection_get_internal( XI_TEXT * text, int *c1, int *c2, int *start_ip )
{
*c1 = text->ip1;
*c2 = text->ip2;
*start_ip = text->selection_start_ip;
}
static void
save_text_state( XI_TEXT * text )
{
save_text = xi_tree_malloc( sizeof( XI_TEXT ), NULL );
save_text->string = xi_tree_malloc( strlen( text->string ) + 1, save_text );
save_text->font = text->font;
strcpy( save_text->string, text->string );
if ( text->multi_line )
{
save_text->line_breaks = xi_tree_malloc( sizeof( XI_TEXT_LINE_BREAK ) * text->nbr_lines, text );
memcpy( save_text->line_breaks, text->line_breaks, sizeof( XI_TEXT_LINE_BREAK ) * text->nbr_lines );
}
save_text->nbr_lines = text->nbr_lines;
save_text->ip1 = text->ip1;
save_text->ip2 = text->ip2;
save_text->delta_y = text->delta_y;
save_text->delta_x = text->delta_x;
save_text->selection_start_ip = text->selection_start_ip;
}
static void
display_if_necessary( XI_TEXT * text, BOOLEAN recalc_delta_y )
{
/* This contains a hack for Mac - xvt_dwin_scroll_rect is not called
the scrollbar position does not update */
#if XIWS == XIWS_MAC
BOOLEAN scrolled_rect = FALSE;
#endif
xi_text_caret_off( text );
if ( text->multi_line )
{
xi_text_wrap_internal( text );
recalc_line_ips( text );
}
xi_text_draw( text, 0L, 0L, FALSE );
if ( text->multi_line )
{
if ( recalc_delta_y )
calc_delta_y( text );
}
else
calc_delta_x( text );
if ( text->sb_win )
xi_text_sb_set( text );
if ( save_text && save_text->delta_y != text->delta_y )
{
int delta = save_text->delta_y - text->delta_y;
XinRect r;
/* need to do this here, because a Paint event will come thru */
free_save_text_state( );
xi_set_update_obj( text->parent_obj );
r = text->prect;
r.bottom = r.top + text->max_lines_to_draw * text->font_height;
r.right = r.left + text->internal_pix_width;
xi_scroll_rect( text->win, &r, 0,
delta * text->font_height );
#if XIWS == XIWS_MAC
scrolled_rect = TRUE;
#endif
}
if ( save_text && save_text->delta_x != text->delta_x )
{
if ( text->right_justify )
{
int old_delta_x_pix = XinFontTextWidthGet( save_text->font,
&save_text->string[strlen( save_text->string ) - save_text->delta_x],
save_text->delta_x );
int delta_x_pix = XinFontTextWidthGet( text->font,
&text->string[strlen( text->string ) - text->delta_x],
text->delta_x );
XinRect r;
/* need to do this here, because a Paint event will come thru */
free_save_text_state( );
xi_set_update_obj( text->parent_obj );
r = text->prect;
r.bottom = r.top + text->font_height;
r.left = r.right - text->internal_pix_width;
xi_scroll_rect( text->win, &r, delta_x_pix - old_delta_x_pix, 0 );
#if XIWS == XIWS_MAC
scrolled_rect = TRUE;
#endif
}
else
{
int old_delta_x_pix = XinFontTextWidthGet( save_text->font, save_text->string,
save_text->delta_x );
int delta_x_pix = XinFontTextWidthGet( text->font, text->string,
text->delta_x );
XinRect r;
/* need to do this here, because a Paint event will come thru */
free_save_text_state( );
xi_set_update_obj( text->parent_obj );
r = text->prect;
r.bottom = r.top + text->font_height;
r.right = r.left + text->internal_pix_width;
xi_scroll_rect( text->win, &r, old_delta_x_pix - delta_x_pix, 0 );
#if XIWS == XIWS_MAC
scrolled_rect = TRUE;
#endif
}
}
#if XIWS == XIWS_MAC
if (!scrolled_rect)
{
XinRect r;
r = text->prect;
r.bottom = r.top + text->font_height;
r.right = r.left + text->internal_pix_width;
xi_scroll_rect( text->win, &r, 0, 0 );
}
#endif
xi_text_caret_on( text );
if ( save_text )
free_save_text_state( );
}
static int
get_line_from_ip( XI_TEXT * text, int ip )
{
int cnt;
for ( cnt = 0; cnt < text->nbr_lines; ++cnt )
{
if ( text->line_breaks[cnt].line_break > ip )
return ( cnt - 1 );
}
return ( text->nbr_lines - 1 );
}
static void
calc_delta_y( XI_TEXT * text )
{
int ip,
line;
if ( text->nbr_lines <= text->max_lines_to_draw )
{
text->delta_y = 0;
return;
}
if ( text->ip1 == text->selection_start_ip )
ip = text->ip2;
else
ip = text->ip1;
line = get_line_from_ip( text, ip );
if ( line < text->delta_y )
{
text->delta_y = line;
if ( text->nbr_lines - text->delta_y < text->max_lines_to_draw )
text->delta_y = text->nbr_lines - text->max_lines_to_draw;
return;
}
if ( line > text->delta_y + ( text->max_lines_to_draw - 1 ) )
{
text->delta_y += line - ( text->delta_y + ( text->max_lines_to_draw - 1 ) );
if ( text->nbr_lines - text->delta_y < text->max_lines_to_draw )
text->delta_y = text->nbr_lines - text->max_lines_to_draw;
return;
}
if ( text->nbr_lines - text->delta_y < text->max_lines_to_draw )
text->delta_y = text->nbr_lines - text->max_lines_to_draw;
}
/*
this function calculates text->delta_x such that the insertion point that is
not the selection start insertion point is visible.
*/
static void
calc_delta_x( XI_TEXT * text )
{
int ip,
delta_x_pix,
dx,
ip_pix,
cnt;
int len = strlen( text->string );
if ( text->ip1 == text->selection_start_ip )
ip = text->ip2;
else
ip = text->ip1;
if ( text->right_justify )
{
/* for a right justified field, if the insertion point is to the right of
* the visible area, then delta_x is easy to calculate. it is the length
* of the string minus the insertion point. */
if ( ( len - ip ) < text->delta_x )
{
text->delta_x = len - ip;
return;
}
}
else
{
/* for a left justified field, if the insertion point is to the left of the
* visible area, then delta_x is easy to calculate. it is the insertion
* point. */
if ( ip < text->delta_x )
{
text->delta_x = ip;
return;
}
}
if ( text->right_justify )
{
/* for a right justified field, if the insertion point is to the left of
* the visible area, then the algorythm is as follows: 1. calculate the
* length of delta_x in pixels 2. calculate the length of the string from
* ip to the end of the string 3. calculate the number of pixels that we
* must make visible: (2) - (1) - internal pixel width 4. count the number
* of characters from delta_x, going to the left, until the length of the
* characters is greater than (3) */
delta_x_pix = XinFontTextWidthGet( text->font,
&text->string[len - text->delta_x], text->delta_x );
ip_pix = XinFontTextWidthGet( text->font, &text->string[ip], -1 );
dx = ip_pix - delta_x_pix - text->internal_pix_width;
for ( cnt = text->delta_x; cnt < len; ++cnt )
if ( XinFontTextWidthGet( text->font, &text->string[len - cnt], cnt - text->delta_x ) > dx )
break;
text->delta_x = cnt;
}
else
{
/* for a left justified field, if the insertion point is to the right of
* the visible area, then the algorythm is as follows: 1. calculate the
* length of delta_x in pixels 2. calculate the length of the string from
* the beginning of the string to the insertion point 3. calculate the
* number of pixels that we must make visible: (2) - (1) - internal pixel
* width 4. count the number of characters from delta_x, going to the
* right, until the length of the characters is greater than (3) */
delta_x_pix = XinFontTextWidthGet( text->font, text->string, text->delta_x );
ip_pix = XinFontTextWidthGet( text->font, text->string, ip );
dx = ip_pix - delta_x_pix - text->internal_pix_width;
for ( cnt = text->delta_x; cnt < len; ++cnt )
if ( XinFontTextWidthGet( text->font, &text->string[text->delta_x], cnt - text->delta_x ) >= dx )
break;
text->delta_x = cnt;
}
}
/*
this is a utility function that returns the text and line length for a given line.
this function is only used if the xi_text is multi_line.
*/
static char *
get_line( XI_TEXT * text, int line, int *line_len )
{
int len;
char *s;
s = &text->string[text->line_breaks[line].line_break];
if ( line < text->nbr_lines - 1 )
len = text->line_breaks[line + 1].line_break -
text->line_breaks[line].line_break;
else
len = strlen( s );
if ( line_len )
*line_len = len;
return s;
}
/*
the functions key_left, key_right, key_line_home, key_line_end, key_word_left,
key_word_right, key_backspace, key_delete, key_character manipulate the xi_text
when editing. no modifications are necessary for single, multi_line, or
right justified edit controls.
the functions key_up, key_page_up, key_down, key_page_down are only used for
multi-line edit controls.
*/
static void
key_left( XI_TEXT * text, XinEvent * ep )
{
if ( text->ip1 == text->ip2 )
{
if ( ep->v.character.shift )
{
text->selection_start_ip = text->ip2;
if ( text->ip1 >= 1 )
--text->ip1;
}
else
{
if ( text->ip1 >= 1 )
text->selection_start_ip = text->ip1 = --text->ip2;
}
}
else if ( text->ip1 == text->selection_start_ip )
{
if ( ep->v.character.shift )
text->ip2--;
else
text->selection_start_ip = text->ip1 = --text->ip2;
}
else
{
if ( ep->v.character.shift )
{
if ( text->ip1 >= 1 )
text->ip1--;
}
else
{
int new_ip = text->ip1;
if ( text->ip1 >= 1 )
new_ip = text->ip1 - 1;
text->selection_start_ip = text->ip1 = text->ip2 = new_ip;
}
}
display_if_necessary( text, TRUE );
}
static void
key_right( XI_TEXT * text, XinEvent * ep )
{
if ( text->ip1 == text->ip2 )
{
if ( ep->v.character.shift )
{
text->selection_start_ip = text->ip1;
if ( text->ip2 < ( int ) strlen( text->string ) )
++text->ip2;
}
else
{
if ( text->ip2 < ( int ) strlen( text->string ) )
text->selection_start_ip = text->ip1 = ++text->ip2;
}
}
else if ( text->ip2 == text->selection_start_ip )
{
if ( ep->v.character.shift )
text->ip1++;
else
text->selection_start_ip = text->ip2 = ++text->ip1;
}
else
{
if ( ep->v.character.shift )
{
if ( text->ip2 < ( int ) strlen( text->string ) )
text->ip2++;
}
else
{
int new_ip = text->ip2;
if ( text->ip2 < ( int ) strlen( text->string ) )
new_ip = text->ip2 + 1;
text->selection_start_ip = text->ip1 = text->ip2 = new_ip;
}
}
display_if_necessary( text, TRUE );
}
static BOOLEAN
get_ip_one_line_above( XI_TEXT * text, int ip, int *new_ip )
{
int line,
pix_len;
char *s;
char *pls;
line = get_line_from_ip( text, ip );
if ( line >= 1 )
{
int cnt,
len;
s = get_line( text, line, NULL );
pix_len = XinFontTextWidthGet( text->font, s, ip - text->line_breaks[line].line_break );
pls = get_line( text, line - 1, &len );
for ( cnt = 0; cnt < len; ++cnt )
{
int lpix_len;
lpix_len = XinFontTextWidthGet( text->font, pls, cnt );
if ( lpix_len >= pix_len )
{
*new_ip = text->line_breaks[line - 1].line_break + cnt;
return TRUE;
}
}
*new_ip = text->line_breaks[line - 1].line_break + cnt - 1;
return TRUE;
}
return FALSE;
}
static BOOLEAN
key_up( XI_TEXT * text, XinEvent * ep, BOOLEAN redisplay )
{
if ( !text->multi_line )
return FALSE;
if ( text->ip1 == text->ip2 )
{
int new_ip;
if ( get_ip_one_line_above( text, text->ip1, &new_ip ) )
{
if ( ep->v.character.shift )
{
text->ip1 = new_ip;
text->selection_start_ip = text->ip2;
}
else
text->selection_start_ip = text->ip1 = text->ip2 = new_ip;
if ( redisplay )
display_if_necessary( text, TRUE );
}
return TRUE;
}
if ( text->selection_start_ip == text->ip1 )
{
int new_ip;
if ( get_ip_one_line_above( text, text->ip2, &new_ip ) )
{
if ( ep->v.character.shift )
{
text->ip2 = new_ip;
if ( text->ip2 < text->ip1 )
{
int temp;
temp = text->ip1;
text->ip1 = text->ip2;
text->ip2 = temp;
text->selection_start_ip = text->ip2;
}
}
else
{
text->selection_start_ip = text->ip1 = text->ip2 = new_ip;
}
if ( redisplay )
display_if_necessary( text, TRUE );
}
}
else
{
int new_ip;
if ( get_ip_one_line_above( text, text->ip1, &new_ip ) )
{
if ( ep->v.character.shift )
text->ip1 = new_ip;
else
text->selection_start_ip = text->ip1 = text->ip2 = new_ip;
if ( redisplay )
display_if_necessary( text, TRUE );
} else if ( text->ip1 != text->ip2 && !ep->v.character.shift )
{
text->selection_start_ip = text->ip2 = text->ip1;
if ( redisplay )
display_if_necessary( text, TRUE );
}
}
return TRUE;
}
static void
key_page_up( XI_TEXT * text, XinEvent * ep )
{
int cnt;
if ( !text->multi_line )
return;
for ( cnt = 0; cnt < text->max_lines_to_draw; ++cnt )
key_up( text, ep, FALSE );
display_if_necessary( text, TRUE );
}
static BOOLEAN
get_ip_one_line_below( XI_TEXT * text, int ip, int *new_ip )
{
int line,
pix_len;
char *s;
char *pls;
line = get_line_from_ip( text, ip );
if ( line < text->nbr_lines - 1 )
{
int cnt,
len;
s = get_line( text, line, NULL );
pix_len = XinFontTextWidthGet( text->font, s, ip - text->line_breaks[line].line_break );
pls = get_line( text, line + 1, &len );
for ( cnt = 0; cnt < len; ++cnt )
{
int lpix_len;
lpix_len = XinFontTextWidthGet( text->font, pls, cnt );
if ( lpix_len >= pix_len )
{
*new_ip = text->line_breaks[line + 1].line_break + cnt;
return TRUE;
}
}
*new_ip = text->line_breaks[line + 1].line_break + cnt - 1;
if ( line + 1 == text->nbr_lines - 1 )
++* new_ip;
return TRUE;
}
return FALSE;
}
static BOOLEAN
key_down( XI_TEXT * text, XinEvent * ep, BOOLEAN redisplay )
{
if ( !text->multi_line )
return FALSE;
if ( text->ip1 == text->ip2 )
{
int new_ip;
if ( get_ip_one_line_below( text, text->ip1, &new_ip ) )
{
if ( ep->v.character.shift )
{
text->ip2 = new_ip;
text->selection_start_ip = text->ip1;
}
else
text->selection_start_ip = text->ip1 = text->ip2 = new_ip;
if ( redisplay )
display_if_necessary( text, TRUE );
}
return TRUE;
}
if ( text->selection_start_ip == text->ip2 )
{
int new_ip;
if ( get_ip_one_line_below( text, text->ip1, &new_ip ) )
{
if ( ep->v.character.shift )
{
text->ip1 = new_ip;
if ( text->ip2 < text->ip1 )
{
int temp;
temp = text->ip1;
text->ip1 = text->ip2;
text->ip2 = temp;
text->selection_start_ip = text->ip1;
}
}
else
text->selection_start_ip = text->ip1 = text->ip2 = new_ip;
if ( redisplay )
display_if_necessary( text, TRUE );
}
}
else
{
int new_ip;
if ( get_ip_one_line_below( text, text->ip2, &new_ip ) )
{
if ( ep->v.character.shift )
text->ip2 = new_ip;
else
text->selection_start_ip = text->ip1 = text->ip2 = new_ip;
if ( redisplay )
display_if_necessary( text, TRUE );
} else if ( text->ip1 != text->ip2 && !ep->v.character.shift )
{
text->selection_start_ip = text->ip1 = text->ip2;
if ( redisplay )
display_if_necessary( text, TRUE );
}
}
return TRUE;
}
static void
key_page_down( XI_TEXT * text, XinEvent * ep )
{
int cnt;
if ( !text->multi_line )
return;
for ( cnt = 0; cnt < text->max_lines_to_draw; ++cnt )
key_down( text, ep, FALSE );
display_if_necessary( text, TRUE );
}
static void
key_line_home( XI_TEXT * text, XinEvent * ep )
{
if ( text->multi_line )
{
if ( text->ip1 == text->ip2 )
{
int home_ip,
line;
line = get_line_from_ip( text, text->ip1 );
home_ip = text->line_breaks[line].line_break;
if ( ep->v.character.shift )
{
text->ip1 = home_ip;
text->selection_start_ip = text->ip2;
}
else
text->selection_start_ip = text->ip1 = text->ip2 = home_ip;
}
else
{
if ( text->selection_start_ip == text->ip1 )
{
int home_ip,
line;
line = get_line_from_ip( text, text->ip2 );
home_ip = text->line_breaks[line].line_break;
if ( ep->v.character.shift )
{
text->ip2 = home_ip;
if ( text->ip2 < text->ip1 )
{
int temp;
temp = text->ip1;
text->ip1 = text->ip2;
text->ip2 = temp;
text->selection_start_ip = text->ip1;
}
}
else
text->selection_start_ip = text->ip1 = text->ip2 = home_ip;
}
else
{
int home_ip,
line;
line = get_line_from_ip( text, text->ip1 );
home_ip = text->line_breaks[line].line_break;
if ( ep->v.character.shift )
text->ip1 = home_ip;
else
text->selection_start_ip = text->ip1 = text->ip2 = home_ip;
}
}
}
else
{
if ( text->ip1 == text->ip2 )
{
if ( ep->v.character.shift )
{
text->ip1 = 0;
text->selection_start_ip = text->ip2;
}
else
text->selection_start_ip = text->ip1 = text->ip2 = 0;
}
else
{
if ( text->selection_start_ip == text->ip1 )
{
if ( ep->v.character.shift )
{
text->ip2 = 0;
if ( text->ip2 < text->ip1 )
{
int temp;
temp = text->ip1;
text->ip1 = text->ip2;
text->ip2 = temp;
text->selection_start_ip = text->ip1;
}
}
else
text->selection_start_ip = text->ip1 = text->ip2 = 0;
}
else
{
if ( ep->v.character.shift )
text->ip1 = 0;
else
text->selection_start_ip = text->ip1 = text->ip2 = 0;
}
}
}
display_if_necessary( text, TRUE );
}
static void
key_line_end( XI_TEXT * text, XinEvent * ep )
{
if ( text->multi_line )
{
if ( text->ip1 == text->ip2 )
{
int end_ip,
line,
len;
line = get_line_from_ip( text, text->ip1 );
get_line( text, line, &len );
end_ip = text->line_breaks[line].line_break + len - 1;
if ( line == text->nbr_lines - 1 )
++end_ip;
if ( ep->v.character.shift )
{
text->ip2 = end_ip;
text->selection_start_ip = text->ip1;
}
else
text->selection_start_ip = text->ip1 = text->ip2 = end_ip;
}
else
{
if ( text->selection_start_ip == text->ip1 )
{
int end_ip,
line,
len;
line = get_line_from_ip( text, text->ip2 );
get_line( text, line, &len );
end_ip = text->line_breaks[line].line_break + len - 1;
if ( line == text->nbr_lines - 1 )
++end_ip;
if ( ep->v.character.shift )
text->ip2 = end_ip;
else
text->selection_start_ip = text->ip1 = text->ip2 = end_ip;
}
else
{
int end_ip,
line,
len;
line = get_line_from_ip( text, text->ip2 );
get_line( text, line, &len );
if ( line == text->nbr_lines - 1 )
end_ip = text->line_breaks[line].line_break + len;
else
end_ip = text->line_breaks[line].line_break + len - 1;
if ( ep->v.character.shift )
{
text->ip1 = end_ip;
if ( text->ip2 < text->ip1 )
{
int temp;
temp = text->ip1;
text->ip1 = text->ip2;
text->ip2 = temp;
text->selection_start_ip = text->ip1;
}
}
else
text->selection_start_ip = text->ip1 = text->ip2 = end_ip;
}
}
}
else
{
if ( text->ip1 == text->ip2 )
{
int end_ip;
end_ip = strlen( text->string );
if ( ep->v.character.shift )
{
text->ip2 = end_ip;
text->selection_start_ip = text->ip1;
}
else
text->selection_start_ip = text->ip1 = text->ip2 = end_ip;
}
else
{
if ( text->selection_start_ip == text->ip1 )
{
int end_ip;
end_ip = strlen( text->string );
if ( ep->v.character.shift )
text->ip2 = end_ip;
else
text->selection_start_ip = text->ip1 = text->ip2 = end_ip;
}
else
{
int end_ip;
end_ip = strlen( text->string );
if ( ep->v.character.shift )
{
text->ip1 = end_ip;
if ( text->ip2 < text->ip1 )
{
int temp;
temp = text->ip1;
text->ip1 = text->ip2;
text->ip2 = temp;
text->selection_start_ip = text->ip1;
}
}
else
text->selection_start_ip = text->ip1 = text->ip2 = end_ip;
}
}
}
display_if_necessary( text, TRUE );
}
static int
get_ip_one_word_left( XI_TEXT * text, int ip )
{
int line;
if ( text->multi_line )
{
line = get_line_from_ip( text, ip );
if ( ip == text->line_breaks[line].line_break )
{
int len;
if ( line == 0 )
return ip;
--line;
get_line( text, line, &len );
return text->line_breaks[line].line_break + len - 1;
}
}
if ( !text->multi_line )
{
if ( ip == 0 )
return ip;
}
/* move back one character, to get away from the beginning of a word */
--ip;
/* find the first character that is not white space */
while ( TRUE )
{
if ( text->multi_line )
{
if ( ip == text->line_breaks[line].line_break )
return ip;
}
else
{
if ( ip == 0 )
return ip;
}
if ( is_word_char( text->string[ip] ) )
{
/* find the first character that is white space */
while ( TRUE )
{
if ( text->multi_line )
{
if ( ip == text->line_breaks[line].line_break )
return ip;
}
else
{
if ( ip == 0 )
return ip;
}
if ( !is_word_char( text->string[ip] ) )
return ip;
--ip;
}
}
--ip;
}
}
static void
key_word_left( XI_TEXT * text, XinEvent * ep )
{
if ( text->ip1 == text->ip2 )
{
int left_ip;
left_ip = get_ip_one_word_left( text, text->ip1 );
if ( ep->v.character.shift )
{
text->ip1 = left_ip;
text->selection_start_ip = text->ip2;
}
else
text->selection_start_ip = text->ip1 = text->ip2 = left_ip;
}
else
{
if ( text->selection_start_ip == text->ip1 )
{
int left_ip;
left_ip = get_ip_one_word_left( text, text->ip2 );
if ( ep->v.character.shift )
{
text->ip2 = left_ip;
if ( text->ip2 < text->ip1 )
{
int temp;
temp = text->ip1;
text->ip1 = text->ip2;
text->ip2 = temp;
text->selection_start_ip = text->ip2;
}
}
else
text->selection_start_ip = text->ip1 = text->ip2 = left_ip;
}
else
{
int left_ip;
left_ip = get_ip_one_word_left( text, text->ip1 );
if ( ep->v.character.shift )
text->ip1 = left_ip;
else
text->selection_start_ip = text->ip1 = text->ip2 = left_ip;
}
}
display_if_necessary( text, TRUE );
}
static int
get_ip_one_word_right( XI_TEXT * text, int ip )
{
int line,
len,
string_len;
if ( text->multi_line )
{
line = get_line_from_ip( text, ip );
get_line( text, line, &len );
if ( ip == text->line_breaks[line].line_break + len )
{
if ( line == text->nbr_lines - 1 )
return ip;
++line;
return text->line_breaks[line].line_break;
}
}
string_len = strlen( text->string );
while ( TRUE )
{
if ( text->multi_line )
{
if ( ip == text->line_breaks[line].line_break + len )
return ip;
}
else
{
if ( ip == string_len )
return ip;
}
if ( is_word_char( text->string[ip] ) )
{
while ( TRUE )
{
if ( text->multi_line )
{
if ( ip == text->line_breaks[line].line_break + len )
return ip;
}
else
{
if ( ip == string_len )
return ip;
}
if ( !is_word_char( text->string[ip] ) )
{
while ( TRUE )
{
if ( text->multi_line )
{
if ( ip == text->line_breaks[line].line_break + len )
return ip;
}
else
{
if ( ip == string_len )
return ip;
}
if ( is_word_char( text->string[ip] ) )
return ip;
++ip;
}
}
++ip;
}
}
++ip;
}
}
static void
key_word_right( XI_TEXT * text, XinEvent * ep )
{
if ( text->ip1 == text->ip2 )
{
int right_ip;
right_ip = get_ip_one_word_right( text, text->ip1 );
if ( ep->v.character.shift )
{
text->ip2 = right_ip;
text->selection_start_ip = text->ip1;
}
else
text->selection_start_ip = text->ip1 = text->ip2 = right_ip;
}
else
{
if ( text->selection_start_ip == text->ip2 )
{
int right_ip;
right_ip = get_ip_one_word_right( text, text->ip1 );
if ( ep->v.character.shift )
{
text->ip1 = right_ip;
if ( text->ip2 < text->ip1 )
{
int temp;
temp = text->ip1;
text->ip1 = text->ip2;
text->ip2 = temp;
text->selection_start_ip = text->ip1;
}
}
else
text->selection_start_ip = text->ip1 = text->ip2 = right_ip;
}
else
{
int right_ip;
right_ip = get_ip_one_word_right( text, text->ip2 );
if ( ep->v.character.shift )
text->ip2 = right_ip;
else
text->selection_start_ip = text->ip1 = text->ip2 = right_ip;
}
}
display_if_necessary( text, TRUE );
}
static void
delete_selection( XI_TEXT * text, BOOLEAN redisplay )
{
memmove( &text->string[text->ip1],
&text->string[text->ip2],
strlen( text->string ) - text->ip2 + 1 );
text->selection_start_ip = text->ip2 = text->ip1;
if ( redisplay )
display_if_necessary( text, TRUE );
}
static void
key_backspace( XI_TEXT * text, BOOLEAN * changed )
{
if ( text->ip1 == text->ip2 )
{
if ( text->ip1 >= 1 )
{
memmove( &text->string[text->ip1 - 1],
&text->string[text->ip1],
strlen( text->string ) - text->ip1 + 1 );
text->selection_start_ip = text->ip2 = --text->ip1;
display_if_necessary( text, TRUE );
if ( changed )
*changed = TRUE;
}
else
{
if ( changed )
*changed = FALSE;
}
}
else
{
delete_selection( text, TRUE );
if ( changed )
*changed = TRUE;
}
}
static void
key_delete( XI_TEXT * text, BOOLEAN * changed )
{
if ( text->ip1 == text->ip2 )
{
int len;
len = strlen( text->string );
if ( text->ip2 <= len - 1 )
{
memmove( &text->string[text->ip2],
&text->string[text->ip2 + 1],
len - text->ip2 + 1 );
display_if_necessary( text, TRUE );
if ( changed )
*changed = TRUE;
}
else
{
if ( changed )
*changed = FALSE;
}
}
else
{
delete_selection( text, TRUE );
if ( changed )
*changed = TRUE;
}
}
static BOOLEAN
key_character( XI_TEXT * text, XinEvent * ep, BOOLEAN * changed )
{
int len;
int ch;
if ( changed )
*changed = FALSE;
if ( ep->v.character.ch > 255 )
return FALSE;
if ( ep->v.character.ch == '\r' || ep->v.character.ch == '\n' )
{
if ( !text->cr_ok || text->read_only )
return FALSE;
}
else if ( ep->v.character.ch < 128 && !isprint( ep->v.character.ch ) )
return FALSE;
ch = ep->v.character.ch;
if ( ch == '\r' )
ch = '\n';
len = strlen( text->string );
if ( text->ip1 != text->ip2 )
{
delete_selection( text, FALSE );
if ( changed )
*changed = TRUE;
}
len = strlen( text->string );
if ( text->var_len_text || ( ( int ) len < text->buffer_size - 1 ) )
{
if (text->var_len_text)
reallocate_text( text, len + 2, FALSE );
memmove( &text->string[text->ip1 + 1],
&text->string[text->ip1],
len - text->ip1 + 1 );
text->string[text->ip1] = ch;
text->selection_start_ip = text->ip1 = ++text->ip2;
if ( changed )
*changed = TRUE;
if ( len + 2 > text->buffer_size )
text->buffer_size = len + 2;
}
display_if_necessary( text, TRUE );
return TRUE;
}
/*
this function is changes delta_y when the user operates the scroll bar on a
multi-line edit control.
this function is only used for multi_line edit controls.
*/
static void
move_delta( XI_TEXT * text, int nbr_lines )
{
if ( nbr_lines < 0 )
{
nbr_lines = -nbr_lines;
if ( text->delta_y >= nbr_lines )
text->delta_y -= nbr_lines;
else
text->delta_y = 0;
}
else
{
if ( text->delta_y + nbr_lines < text->nbr_lines - text->max_lines_to_draw )
text->delta_y += nbr_lines;
else
{
text->delta_y = text->nbr_lines - text->max_lines_to_draw;
if ( text->delta_y < 0 )
text->delta_y = 0;
}
}
display_if_necessary( text, FALSE );
}
/*
this function is changes delta_y when the user operates the scroll bar on a
multi-line edit control.
this function is only used for multi_line edit controls.
*/
static void
set_pos_delta( XI_TEXT * text, int pos, int prop )
{
int new_pos;
new_pos = ( int ) ( ( ( double ) text->nbr_lines - ( double ) text->max_lines_to_draw ) *
( double ) pos / ( double ) ( 1000 - prop ) );
if ( new_pos < 0 )
new_pos = 0;
text->delta_y = new_pos;
display_if_necessary( text, FALSE );
}
static int
xi_text_point_hit_test( XinFont * font, char *str, int hpos, int last_ip, int del_x )
{
int left_point,
right_point;
int x1,
x2,
x3;
if ( hpos == 0 )
{
left_point = 0;
x1 = XinFontTextWidthGet( font, str, 1 );
right_point = x1 / 2;
}
else
{
x1 = XinFontTextWidthGet( font, str, hpos - 1 );
x2 = XinFontTextWidthGet( font, &str[hpos - 1], 1 );
left_point = x1 + x2 / 2 + 1;
if ( hpos == last_ip )
right_point = x1 + x2;
else
{
x3 = XinFontTextWidthGet( font, &str[hpos], 1 );
right_point = x1 + x2 + x3 / 2;
}
}
if ( del_x < left_point )
return -1;
else if ( del_x > right_point )
return 1;
else
return 0;
}
/*
this function returns an insertion point, given an XinEvent.
this function works by doing a binary search.
*/
static void
xi_text_hit_test( XI_TEXT * text, XinEvent * ep, int *ip )
{
int del_y,
del_x,
line,
len,
cnt,
last_ip;
int hpos,
d,
delta_x_pix,
max_str_len;
char *str;
if ( text->multi_line )
{
if ( ep->v.mouse.where.v < text->prect.top )
{
int lines_above;
lines_above = ( text->prect.top - ep->v.mouse.where.v ) / text->font_height + 1;
line = text->delta_y - lines_above;
if ( line < 0 )
line = 0;
}
else
{
del_y = ep->v.mouse.where.v - text->prect.top;
line = del_y / text->font_height + text->delta_y;
if ( line >= text->nbr_lines )
line = text->nbr_lines - 1;
}
str = get_line( text, line, &len );
delta_x_pix = XinFontTextWidthGet( text->font, str, text->delta_x );
del_x = ep->v.mouse.where.h - text->prect.left + delta_x_pix;
}
else
{
len = strlen( text->string );
if ( text->password )
str = alloc_password_string( len, text );
else
str = text->string;
if ( text->right_justify )
{
/* calculate del_x. del_x is the distance in pixels from the virtual
* beginning of the right justified text to the horizontal position of
* the mouse. the code below works just fine if del_x is negative: ie if
* the mouse is to the left of the right justified text. */
int str_len_in_pix;
delta_x_pix = XinFontTextWidthGet( text->font,
&str[len - text->delta_x], text->delta_x );
str_len_in_pix = XinFontTextWidthGet( text->font, str, -1 );
del_x = ep->v.mouse.where.h - ( text->prect.right - str_len_in_pix +
delta_x_pix );
}
else
{
/* calculate del_x. del_x is the distance in pixels from the virtual
* beginning of the left justified text to the horizontal position of the
* mouse. the code below works just fine if del_x is negative: ie if the
* mouse is to the left of the text. */
delta_x_pix = XinFontTextWidthGet( text->font, str, text->delta_x );
del_x = ep->v.mouse.where.h - text->prect.left + delta_x_pix;
}
}
/* binary search - find cnt where the mathmatical line between characters is
* closest to pix_len.
*
* this binary search uses a deterministic algorythm. by this, when starting
* searching, hpos is always a power of 2. d is the delta that is added to
* or subtracted from hpos after each comparison. d is initialized as hpos /
* 2. therefore, d is *always* a power of 2. */
/* last_ip = len + 1, because there is one more possible insertion point than
* characters */
last_ip = len + 1;
hpos = 1;
while ( hpos <= last_ip )
hpos = hpos << 1;
d = hpos / 2;
--hpos;
if ( del_x < 0 )
del_x = 0;
max_str_len = XinFontTextWidthGet( text->font, str, len );
if ( del_x > max_str_len )
del_x = max_str_len;
while ( TRUE )
{
int result;
if ( hpos > len )
{
if ( d == 0 )
{
cnt = len;
break;
}
hpos -= d;
d /= 2;
continue;
}
result = xi_text_point_hit_test( text->font, str, hpos, last_ip, del_x );
if ( result == 0 )
{
cnt = hpos;
break;
}
if ( result < 0 )
{
hpos -= d;
d /= 2;
}
else
{
hpos += d;
d /= 2;
}
}
if ( text->multi_line )
*ip = text->line_breaks[line].line_break + cnt;
else
*ip = cnt;
if ( text->password )
xi_tree_free( str );
}
/*
only used if multi_line, and if there is a scroll bar.
*/
void
xi_text_control_event( XI_TEXT * text, XinEvent * ep )
{
save_text_state( text );
switch ( ep->v.control.ctrl_info.v.scroll.action )
{
case XinScrollBarActionLineUp:
move_delta( text, -1 );
break;
case XinScrollBarActionLineDown:
move_delta( text, 1 );
break;
case XinScrollBarActionPageUp:
move_delta( text, -text->max_lines_to_draw );
break;
case XinScrollBarActionPageDown:
move_delta( text, text->max_lines_to_draw );
break;
case XinScrollBarActionThumb:
case XinScrollBarActionThumbTrack:
{
int percent = ep->v.control.ctrl_info.v.scroll.position;
int prop = XinScrollBarProportionGet( text->sb_win, XinScrollBarTypeEither );
set_pos_delta( text, percent, prop );
break;
}
}
if ( save_text )
free_save_text_state( );
}
/*
xi_text_character_event, xi_text_mouse_down_event, xi_text_mouse_double_event,
xi_text_mouse_move_event, and xi_text_mouse_up_event don't care whether the
xi_text is single or multi_line, or if it is right justified.
*/
BOOLEAN
xi_text_character_event( XI_TEXT * text, XinEvent * ep, BOOLEAN * changed )
{
BOOLEAN retval;
save_text_state( text );
switch ( ep->v.character.ch )
{
case XI_KEY_LEFT:
key_left( text, ep );
retval = TRUE;
break;
case XI_KEY_RIGHT:
key_right( text, ep );
retval = TRUE;
break;
case XI_KEY_UP:
retval = key_up( text, ep, TRUE );
break;
case XI_KEY_DOWN:
retval = key_down( text, ep, TRUE );
break;
case XI_KEY_PREV:
key_page_up( text, ep );
retval = TRUE;
break;
case XI_KEY_NEXT:
key_page_down( text, ep );
retval = TRUE;
break;
case XI_KEY_LHOME:
case XI_KEY_HOME:
key_line_home( text, ep );
retval = TRUE;
break;
case XI_KEY_LEND:
case XI_KEY_END:
key_line_end( text, ep );
retval = TRUE;
break;
case XI_KEY_WLEFT:
key_word_left( text, ep );
retval = TRUE;
break;
case XI_KEY_WRIGHT:
key_word_right( text, ep );
retval = TRUE;
break;
case '\b':
if ( !text->read_only )
key_backspace( text, changed );
retval = TRUE;
break;
case XI_KEY_DEL:
if ( !text->read_only )
key_delete( text, changed );
retval = TRUE;
break;
default:
if ( text->read_only )
{
if ( ep->v.character.ch == '\033' ||
ep->v.character.ch == '\r' ||
ep->v.character.ch == '\n' )
{
if ( save_text )
free_save_text_state( );
return FALSE;
}
if ( (ep->v.character.ch != '\r' && ep->v.character.ch != '\n')
|| !text->itf->v.itf->tab_on_enter )
{
if ( save_text )
free_save_text_state( );
return TRUE;
}
}
retval = key_character( text, ep, changed );
break;
}
if ( save_text )
free_save_text_state( );
return retval;
}
void
xi_text_mouse_down_event( XI_TEXT * text, XinEvent * ep, BOOLEAN gaining_focus )
{
int ip;
if ( text->timer_set )
{
XinWindowTimerKill( text->win, text->timer_id );
text->timer_set = FALSE;
text->timer_id = 0;
xi_text_selection_set( text, 0, 32000 );
return;
}
if ( gaining_focus && ( text->parent_obj == 0 ||
( xi_get_attrib( text->parent_obj ) & XI_ATR_AUTOSELECT ) ) &&
xi_get_pref( XI_PREF_AUTOSEL_ON_MOUSE ) )
{
xi_text_selection_set( text, 0, INT_MAX );
return;
}
xi_text_hit_test( text, ep, &ip );
if ( ep->v.mouse.shift )
{
if ( text->selection_start_ip == text->ip1 )
xi_text_selection_set_internal( text, text->ip1, ip, text->ip1, TRUE, FALSE );
else
xi_text_selection_set_internal( text, ip, text->ip2, text->ip2, TRUE, FALSE );
}
else
xi_text_selection_set_internal( text, ip, ip, ip, TRUE, FALSE );
XinWindowMouseTrap( text->win, TRUE );
text->selecting = TRUE;
}
void
xi_text_mouse_double_event( XI_TEXT * text, XinEvent * ep )
{
int ip,
right_ip,
left_ip;
long time;
xi_text_hit_test( text, ep, &ip );
right_ip = get_ip_one_word_right( text, ip );
if ( text->string[right_ip - 1] == ' ' )
--right_ip;
left_ip = get_ip_one_word_left( text, ip );
if ( text->string[left_ip] == ' ' )
++left_ip;
xi_text_selection_set_internal( text, left_ip, right_ip, left_ip, TRUE, FALSE );
XinWindowMouseTrap( text->win, TRUE );
text->double_selecting = TRUE;
text->double_selecting_start_ip = ip;
time = xi_get_pref( XI_PREF_TRIPLE_CLICK_TIME );
if ( time )
{
text->timer_id = XinWindowTimerSet( text->win, time );
text->timer_set = TRUE;
}
}
void
xi_text_mouse_move_event( XI_TEXT * text, XinEvent * ep )
{
int ip;
if ( text->selecting )
{
xi_text_hit_test( text, ep, &ip );
if ( ip < text->selection_start_ip )
xi_text_selection_set_internal( text, ip, text->selection_start_ip,
text->selection_start_ip, TRUE, FALSE );
else
xi_text_selection_set_internal( text, text->selection_start_ip, ip,
text->selection_start_ip, TRUE, FALSE );
}
if ( text->double_selecting )
{
xi_text_hit_test( text, ep, &ip );
if ( ip < text->double_selecting_start_ip )
{
int left_ip,
right_ip;
left_ip = get_ip_one_word_left( text, ip );
if ( text->string[left_ip] == ' ' )
++left_ip;
right_ip = get_ip_one_word_right( text, text->double_selecting_start_ip );
if ( text->string[right_ip - 1] == ' ' )
--right_ip;
xi_text_selection_set_internal( text, left_ip, right_ip,
right_ip, TRUE, FALSE );
}
else
{
int left_ip,
right_ip;
right_ip = get_ip_one_word_right( text, ip );
if ( text->string[right_ip - 1] == ' ' )
--right_ip;
left_ip = get_ip_one_word_left( text, text->double_selecting_start_ip );
if ( text->string[left_ip] == ' ' )
++left_ip;
xi_text_selection_set_internal( text, left_ip, right_ip,
left_ip, TRUE, FALSE );
}
}
}
void
xi_text_mouse_up_event( XI_TEXT * text, XinEvent * ep )
{
int ip;
if ( text->selecting )
{
xi_text_hit_test( text, ep, &ip );
if ( ip < text->selection_start_ip )
xi_text_selection_set_internal( text, ip, text->selection_start_ip,
text->selection_start_ip, TRUE, FALSE );
else
xi_text_selection_set_internal( text, text->selection_start_ip, ip,
text->selection_start_ip, TRUE, FALSE );
XinWindowMouseRelease( );
text->selecting = FALSE;
}
if ( text->double_selecting )
{
XinWindowMouseRelease( );
text->double_selecting = FALSE;
}
}
void
xi_text_timer_event( XI_TEXT * text, XinEvent * ep )
{
if ( ep->v.timer.id == text->timer_id )
{
XinWindowTimerKill( text->win, ep->v.timer.id );
text->timer_id = 0;
text->timer_set = FALSE;
}
}
/*
all events are passed to this function.
*/
BOOLEAN
xi_text_event( XI_TEXT * text, XinEvent * ep, BOOLEAN gaining_focus, BOOLEAN * changed )
{
BOOLEAN retval = FALSE;
if ( changed )
*changed = FALSE;
text = text;
if ( !text )
return retval;
switch ( ep->type )
{
case XinEventControl:
xi_text_control_event( text, ep );
break;
case XinEventCharacter:
retval = xi_text_character_event( text, ep, changed );
break;
case XinEventPaint:
xi_text_draw( text, 0L, 0L, TRUE );
break;
case XinEventMouseDown:
xi_text_mouse_down_event( text, ep, gaining_focus );
break;
case XinEventMouseDouble:
xi_text_mouse_double_event( text, ep );
break;
case XinEventMouseMove:
xi_text_mouse_move_event( text, ep );
break;
case XinEventMouseUp:
xi_text_mouse_up_event( text, ep );
break;
case XinEventTimer:
xi_text_timer_event( text, ep );
break;
}
return retval;
}
/*
this stops editing for an xi_text.
*/
void
xi_text_editing_stop( XI_TEXT * text )
{
if ( text->editing == FALSE )
return;
xi_text_caret_off( text );
xi_text_selection_set_internal( text, 0, 0, 0, FALSE, TRUE );
text->editing = FALSE;
if ( text->sb_win && !text->scrollbar_always_visible )
{
if ( !xi_itf_in_event_destroy( text->itf ) )
XinWindowDestroy( text->sb_win );
text->sb_win = XI_NULL_WINDOW;
}
if ( save_text )
free_save_text_state( );
}
/*
this function sets the physical rectangle for an xi_text.
*/
void
xi_text_prect_set( XI_TEXT * text, XinRect * rctp )
{
int pix_width;
BOOLEAN changed = FALSE;
if ( text->prect.top != rctp->top || text->prect.bottom != rctp->bottom ||
text->prect.left != rctp->left || text->prect.right != rctp->right )
changed = TRUE;
text->prect = *( rctp );
if ( text->font_height == 0 )
{
XinFont *font;
int char_width;
font = text->font;
xi_get_font_metrics_font( font, &text->leading, &text->ascent, &text->descent, &char_width );
text->font_height = text->ascent + text->descent + text->leading;
}
text->max_lines_to_draw = ( rctp->bottom - rctp->top ) / text->font_height;
if ( text->max_lines_to_draw < 1 )
text->max_lines_to_draw = 1;
pix_width = rctp->right - rctp->left;
text->pix_width = pix_width;
if ( text->scrollbar )
{
if ( !text->sb_width )
text->sb_width = ( int ) XinMetricGet( XinMetricVerticalScrollBarWidth );
text->internal_pix_width = text->pix_width - text->sb_width - SB_DELTA;
if ( changed && ( text->scrollbar_always_visible || text->sb_win ) )
{
XinWindowDef Def;
XinRect rct;
MEMCLEAR( Def );
Def.control_id = text->cid;
Def.type = XinWindowTypeVerticalScrollBar;
rct.top = text->prect.top;
rct.bottom = text->prect.bottom;
rct.right = text->prect.left + text->internal_pix_width + SB_DELTA + text->sb_width;
rct.left = text->prect.left + text->internal_pix_width + SB_DELTA;
Def.p_rect = &rct;
Def.title = "";
Def.visible = text->visible;
Def.enabled = TRUE;
Def.parent = text->win;
if ( text->sb_win != XI_NULL_WINDOW )
XinWindowDestroy( text->sb_win );
text->sb_win = XinWindowCreate( &Def );
}
}
else
text->internal_pix_width = text->pix_width;
}
XI_TEXT *
xi_text_focus_get( XinWindow win )
{
XI_OBJ *itf,
*focus_obj;
itf = xi_get_itf( win );
focus_obj = xi_get_focus( itf );
if ( focus_obj )
{
switch ( focus_obj->type )
{
case XIT_CELL:
return lm_xi_text_focus_get( focus_obj->parent->v.list->lm );
case XIT_FIELD:
return stx_xi_text_get( focus_obj->v.field->stx );
default:
return NULL;
}
}
return NULL;
}
void
xi_text_buffer_size_set( XI_TEXT * text, int len )
{
int ip1, ip2, sip;
BOOLEAN reset_ips = FALSE;
if ( xi_text_editing_is( text ) )
{
ip1 = text->ip1;
ip2 = text->ip2;
sip = text->selection_start_ip;
reset_ips = TRUE;
}
if ( len < text->buffer_size )
text->initialized = FALSE;
text->buffer_size = len;
reallocate_text( text, len, TRUE );
xi_text_wrap( text );
if ( reset_ips )
{
ip1 = min( ip1, len - 1 );
ip2 = min( ip2, len - 1 );
sip = min( sip, len - 1 );
xi_text_selection_set_internal( text, ip1, ip2, sip, TRUE, TRUE );
}
}
void
xi_text_font_set( XI_TEXT * text, XinFont * font )
{
int char_width;
BOOLEAN restart = FALSE;
if ( xi_text_editing_is( text ) )
{
xi_text_editing_stop( text );
restart = TRUE;
}
text->font = font;
xi_get_font_metrics_font( font, &text->leading, &text->ascent, &text->descent, &char_width );
text->font_height = text->ascent + text->descent + text->leading;
text->max_lines_to_draw = ( text->prect.bottom - text->prect.top ) / text->font_height;
if ( text->max_lines_to_draw < 1 )
text->max_lines_to_draw = 1;
if ( text->multi_line )
xi_text_wrap_internal( text );
if ( text->sb_win )
xi_text_sb_set( text );
if ( restart )
xi_text_editing_start( text );
}
void
xi_text_visible_set( XI_TEXT * text, BOOLEAN visible )
{
text->visible = visible;
if ( text->sb_win )
{
XinWindowShow( text->sb_win, visible );
}
}
XinRect *
xi_text_rect_get_adjusted( XI_TEXT * text, XinRect * rcta )
{
if ( text->scrollbar )
{
if ( !text->sb_width )
text->sb_width = ( int ) XinMetricGet( XinMetricVerticalScrollBarWidth );
rcta->right = rcta->right - text->sb_width - SB_DELTA;
}
return ( rcta );
}
void
xi_text_paste_internal( XI_TEXT * text, char *string )
{
int len, len2;
int cnt,
ip;
char *str;
if ( text->read_only )
return;
if ( text->ip1 != text->ip2 )
delete_selection( text, TRUE );
len = strlen( text->string );
len2 = strlen( string );
str = xi_tree_malloc( len + len2 + 1, NULL );
/* New string in the middle */
if ( text->ip1 > 0 && text->ip1 < len )
{
gstrncpy( str, text->string, text->ip1 );
strcat( str, string );
ip = strlen( str );
for ( cnt = text->ip1; cnt < len; ++cnt )
{
str[ip] = text->string[cnt];
++ip;
}
}
/* there is nothing in the field, so simply use the string passed */
else if ( len <= 0 )
strcpy( str, string );
else
/* New string at the end */
if ( text->ip1 >= len )
{
strcpy( str, text->string );
strcat( str, string );
}
/* New string at beginning */
else
{
strcpy( str, string );
strcat( str, text->string );
}
len = reallocate_text( text, strlen( str ) + 1, FALSE );
memcpy( text->string, str, len );
xi_tree_free( str );
text->string[ len - 1 ] = '\0';
if ( text->ip2 > len - 1 )
text->ip2 = len - 1;
if ( !text->editing )
return;
XinWindowFontMap( text->win, text->font );
/*
xi_text_caret_off( text );
if ( text->multi_line )
{
xi_text_wrap_internal( text );
recalc_line_ips( text );
calc_delta_y( text );
}
else
calc_delta_x( text );
if ( text->sb_win )
xi_text_sb_set( text );
xi_text_draw_internal( text, 0L, 0L, FALSE );
xi_text_caret_on( text );
*/
display_if_necessary( text, TRUE );
}
void
xi_text_right_justify_set( XI_TEXT* text, BOOLEAN flag )
{
text->right_justify = flag;
if ( text->multi_line )
text->right_justify = FALSE;
}
/* }]) */