/******************************************************************************* * 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 "xidisply.h" /*------------------------------------------------------------------------- Metrics Documentation lmp->rct.top and lmp->rct.bottom are the top and bottom of the list, excluding the horizontal scroll bar. lmp->rct.left is the physical left position of the list lmp->rct.right is the position of the physical right position of the list. However, if the list is a horizontal scrolling list, then lmp->rct.right will be beyond lmp->width. It is useful for drawing using the virtual functions, but it is necessary to subtract lmp->rct.left before calling the drawing functions, because the drawing functions will add lmp->rct.left to the h values before drawing. -------------------------------------------------------------------------*/ /*------------------------------------------------------------------------- ROWS: REALIZED, and ALLOCATED-USED, and ALLOCATED-NOT USED. In the realized rows array, there are three types of rows. First, there are rows that are not being used at all. These are the rows after lmp->nbr_realized_rows. The record handles for these rows are 0L. Second, there are rows that are allocated, and used. These are the rows after 0, and before lmp->nbr_realized_rows. The record handles for these rows are not 0L. Third, there are rows that are allocated, and not used. These are the rows after 0, and less than lmp->nbr_realized_rows. The record handles for these rows are not 0L. If one of these rows is requested to be allocated, and the handle is not 0L, then then allocation event is not done. If one of these rows is requested to be freed, and the handle is 0L, then the free event is not done. -------------------------------------------------------------------------*/ /* Error codes: 20900 - 20918 */ #define LM_REDRAW_ATR (LM_ATR_VISIBLE | LM_ATR_ENABLED) #define LM_REQUESTING_CELL(lm, row, col) (LMP(lm)->in_cell_request && LMP(lm)->cell_request_row == (row) && LMP(lm)->cell_request_col == (col)) #define BUFLEN 256 /*------------------------------------------------------------------------- function: calc_last_vis lmp: current lmp process: calculates the last fully visible column in the list -------------------------------------------------------------------------*/ void lm_calc_last_vis( LM_DATA * lmp ) { LM_COLUMN_DATA *column_data; int l1; if ( !lmp->pixel_width ) lmp->last_vis = lmp->nbr_columns - 1; else { lmp->last_vis = lmp->first_vis; for ( l1 = lmp->first_vis + 1; l1 < lmp->nbr_columns; ++l1 ) { column_data = lmp->lm_column_data[l1]; if ( ( column_data->x_pix_pos - lmp->delta_x + column_data->pix_width ) >= lmp->pixel_width ) { lmp->last_vis = l1 - 1; return; } lmp->last_vis = l1; } if ( l1 == lmp->nbr_columns ) lmp->last_vis = l1 - 1; } } /*------------------------------------------------------------------------- function: lm_get_col_spacing returns: column spacing -------------------------------------------------------------------------*/ int lm_get_col_spacing( void ) { return ( 2 * ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ) + RULE_WIDTH_V ); } /*------------------------------------------------------------------------- function: lm_get_left_most_far_right_column lmp: current lmp nbr_columns: column, from which we will count back notes: This function is also used when the users presses page up on the horizontal scroll bar, to determine the new left most column. Get the column number for the column that would be visible if the list is scrolled entirely over to the right. This is necessary to compute the position of the horizontal thumb, and other reasons. -------------------------------------------------------------------------*/ int lm_get_left_most_far_right_col( LM_DATA * lmp, int nbr_columns ) { int width, i; #if XIWS == XIWS_WM width = lmp->pixel_width; #else width = lmp->pixel_width + ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ) + RULE_WIDTH_V; #endif for ( i = 0; i < min( lmp->fixed_columns, lmp->nbr_columns ); ++i ) width -= lmp->lm_column_data[i]->pix_width + lm_get_col_spacing( ); for ( i = nbr_columns - 1; i >= lmp->fixed_columns; --i ) { width -= lmp->lm_column_data[i]->pix_width + lm_get_col_spacing( ); if ( width <= 0 ) break; } return ( i + 1 ); } /*------------------------------------------------------------------------- function: lm_get_cell_rect rctp: pointer to rectangle to be filled in lm: current lm row: relevant row col: relevant column inner: if TRUE, get the inner rectangle of the cell, else get the outer rectangle of the cell physical_rct: if TRUE, get the physical rectangle, else get the virtual rectangle notes: This function is called before starting the text edit object for the cell. returns: rctp -------------------------------------------------------------------------*/ XinRect * lm_get_cell_rect( XinRect * rctp, LM lm, int row, int col, BOOLEAN inner, BOOLEAN physical_rct ) { LM_COLUMN_DATA *temp_lmcd; int col_offset; LM_DATA *lmp = LMP( lm ); col_offset = ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); temp_lmcd = lmp->lm_column_data[col]; rctp->top = lmp->pix_offsets[row]; #if XIWS != XIWS_WM rctp->bottom = rctp->top + lmp->pix_heights[row] - RULE_WIDTH_V; #else rctp->bottom = rctp->top + lmp->pix_heights[row]; #endif rctp->left = temp_lmcd->x_pix_pos; rctp->right = rctp->left + temp_lmcd->pix_width + 2 * ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); if ( inner ) { rctp->left += col_offset; rctp->right -= col_offset; #if XIWS != XIWS_WM rctp->top += RULE_Y_OFFSET_TOP; rctp->bottom -= RULE_Y_OFFSET_BOTTOM; #endif } if ( physical_rct ) { rctp->left += lmp->rct.left; rctp->right += lmp->rct.left; if ( rctp->left >= lmp->vir_left ) { rctp->left -= lmp->delta_x; rctp->right -= lmp->delta_x; } rctp->top = rctp->top + lmp->rrr_offset + lmp->mlr.top; rctp->bottom = rctp->bottom + lmp->rrr_offset + lmp->mlr.top; } return ( rctp ); } /*------------------------------------------------------------------------- function: get_row_rect rctp: pointer to rectangle to be filled in lm: current lm row: relevant row returns: rctp -------------------------------------------------------------------------*/ XinRect * lm_get_row_rect( XinRect * rctp, LM lm, int row ) { LM_DATA *lmp = LMP( lm ); rctp->top = lmp->pix_offsets[row]; rctp->bottom = rctp->top + lmp->pix_heights[row]; rctp->left = lmp->rct.left + BORDER_WIDTH; #if XIWS != XIWS_WM rctp->right = lmp->rct.right - BORDER_WIDTH; #else rctp->right = lmp->rct.right; #endif if ( lmp->pixel_width ) rctp->right = rctp->left + lmp->pixel_width; return ( rctp ); } /*------------------------------------------------------------------------- function: lm_adj_h lmp: current lmp h: pointer to horizontal coord to convert returns: TRUE if position is visible -------------------------------------------------------------------------*/ BOOLEAN lm_adj_h( LM_DATA * lmp, short *h ) { if ( *h >= lmp->vir_left ) { XinRect r; *h -= lmp->delta_x; if ( *h < lmp->vir_left ) return FALSE; r = lmp->rct; if ( lmp->pixel_width ) r.right = r.left + lmp->pixel_width + 2 * BORDER_WIDTH; return ( *h <= r.right ); } return TRUE; } /*------------------------------------------------------------------------- function: lm_get_list_rct lmp: current lmp r: pointer to rectangle to be filled in returns: r -------------------------------------------------------------------------*/ XinRect * lm_get_list_rct( LM_DATA * lmp, XinRect * r ) { *r = lmp->rct; if ( lmp->pixel_width ) r->right = r->left + lmp->pixel_width + 2 * BORDER_WIDTH; return r; } /*------------------------------------------------------------------------- function: lm_set_fixed_columns -------------------------------------------------------------------------*/ void lm_set_fixed_columns( LM lm, int new_fixed_count ) { LM_DATA *lmp = ( LM_DATA * ) lm; int col_spacing, i; XinRect rct_to_invalidate, rct; if ( new_fixed_count > lmp->nbr_columns ) new_fixed_count = lmp->nbr_columns; col_spacing = lm_get_col_spacing( ); lmp->fixed_columns = new_fixed_count; lmp->first_vis = lmp->fixed_columns; lmp->delta_x = 0; /* calculate the left boundary of the virtual space for the list */ lmp->vir_left = lmp->rct.left + BORDER_WIDTH; for ( i = 0; i < min( lmp->nbr_columns, lmp->fixed_columns ); ++i ) { lmp->vir_left += lmp->lm_column_data[i]->pix_width; lmp->vir_left += col_spacing; } lmp->list_obj->v.list->have_hsb_rct = FALSE; lm_get_list_rct( lmp, &rct_to_invalidate ); xi_invalidate_rect( lmp->win, &rct_to_invalidate ); lm_set_hscroll_range( ( LM ) lmp ); lm_set_hscroll_bar( ( LM ) lmp ); xi_get_hsb_rect( lmp->list_obj, &rct ); xi_offset_rect( &rct, -lmp->list_obj->itf->v.itf->delta_x, -lmp->list_obj->itf->v.itf->delta_y ); XinWindowRectSet( lmp->list_obj->v.list->hsb_win, &rct ); } /*------------------------------------------------------------------------- function: lm_cleanup lm: current lm notes: Destroys all row data and fonts -------------------------------------------------------------------------*/ void lm_cleanup( LM lm ) { LM_DATA *lmp = LMP( lm ); int col_nbr; for ( col_nbr = 0; col_nbr < lmp->nbr_columns; col_nbr++ ) { if ( lmp->lm_column_data[col_nbr]->font ) XinFontDestroy( lmp->lm_column_data[col_nbr]->font ); xi_bitmap_destroy( lmp->lm_column_data[col_nbr]->bitmap ); } lm_remove_all_rows( lmp, TRUE ); } /*------------------------------------------------------------------------- function: lm_delete lm: current lm notes: do not call this function in response to XinEventDestroy. -------------------------------------------------------------------------*/ void lm_delete( LM lm ) { XinRect r; LM_DATA *lmp = LMP( lm ); int focus_row, focus_column; BOOLEAN v_scrolled; if ( lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ) ) { lm_focus_remove( lmp, focus_row, focus_column, v_scrolled ); } lm_cleanup( lm ); lm_get_list_rct( lmp, &r ); xi_invalidate_rect( lmp->win, &r ); xi_tree_free( ( char * ) lm ); } /*------------------------------------------------------------------------- function: calc_x_pix_pos lm: current lm lcdata: column data pointer position: position of column -------------------------------------------------------------------------*/ void calc_x_pix_pos( LM lm, LM_COLUMN_DATA * lcdata, int position ) { LM_DATA *lmp = LMP( lm ); LM_COLUMN_DATA *lmcd; if ( position ) lmcd = lmp->lm_column_data[position - 1]; lcdata->x_pix_pos = ( !position ) ? BORDER_WIDTH : lmcd->x_pix_pos + lmcd->pix_width + lm_get_col_spacing( ); } /*------------------------------------------------------------------------- function: lm_invalidate_rect lmp: current lmp rctp: rectangle to invalidate adj_h: adjust the horizontal coords -------------------------------------------------------------------------*/ void lm_invalidate_rect( LM_DATA * lmp, XinRect * rctp, BOOLEAN adj_h ) { XinRect r; r = *rctp; if ( adj_h ) { lm_adj_h( lmp, &r.left ); lm_adj_h( lmp, &r.right ); } lm_adj_v( lmp, r.top ); lm_adj_v( lmp, r.bottom ); xi_invalidate_rect( lmp->win, &r ); } /*------------------------------------------------------------------------- function: lm_invalidate_rect2 lmp: current lmp rctp: rectangle to invalidate adj_left: if TRUE, adjust left of rctp -------------------------------------------------------------------------*/ void lm_invalidate_rect2( LM_DATA * lmp, XinRect * rctp, BOOLEAN adj_left ) { XinRect r; r = *rctp; if ( adj_left ) lm_adj_h( lmp, &r.left ); xi_invalidate_rect( lmp->win, &r ); } /*------------------------------------------------------------------------- function: lm_get_fixed_columns -------------------------------------------------------------------------*/ int lm_get_fixed_columns( LM lm ) { return ( ( LM_DATA * ) lm )->fixed_columns; } void lm_local_hscroll( LM lm, int nbr_columns ) { LM_DATA *lmp; XI_LIST_DATA *list_data; XI_OBJ *other_list; lmp = ( LM_DATA * ) lm; list_data = lmp->list_obj->v.list; lm_hscroll( lm, nbr_columns, 0 ); if ( list_data->horz_sync_list && !list_data->scrolling_in_progress ) { list_data->scrolling_in_progress = TRUE; other_list = xi_get_obj( lmp->itf_obj, list_data->horz_sync_list ); if ( other_list ) lm_hscroll( other_list->v.list->lm, nbr_columns, 0 ); list_data->scrolling_in_progress = FALSE; } } static void lm_xi_text_set_color_and_attrib( LM_DATA * lmp, LM_COLUMN_DATA * lm_column_data, LM_CELL_DATA * cell_data, int row ) { unsigned long attrib, cell_attrib; cell_data->xi_text->fore_color = lmp->enabled_color; if ( lmp->row_colors[row] ) cell_data->xi_text->fore_color = lmp->row_colors[row]; if ( cell_data->color ) cell_data->xi_text->fore_color = cell_data->color; cell_data->xi_text->back_color = lmp->back_color; if ( cell_data->back_color ) cell_data->xi_text->back_color = cell_data->back_color; cell_attrib = cell_data->attrib & ( XI_ATR_HCENTER | XI_ATR_RJUST ); attrib = lm_column_data->attrib; if ( cell_attrib ) { attrib &= ~( XI_ATR_HCENTER | XI_ATR_RJUST ); attrib |= cell_attrib; } xi_text_right_justify_set( cell_data->xi_text, ( BOOLEAN ) ( ( attrib & XI_ATR_RJUST ) != 0 ) ); } void lm_xi_text_construct( LM_DATA * lmp, int row, int column ) { XinFont *font; LM_COLUMN_DATA *lm_column_data; int pix_spacing; LM_CELL_DATA *cell_data; XinRect rect; lm_column_data = lmp->lm_column_data[column]; cell_data = &lmp->cell_data[row][column]; pix_spacing = lm_column_data->pix_width - ( cell_data->button ? lmp->pix_row_spacing : 0 ); if ( cell_data->font ) font = cell_data->font; else if ( lmp->font ) font = lmp->font; else font = xi_get_system_font( ); cell_data->xi_text = xi_text_construct( lmp->win, pix_spacing, font, lmp, lm_column_data->wrap_text, XI_MULTILINE_SCROLLBAR_CID, FALSE ); rect = lmp->mlr; if ( lmp->pixel_width ) rect.right = rect.left + lmp->pixel_width; xi_text_clip_set( cell_data->xi_text, &rect ); lm_xi_text_set_color_and_attrib( lmp, lm_column_data, cell_data, row ); xi_text_parent_obj_set( cell_data->xi_text, lmp->list_obj ); xi_text_scrollbar_set( cell_data->xi_text, lm_column_data->wrap_text_scrollbar ); xi_text_read_only_set( cell_data->xi_text, ( lm_column_data->attrib & XI_ATR_READONLY ) != 0 ); xi_text_password_set( cell_data->xi_text, ( lm_column_data->attrib & XI_ATR_PASSWORD ) != 0 ); xi_text_cr_ok_set( cell_data->xi_text, lm_column_data->cr_ok ); xi_text_var_len_text_set( cell_data->xi_text, lm_column_data->var_len_text ); xi_text_pix_width_set( cell_data->xi_text, pix_spacing ); xi_text_buffer_size_set( cell_data->xi_text, lm_column_data->text_size ); xi_text_min_buffer_size_set( cell_data->xi_text, lm_column_data->text_size ); } /*------------------------------------------------------------------------- function: do_lm_cb_text lm: current lm row: row col: column -------------------------------------------------------------------------*/ void do_lm_cb_text( LM_DATA * lmp, int row, int col, BOOLEAN preserve_focus_text ) { LM_CELL_DATA *cell_data; LM_CB_DATA lm_cb_data; LM_COLUMN_DATA *lm_column_data; XI_BITMAP *new_bitmap; cell_data = &( lmp->cell_data[row][col] ); lm_cb_data.lm = ( LM ) lmp; lm_cb_data.cb_type = LM_CB_TEXT; lm_cb_data.cid = lmp->cid; lm_cb_data.win = lmp->win; lm_cb_data.row = ( unsigned char ) row; lm_cb_data.rec = lmp->recs[row]; lm_cb_data.column = ( unsigned char ) col; lm_column_data = lmp->lm_column_data[col]; if ( xi_text_buffer_size_get( cell_data->xi_text ) ) { lmp->cell_request_text = XinMemoryAlloc( xi_text_buffer_size_get( cell_data->xi_text ) ); strcpy( lmp->cell_request_text, xi_text_get( cell_data->xi_text ) ); } lm_cb_data.v.text.text = xi_text_get( cell_data->xi_text ); lm_cb_data.v.text.len = xi_text_buffer_size_get( cell_data->xi_text ); if ( lm_cb_data.v.text.text ) lm_cb_data.v.text.text[0] = '\0'; lm_cb_data.v.text.font = NULL; lm_cb_data.v.text.font_id = NULL; lm_cb_data.v.text.icon_rid = cell_data->icon_rid; lm_cb_data.v.text.bitmap = cell_data->bitmap; lm_cb_data.v.text.attrib = cell_data->attrib; lm_cb_data.v.text.color = 0; lm_cb_data.v.text.back_color = 0; lmp->in_cell_request = TRUE; lmp->cell_request_row = row; lmp->cell_request_col = col; ( *lmp->lm_cb ) ( &lm_cb_data ); cell_data->icon_rid = lm_cb_data.v.text.icon_rid; new_bitmap = xi_bitmap_copy( lm_cb_data.v.text.bitmap ); xi_bitmap_destroy( cell_data->bitmap ); cell_data->bitmap = new_bitmap; cell_data->attrib = lm_cb_data.v.text.attrib; cell_data->color = lm_cb_data.v.text.color; cell_data->back_color = lm_cb_data.v.text.back_color; if ( lm_column_data->var_len_text && lm_cb_data.v.text.text != xi_text_get( cell_data->xi_text )) { /* The callback func reallocated the string, so the old pointer is simply not valid. */ xi_text_buffer_set( cell_data->xi_text, lm_cb_data.v.text.text ); xi_text_reinitialize( cell_data->xi_text ); } if ( lm_cb_data.v.text.len != xi_text_buffer_size_get( cell_data->xi_text ) ) xi_text_buffer_size_set( cell_data->xi_text, lm_cb_data.v.text.len ); if ( lmp->cell_request_text && strcmp( xi_text_get( cell_data->xi_text ), lmp->cell_request_text ) != 0 ) xi_text_wrap( cell_data->xi_text ); if ( lmp->cell_request_text ) XinMemoryFree( lmp->cell_request_text ); lmp->in_cell_request = FALSE; lmp->cell_request_text = NULL; cell_data->valid_data = TRUE; lm_focus_cell_text_set( lmp, preserve_focus_text, lm_cb_data.v.text.text, row, col, FALSE ); #if XIWS != XIWS_WM /* cell buttons not supported for XVT/CH */ cell_data->button = lm_cb_data.v.text.button; cell_data->button_on_left = lm_cb_data.v.text.button_on_left; cell_data->button_on_focus = lm_cb_data.v.text.button_on_focus; cell_data->button_full_cell = lm_cb_data.v.text.button_full_cell; cell_data->button_icon_rid = lm_cb_data.v.text.button_icon_rid; new_bitmap = xi_bitmap_copy( lm_cb_data.v.text.button_bitmap ); xi_bitmap_destroy( cell_data->button_bitmap ); cell_data->button_bitmap = new_bitmap; #endif if ( cell_data->font ) XinFontDestroy( cell_data->font ); if ( ( BOOLEAN ) xi_get_pref( XI_PREF_R4_API ) ) { if ( lm_cb_data.v.text.font ) XinFontCopy( &cell_data->font, lm_cb_data.v.text.font ); else cell_data->font = NULL; } #ifdef XI_USE_XVT else { if ( lm_cb_data.v.text.font_id ) cell_data->font = XinFontXvtConvert( lm_cb_data.v.text.font_id ); else cell_data->font = NULL; } #endif if ( cell_data->font != NULL ) XinWindowFontMap( lmp->win, cell_data->font ); if ( cell_data->font ) xi_text_font_set( cell_data->xi_text, cell_data->font ); else if ( lmp->font ) xi_text_font_set( cell_data->xi_text, lmp->font ); else xi_text_font_set( cell_data->xi_text, xi_get_system_font( ) ); lm_xi_text_set_color_and_attrib( lmp, lm_column_data, cell_data, row ); if ( lm_column_data->wrap_text ) xi_text_wrap( cell_data->xi_text ); if ( !preserve_focus_text ) { int frow, fcol; BOOLEAN fis_scrolled; lm_focus_cell_get( lmp, &frow, &fcol, &fis_scrolled ); if ( frow == row && fcol == col ) { lmp->focus_state->focus_cell_attrib = cell_data->attrib; if ( lmp->focus_state->focus_cell_font ) { XinFontDestroy( lmp->focus_state->focus_cell_font ); lmp->focus_state->focus_cell_font = NULL; } if ( cell_data->font ) { XinFontCopy( &lmp->focus_state->focus_cell_font, cell_data->font ); } lmp->focus_state->focus_cell_color = cell_data->color; lmp->focus_state->focus_cell_back_color = cell_data->back_color; lmp->focus_state->button = cell_data->button; lmp->focus_state->button_full_cell = cell_data->button_full_cell; lmp->focus_state->button_on_left = cell_data->button_on_left; lmp->focus_state->button_icon_rid = cell_data->button_icon_rid; lmp->focus_state->button_bitmap = cell_data->button_bitmap; } } } /*------------------------------------------------------------------------- function: lm_get_attrib lm: current lm lm_part: may be LM_LIST, LM_ROW, LM_COLUMN, or LM_CELL returns: attribute notes: all rows are -------------------------------------------------------------------------*/ unsigned long lm_get_attrib( LM lm, LM_PART lm_part, int idx, int idx2, BOOLEAN v_scrolled ) { LM_DATA *lmp = LMP( lm ); switch ( lm_part ) { case LM_LIST: return ( lmp->attrib ); case LM_ROW: if ( v_scrolled ) return lm_focus_rec_attrib_get( lmp ); else return ( lmp->row_attribs[idx] ); case LM_COLUMN: return ( lmp->lm_column_data[idx]->attrib ); case LM_CELL: { LM_CELL_DATA *cell_data; /* TODO this used to test lm_focus_state_get also */ if ( v_scrolled ) return lm_focus_cell_attrib_get( lmp ); cell_data = &lmp->cell_data[idx][idx2]; if ( !cell_data->valid_data ) do_lm_cb_text( lmp, idx, idx2, TRUE ); return ( cell_data->attrib ); } } XinError( 20907, XinSeverityFatal, 0L ); return 0L; } /*------------------------------------------------------------------------- function: lm_get_buf_size lm: current lm part: must be LM_COLUMN idx: column number -------------------------------------------------------------------------*/ int lm_get_buf_size( LM lm, LM_PART part, int idx ) { LM_DATA *lmp = LMP( lm ); NOREF( part ); return ( lmp->lm_column_data[idx]->text_size ); } /*------------------------------------------------------------------------- function: lm_get_cid lm: current lm returns: control id of list -------------------------------------------------------------------------*/ int lm_get_cid( LM lm ) { LM_DATA *lmp = LMP( lm ); return lmp->cid; } /*------------------------------------------------------------------------- function: lm_get_rect lm: current lm part: may be LM_LIST, LM_COLUMN, or LM_ROW idx: if LM_LIST, not used if LM_COLUMN, column number if LM_ROW, row number rct: rectangle to be filled in returns: rct -------------------------------------------------------------------------*/ XinRect * lm_get_rect( LM lm, LM_PART part, int idx, XinRect * rct ) { LM_DATA *lmp = LMP( lm ); switch ( part ) { case LM_LIST: *rct = lmp->rct; if ( lmp->pixel_width ) rct->right = rct->left + lmp->pixel_width + 2 * BORDER_WIDTH; break; case LM_COLUMN: { LM_COLUMN_DATA *column_data; column_data = lmp->lm_column_data[idx]; if ( idx >= lmp->fixed_columns ) rct->left = lmp->rct.left + column_data->x_pix_pos - lmp->delta_x; else rct->left = lmp->rct.left + column_data->x_pix_pos; rct->right = rct->left + column_data->pix_width + 2 * ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); rct->top = lmp->rct.top; rct->bottom = lmp->rct.bottom; break; } case LM_ROW: { int con; con = lmp->rrr_offset + lmp->mlr.top; rct->top = lmp->pix_offsets[idx] + con; rct->bottom = rct->top + lmp->pix_heights[idx]; rct->left = lmp->rct.left; rct->right = lmp->rct.right; break; } default: break; } return rct; } /*------------------------------------------------------------------------- function: lm_get_text lm: current lm s: string to be filled in NULL is valid len: size of string to be filled in if the length of the text is longer than the size of the buffer s, then len-1 characters will be copied, and s[len-1] will be set to '\0' row: row number of cell column: column number of cell returns: s -------------------------------------------------------------------------*/ char * lm_get_text( LM lm, char *s, int len, int row, int column, BOOLEAN v_scrolled ) { LM_DATA *lmp = LMP( lm ); char *b; if ( row == LM_HEADING_TEXT ) b = lmp->lm_column_data[column]->heading_text; /* TODO this used to test lm_focus_state_get also */ else if ( v_scrolled ) b = lm_focus_cell_text_get( lmp ); else { LM_CELL_DATA *cell_data; if ( row >= lmp->nbr_realized_rows || column >= lmp->nbr_columns ) { XinError( 20908, XinSeverityWarning, 0L ); b = NULL; } else { cell_data = &lmp->cell_data[row][column]; if ( !cell_data->valid_data ) { do_lm_cb_text( lmp, row, column, TRUE ); b = xi_text_get( cell_data->xi_text ); } else { /* If we're currently in a cell request, xi_text_get( * cell_data->xi_text ) will return "". */ if ( LM_REQUESTING_CELL( lm, row, column ) ) b = lmp->cell_request_text; else b = xi_text_get( cell_data->xi_text ); } } } if ( s && b ) { tstrncpy( s, b, len ); return s; } else return b; } /*------------------------------------------------------------------------- function: lm_get_sel lm: current lm c1: pointer to integer to contain selection start c2: pointer to integer to contain selection stop -------------------------------------------------------------------------*/ void lm_get_sel( LM lm, int *c1, int *c2 ) { lm_focus_cell_selection_get( ( LM_DATA * ) lm, c1, c2 ); } /*------------------------------------------------------------------------- function: lm_invalidate_rows_internal lm: current lm row_start: starting row row_end: ending row redraw: if TRUE, then redraw the rows column: if -1, then do for all columns if set, then do for a specific column -------------------------------------------------------------------------*/ void lm_invalidate_rows_internal( LM lm, int row_start, int row_end, BOOLEAN redraw, int column, BOOLEAN preserve_focus_text ) { int row, col; XinRect rct1, rct2; LM_DATA *lmp = LMP( lm ); int focus_row, focus_column; BOOLEAN v_scrolled, has_focus, optimize; row_end = min( row_end, lmp->nbr_realized_rows - 1 ); if ( row_start > row_end || row_start < 0 ) return; has_focus = lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); optimize = xi_get_pref( XI_PREF_OPTIMIZE_CELL_REQUESTS ) != 0; for ( row = row_start; row <= row_end; row++ ) { for ( col = ( column == -1 ) ? 0 : column; col < ( ( column == -1 ) ? ( lmp->nbr_columns ) : ( column + 1 ) ); col++ ) { if ( optimize ) { /* if the cell that we are doing cell request for has focus, and we are * not supposed to preserve focus text, then we need to call * do_lm_cb_text so that the focus cell text will be set to the new * value so that when we restart editing, the correct value will be in * the edit control. */ if ( has_focus && !v_scrolled && !preserve_focus_text && row == focus_row && col == focus_column ) do_lm_cb_text( lmp, row, col, preserve_focus_text ); else { LM_CELL_DATA *cell_data; cell_data = &( lmp->cell_data[row][col] ); cell_data->valid_data = FALSE; } } else { do_lm_cb_text( lmp, row, col, preserve_focus_text ); } } } if ( redraw ) { if ( ( column == -1 ) && ( row_start == 0 ) && ( row_end == lmp->nbr_realized_rows - 1 ) ) { lm_get_row_rect( &rct1, lm, row_start ); lm_get_row_rect( &rct2, lm, row_end ); rct1.bottom = rct2.bottom; lm_invalidate_rect( lmp, &rct1, FALSE ); } else { int col_start, col_end; if ( column == -1 ) { col_start = 0; col_end = lmp->nbr_columns - 1; } else col_start = col_end = column; draw_cell_range( lmp, row_start, row_end, col_start, col_end, FALSE ); } } } /*------------------------------------------------------------------------- function: lm_invalidate_rows lm: current lm row_start: starting row row_end: ending row redraw: if TRUE, then redraw the rows -------------------------------------------------------------------------*/ void lm_invalidate_rows( LM lm, int row_start, int row_end, BOOLEAN redraw ) { lm_invalidate_rows_internal( lm, row_start, row_end, redraw, -1, TRUE ); } /*------------------------------------------------------------------------- function: lm_set_color lm: current lm part: must be LM_ROW, or LM_CELL idx: if LM_ROW, row number if LM_CELL, row number idx2: if LM_ROW, not used if LM_CELL, column number color: color to set half_baked: if set, don't redraw -------------------------------------------------------------------------*/ void lm_set_color( LM lm, LM_PART part, int idx, int idx2, BOOLEAN v_scrolled, XinColor color, BOOLEAN half_baked ) { BOOLEAN do_redraw; LM_DATA *lmp = LMP( lm ); switch ( part ) { case LM_ROW: if ( v_scrolled ) lm_focus_rec_color_set( lmp, color ); else { do_redraw = ( color != lmp->row_colors[idx] ); lmp->row_colors[idx] = color; if ( !half_baked && do_redraw ) { XinRect r; lm_get_row_rect( &r, lm, idx ); lm_invalidate_rect( lmp, &r, FALSE ); } } break; case LM_CELL: if ( !v_scrolled ) { LM_CELL_DATA *cell_data; cell_data = &lmp->cell_data[idx][idx2]; if ( !cell_data->valid_data ) return; cell_data->color = color; } break; default: break; } } /*------------------------------------------------------------------------- function: lm_move_to lmp: current lmp p: point -------------------------------------------------------------------------*/ void lm_move_to( LM_DATA * lmp, XinPoint p, BOOLEAN adj_v, BOOLEAN adj_h ) { if ( adj_h ) { p.h += lmp->rct.left; lm_adj_h( lmp, &p.h ); } if ( adj_v ) lm_adj_v( lmp, p.v ); xi_move_to( lmp->win, p ); } /*------------------------------------------------------------------------- function: lm_draw_line lmp: current lmp p: point -------------------------------------------------------------------------*/ void lm_draw_line( LM_DATA * lmp, XinPoint p, BOOLEAN adj_v, BOOLEAN adj_h ) { if ( adj_h ) { p.h += lmp->rct.left; if ( lm_adj_h( lmp, &p.h ) ) { if ( adj_v ) lm_adj_v( lmp, p.v ); xi_draw_line( lmp->win, p ); } } else { if ( adj_v ) lm_adj_v( lmp, p.v ); xi_draw_line( lmp->win, p ); } } /*------------------------------------------------------------------------- function: xvtch_draw_line lmp: current lm pointer from: from point to: to point -------------------------------------------------------------------------*/ #if XIWS == XIWS_WM static void xvtch_draw_line( LM_DATA * lmp, XinPoint * from, XinPoint * to, BOOLEAN adj_h ) { int i, len; char c_v = ( char ) C_V; char *s, *s2; if ( from->h == to->h ) { /* draw vertical line */ if ( adj_h ) { from->h += lmp->rct.left; if ( lm_adj_h( lmp, &from->h ) ) { for ( i = from->v + VPIX_PER_CH; i <= to->v; i += VPIX_PER_CH ) xi_draw_text( lmp->win, lmp->cur_font, from->h, i, &c_v, 1 ); } } else { for ( i = from->v + VPIX_PER_CH; i <= to->v; i += VPIX_PER_CH ) xi_draw_text( lmp->win, from->h, i, &c_v, 1 ); } } else { len = ( to->h - from->h ) / VPIX_PER_CH; s = xi_tree_malloc( len + 4, NULL ); s2 = s; for ( i = 0; i < len; ++i ) { *s2 = C_H; ++s2; } xi_draw_text( lmp->win, from->h, from->v + VPIX_PER_CH, s, len ); xi_tree_free( s ); } } #endif #if XIWS == XIWS_WM static char c_xd, c_xu, c_v, c_xx; #endif void lm_xi_text_prect_get( LM_DATA * lmp, LM_COLUMN_DATA * column_data, LM_CELL_DATA * cell_data, int row, int col, int col_offset, int leading, int ascent, int descent, XinRect * rctp ) { XinRect rct, row_rct; lm_get_row_rect( &row_rct, ( LM ) lmp, row ); /* don't need to test for the rule, it is drawn elsewhere */ #if XIWS != XIWS_WM row_rct.bottom--; if ( row_rct.bottom < row_rct.top ) row_rct.bottom = row_rct.top; #endif lm_get_rect( ( LM ) lmp, LM_COLUMN, col, &rct ); lm_adj_v( lmp, row_rct.top ); lm_adj_v( lmp, row_rct.bottom ); rct.top = row_rct.top; rct.bottom = row_rct.bottom; rct.left += col_offset; rct.right -= col_offset; if ( !column_data->wrap_text ) { if ( column_data->vertical_align_bottom ) { int new_top; #if XIWS == XIWS_WM new_top = rct.bottom - ( leading + ascent + descent ); #else new_top = rct.bottom - ( leading + ascent + descent ) - 3; #endif rct.top = max( rct.top, new_top ); } else if ( column_data->vertical_align_center ) { int height, delta, new_top, new_bottom; height = leading + ascent + descent; delta = ( rct.bottom - rct.top - height ) / 2; new_top = rct.top + delta; new_bottom = rct.bottom - delta; rct.top = max( rct.top, new_top ); rct.bottom = min( rct.bottom, new_bottom ); } else if ( !column_data->vertical_align_center && !cell_data->button_full_cell ) { int new_bottom; #if XIWS == XIWS_WM new_bottom = rct.top + leading + ascent + descent; #else new_bottom = rct.top + leading + ascent + descent + 4; #endif rct.bottom = min( rct.bottom, new_bottom ); } } if ( cell_data->button ) { XinRect r; r = rct; xi_inflate_rect( &r, 1 ); if ( !cell_data->button_full_cell && ( !cell_data->button_on_focus || lm_row_has_focus( lmp, row, FALSE ) ) ) { if ( cell_data->button_on_left ) { r.right = r.left + lmp->pix_row_spacing; rct.left = r.right; } else { r.left = r.right - lmp->pix_row_spacing; rct.right = r.left - ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); } } } #if XIWS != XIWS_WM rct.top += RULE_Y_OFFSET_TOP; rct.bottom -= RULE_Y_OFFSET_BOTTOM; #endif if ( column_data->column_well || column_data->column_platform ) { rct.left++; rct.right--; } *rctp = rct; } /*------------------------------------------------------------------------- function: redraw_cell lm: current lm row: col: update: if TRUE, redraw_cell is being called due to an XinEventPaint event. clip_rct: clip rectangle -------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /* redraw_cell */ /*-------------------------------------------------------------------------*/ void redraw_cell( LM lm, int row, int col, BOOLEAN update ) { draw_cell_range( ( LM_DATA * ) lm, row, row, col, col, update ); } /*-------------------------------------------------------------------------*/ /* lm_redraw_row */ /*-------------------------------------------------------------------------*/ void lm_redraw_row( LM_DATA * lmp, int row, BOOLEAN update ) { draw_cell_range( lmp, row, row, 0, lmp->nbr_columns - 1, update ); } /*------------------------------------------------------------------------- function: lm_hit_test lm: current lm ep: xvt event oevt: original xvt event, without virtual space coordinate conversions rowp: if set, to be filled in with the results of the row hit test columnp: if set, to be filled in with the results of the column hit test is_vis: row is fully visible is_part_vis:row is partially visible returns: 0 - no hit 1 - hit 5 - hit on cell button -------------------------------------------------------------------------*/ int lm_hit_test( LM lm, XinEvent * ep, XinEvent * oevt, int *rowp, int *columnp, BOOLEAN * is_vis, BOOLEAN * is_hit, BOOLEAN * is_part_vis ) { int row, column, temp, col_offset, tmp_v, i, first, last; int *pix_offsetsp, *pix_heightsp; LM_DATA *lmp = LMP( lm ); XinPoint where; if ( is_vis ) *is_vis = TRUE; if ( is_hit ) *is_hit = FALSE; if ( is_part_vis ) *is_part_vis = FALSE; col_offset = ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); where = ep->v.mouse.where; /* if the mouse is to the left or the right of the list, return 0 */ if ( oevt->v.mouse.where.h < 0 ) return 0; if ( lmp->pixel_width && ( oevt->v.mouse.where.h > ( lmp->rct.left + lmp->pixel_width + BORDER_WIDTH ) ) ) return 0; /* if the mouse is in the column headings */ if ( where.v > lmp->pix_top && where.v < lmp->pix_row1_top ) { for ( column = 0; column < lmp->nbr_columns; column++ ) { LM_COLUMN_DATA *column_data; column_data = lmp->lm_column_data[column]; temp = column_data->x_pix_pos + col_offset; if ( where.h >= temp && where.h < temp + column_data->pix_width ) { *rowp = -1; *columnp = column; return 1; } } return 0; } /* if the mouse is above the list, return 0 */ if ( where.v < lmp->pix_row1_top ) return 0; /* figure out what row the mouse is in */ tmp_v = where.v - lmp->mlr.top - lmp->rrr_offset; first = max( lmp->first_fully_vis - 1, 0 ); last = min( lmp->last_fully_vis + 1, lmp->nbr_realized_rows - 1 ); for ( i = first, pix_offsetsp = &lmp->pix_offsets[i], pix_heightsp = &lmp->pix_heights[i]; i <= last; ++i, ++pix_offsetsp, ++pix_heightsp ) { if ( tmp_v >= *pix_offsetsp && tmp_v < ( *pix_offsetsp + *pix_heightsp ) ) { row = i; break; } } if ( i > last ) { if ( is_vis ) *is_vis = FALSE; return 0; } if ( is_vis ) { if ( i < lmp->first_fully_vis || i > lmp->last_fully_vis ) { *is_vis = FALSE; if ( ( i == lmp->last_fully_vis + 1 ) && ( ep->v.mouse.where.v < lmp->mlr.bottom ) ) { if ( is_part_vis ) *is_part_vis = TRUE; } } else *is_vis = TRUE; } else if ( i < lmp->first_fully_vis || i > lmp->last_fully_vis ) return 0; for ( column = 0; column < lmp->nbr_columns; column++ ) { LM_COLUMN_DATA *column_data; column_data = lmp->lm_column_data[column]; temp = column_data->x_pix_pos + col_offset; if ( where.h >= temp && where.h < temp + column_data->pix_width ) { if ( ( ( lmp->attrib & ( LM_ATR_ENABLED | LM_ATR_VISIBLE ) ) == ( LM_ATR_ENABLED | LM_ATR_VISIBLE ) ) && ( lmp->lm_column_data[column]->attrib & ( LM_COL_ATR_ENABLED | LM_COL_ATR_SELECTABLE ) ) ) { LM_CELL_DATA *cell_data; *rowp = row; *columnp = column; if ( is_hit ) *is_hit = TRUE; cell_data = &lmp->cell_data[row][column]; if ( cell_data->button ) { XinRect outer_rct; XinPoint p; lm_get_cell_rect( &outer_rct, lm, row, column, FALSE, FALSE ); xi_inflate_rect( &outer_rct, 1 ); if ( !cell_data->button_full_cell ) { if ( cell_data->button_on_left ) outer_rct.right = outer_rct.left + lmp->pix_row_spacing; else outer_rct.left = outer_rct.right - lmp->pix_row_spacing; } p = ep->v.mouse.where; p.v = tmp_v; if ( xi_pt_in_rect( &outer_rct, p ) ) { return 5; } } else if ( lm_focus_cell_has( lmp, row, column, FALSE ) ) { XinRect outer_rct; XinPoint p; lm_get_cell_rect( &outer_rct, lm, row, column, FALSE, FALSE ); xi_inflate_rect( &outer_rct, 1 ); if ( cell_data->xi_text ) xi_text_rect_get_adjusted( cell_data->xi_text, &outer_rct ); p = ep->v.mouse.where; p.v = tmp_v; if ( !xi_pt_in_rect( &outer_rct, p ) ) { return 0; } } return 1; } else { *rowp = row; *columnp = column; if ( is_hit ) *is_hit = TRUE; if ( is_part_vis ) *is_part_vis = FALSE; return 0; } } } if ( is_part_vis ) *is_part_vis = FALSE; return 0; } /*------------------------------------------------------------------------- function: lm_set_font lm: current lm part: must be LM_COLUMN, or LM_CELL idx: column number idx2: if LM_CELL, row number font: font to set -------------------------------------------------------------------------*/ void lm_set_font( LM lm, LM_PART part, int idx, int idx2, XinFont * font ) { LM_DATA *lmp = LMP( lm ); LM_CELL_DATA *cell_data; switch ( part ) { case LM_COLUMN: { LM_COLUMN_DATA *lmcd; XinRect rct; lmcd = lmp->lm_column_data[idx]; if ( lmcd->font ) XinFontDestroy( lmcd->font ); if ( font ) XinFontCopy( &lmcd->font, font ); else lmcd->font = NULL; lm_get_rect( lm, LM_COLUMN, idx, &rct ); lm_invalidate_rect( lmp, &rct, TRUE ); break; } case LM_CELL: { cell_data = &lmp->cell_data[idx][idx2]; if ( cell_data->font ) XinFontDestroy( cell_data->font ); if ( font ) XinFontCopy( &cell_data->font, font ); else cell_data->font = NULL; if ( cell_data->font != NULL ) XinWindowFontMap( lmp->win, cell_data->font ); break; } default: break; } } /*********************************************************************/ /* LM drawing functions */ #if 0 /*------------------------------------------------------------------------- function: lm_set_clip lmp: current lmp rctp: clipping rectangle -------------------------------------------------------------------------*/ static void lm_set_clip( LM_DATA * lmp, XinRect * rctp ) { XinRect r; r = *rctp; r.left += lmp->rct.left; r.right += lmp->rct.left; lm_adj_h( lmp, &r.left ); lm_adj_h( lmp, &r.right ); xi_set_clip( lmp->win, &r ); } #endif /*------------------------------------------------------------------------- function: lm_draw_text lmp: current lmp h: h position v: v position s: string len: length of string -------------------------------------------------------------------------*/ #if XIWS == XIWS_WM static void lm_draw_text( LM_DATA * lmp, int h, int v, char *s, int len ) { short sh; sh = h; sh += lmp->rct.left; if ( lm_adj_h( lmp, &sh ) ) { lm_adj_v( lmp, v ); xi_draw_text( lmp->win, sh, v, s, len ); } } #endif /* Make it so a row can have the focus. Tab will move focus to a cell in the row, if there is a cell in the row that can take focus. If XI_ATR_SELECTABLE is set for any columns, space will select the row. Up arrow and down arrow work. If the focus is on a cell, the focus stays on a cell. If the focus is on a row, the focus stays on a row. Page up and down should work. If the focus is on the last editable cell, and tab_wrap is set, then the focus moves to the next row. If the focus is the focus move to the current row. Have an option on a list by list basis, enter selects if a row has the focus. Enter will act like a double click selection. If the row focus option is set, put up the other hand cursor. possibly revisit how text is stored in cells - may want indefinite buffer size for a column. when entering text in a cell, continually evaluate the correct height of the cell. The function to recalc number of rows and break points should compare line by line, until there is a difference, then do detailed comparison after the difference. it should be possible to scroll the list backwards to gain more room above if the cell is at the bottom of the list if the cell is already the maximum possible height, then not necessary to do any more comparisons limit text edit to buffer size make sure that the clipboard works TODO in the future ------------------ if height is 0, make the list height be the same as the window height expose lm drawing functions xi_set_icon for buttons make an option so that when the user tabs to an edit control that already contains text, the insertion point is set to the right of the text. settable background color for text on a 3d radio button or check box, (tab buttons also) make function xi_is_nav_char make readonly cells need colors for static text want ability on XVT/CH to have single character fields. Eliminate the [ and ]. */ void set_focus_cell_rct( LM_DATA * lmp, int row, int col, BOOLEAN in_update_event ) { LM_CELL_DATA *cell_data; LM_COLUMN_DATA *column_data; int leading, ascent, descent; int col_offset; XinRect rct; XinRect mlr = lmp->mlr; col_offset = ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); cell_data = &lmp->cell_data[row][col]; column_data = lmp->lm_column_data[col]; if ( cell_data->font ) XinFontMetricsGet( cell_data->font, &leading, &ascent, &descent ); else XinFontMetricsGet( lmp->font, &leading, &ascent, &descent ); lm_xi_text_prect_get( lmp, column_data, cell_data, row, col, col_offset, leading, ascent, descent, &rct ); xi_text_prect_set( cell_data->xi_text, &rct ); if ( lmp->pixel_width ) mlr.right = mlr.left + lmp->pixel_width; xi_text_clip_set( cell_data->xi_text, &mlr ); if ( xi_text_editing_is( cell_data->xi_text ) ) xi_text_draw( cell_data->xi_text, 0L, 0L, in_update_event ); } XI_TEXT * lm_xi_text_focus_get( LM lm ) { LM_DATA *lmp = ( LM_DATA * ) lm; int row, col; BOOLEAN is_vert; LM_CELL_DATA *cell_data; if ( !lm_focus_cell_get( lmp, &row, &col, &is_vert ) ) return NULL; if ( is_vert ) return NULL; cell_data = &( lmp->cell_data[row][col] ); return cell_data->xi_text; }