Files correlati : Ricompilazione Demo : [ ] Commento : Riportata la versione 22.1 patch 246 git-svn-id: svn://10.65.10.50/trunk@12818 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			3391 lines
		
	
	
		
			90 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			3391 lines
		
	
	
		
			90 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"
 | |
| #include <limits.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 = NULL;
 | |
|   int cnt,
 | |
|   nbr_lines = 0,
 | |
|   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 = NULL;
 | |
|     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 = 0;
 | |
|       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 = 0;
 | |
| 
 | |
|     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
 | |
|         ep->v.character.ch = XI_KEY_BTAB; // AGA was here
 | |
|     }
 | |
|   }
 | |
|   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;
 | |
|       else
 | |
|         ep->v.character.ch = XI_KEY_BTAB; // AGA was here
 | |
|       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
 | |
|         ep->v.character.ch = '\t';       // AGA was here
 | |
|     }
 | |
|   }
 | |
|   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;
 | |
|       else
 | |
|         ep->v.character.ch = '\t'; // AGA was here
 | |
| 
 | |
|       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 = 0;
 | |
| 
 | |
|   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 = 0,
 | |
|   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 = 0,
 | |
|   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;
 | |
|       }
 | |
|     default:
 | |
|       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;
 | |
|     case XI_KEY_F3:
 | |
|     case XI_KEY_F4:
 | |
|       retval = key_character( text, ep, changed );  // Added by AGA
 | |
|       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;
 | |
|     default:
 | |
|       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 = 0, ip2 = 0, sip = 0;
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /* }]) */
 |