/******************************************************************************* * 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" #include "xi_int.h" #define REALIZED_ROWS_GRANULE 4 #define REC_AT_TOP 1 #define REC_AT_BOTTOM 0 #define CELL_VERTICAL_MARGIN (RULE_Y_OFFSET_TOP + RULE_Y_OFFSET_BOTTOM + 2) /*------------------------------------------------------------------------- function: lm_allocate_rec_info lmp: current lmp realized_rows_array_len: number of realized rows in the realized rows array -------------------------------------------------------------------------*/ void lm_allocate_rec_info( LM_DATA * lmp, int realized_rows_array_len ) { int old_len = lmp->realized_rows_array_len; int i; lmp->realized_rows_array_len = realized_rows_array_len; /* recs */ if ( lmp->recs ) { lmp->recs = ( long * ) xi_tree_realloc( lmp->recs, sizeof( long ) * realized_rows_array_len ); for ( i = old_len; i < realized_rows_array_len; ++i ) lmp->recs[i] = 0L; } else lmp->recs = ( long * ) xi_tree_malloc( sizeof( long ) * realized_rows_array_len, lmp ); /* pix_offsets */ if ( lmp->pix_offsets ) lmp->pix_offsets = ( int * ) xi_tree_realloc( lmp->pix_offsets, sizeof( int ) * realized_rows_array_len ); else lmp->pix_offsets = ( int * ) xi_tree_malloc( sizeof( int ) * realized_rows_array_len, lmp ); /* pix_heights */ if ( lmp->pix_heights ) lmp->pix_heights = ( int * ) xi_tree_realloc( lmp->pix_heights, sizeof( int ) * realized_rows_array_len ); else lmp->pix_heights = ( int * ) xi_tree_malloc( sizeof( int ) * realized_rows_array_len, lmp ); /* set_heights */ if ( lmp->set_heights ) lmp->set_heights = ( short * ) xi_tree_realloc( lmp->set_heights, sizeof( int ) * realized_rows_array_len ); else lmp->set_heights = ( short * ) xi_tree_malloc( sizeof( int ) * realized_rows_array_len, lmp ); /* row_attribs */ if ( lmp->row_attribs ) { lmp->row_attribs = ( unsigned long * ) xi_tree_realloc( lmp->row_attribs, sizeof( unsigned long ) * realized_rows_array_len ); for ( i = old_len; i < realized_rows_array_len; ++i ) lmp->row_attribs[i] = LM_ROW_ATR_ENABLED; } else lmp->row_attribs = ( unsigned long * ) xi_tree_malloc( sizeof( unsigned long ) * realized_rows_array_len, lmp ); /* row_colors */ if ( lmp->row_colors ) { lmp->row_colors = ( XinColor * ) xi_tree_realloc( lmp->row_colors, sizeof( XinColor ) * realized_rows_array_len ); for ( i = old_len; i < realized_rows_array_len; ++i ) lmp->row_colors[i] = 0L; } else lmp->row_colors = ( XinColor * ) xi_tree_malloc( sizeof( XinColor ) * realized_rows_array_len, lmp ); /* cell_data */ if ( lmp->cell_data ) { /* cell data destructor */ for ( i = realized_rows_array_len; i < old_len; ++i ) xi_tree_free( lmp->cell_data[i] ); lmp->cell_data = ( LM_CELL_DATA ** ) xi_tree_realloc( lmp->cell_data, sizeof( LM_CELL_DATA * ) * realized_rows_array_len ); for ( i = old_len; i < realized_rows_array_len; ++i ) lmp->cell_data[i] = ( LM_CELL_DATA * ) xi_tree_malloc( ( lmp->nbr_columns + 1 ) * sizeof( LM_CELL_DATA ), lmp ); } else lmp->cell_data = ( LM_CELL_DATA ** ) xi_tree_malloc( sizeof( LM_CELL_DATA * ) * realized_rows_array_len, lmp ); } /*------------------------------------------------------------------------- function: lm_create win: window in which to put list lm_def: list definition parent: tree memory parent -------------------------------------------------------------------------*/ LM lm_create( XinWindow win, LM_DEF * lm_def, void *parent ) { LM_DATA *lmp; int i, font_height, leading, ascent, descent, mch, realized_rows_array_len; lmp = LMP( xi_tree_malloc( sizeof( LM_DATA ), parent ) ); lmp->focus_state = ( LM_FOCUS_STATE * ) xi_tree_malloc( sizeof( LM_FOCUS_STATE ), lmp ); lmp->focus_state->where = LM_FOCUS_NOWHERE; lmp->cid = lm_def->cid; lmp->list_obj = lm_def->list_obj; lmp->itf_obj = lm_def->itf_obj; lmp->win = win; lmp->font = lm_def->font; lmp->is_list_font = lm_def->is_list_font; lmp->back_color = lm_def->back_color; lmp->enabled_color = lm_def->enabled_color; lmp->disabled_color = lm_def->disabled_color; lmp->disabled_back_color = lm_def->disabled_back_color; lmp->active_color = lm_def->active_color; lmp->active_back_color = lm_def->active_back_color; lmp->white_space_color = lm_def->white_space_color; lmp->rule_color = lm_def->rule_color; lmp->attrib = lm_def->attrib; lmp->lm_cb = lm_def->lm_cb; lmp->no_heading = lm_def->no_heading; lmp->sizable_columns = lm_def->sizable_columns; lmp->movable_columns = lm_def->movable_columns; lmp->fixed_columns = lm_def->fixed_columns; lmp->resize_with_window = lm_def->resize_with_window; lmp->horz_sync_list = lm_def->horz_sync_list; lmp->vert_sync_list = lm_def->vert_sync_list; lmp->row_focus_border = lm_def->row_focus_border; lmp->row_focus_border_color = lm_def->row_focus_border_color; lmp->single_select = lm_def->single_select; lmp->pixel_width = lm_def->pixel_width; lmp->pixel_height = lm_def->pixel_height; #if XIWS == XIWS_WM lmp->min_cell_height = 0; lmp->min_heading_height = lm_def->min_heading_height; #else lmp->min_cell_height = lm_def->min_cell_height; lmp->min_heading_height = lm_def->min_heading_height; #endif lmp->no_horz_lines = lm_def->no_horz_lines; lmp->no_vert_lines = lm_def->no_vert_lines; lmp->first_vis = lmp->fixed_columns; lmp->drop_and_delete = lm_def->drop_and_delete; lmp->select_cells = lm_def->select_cells; lmp->get_all_records = lm_def->get_all_records; lmp->retain_back_color_on_select = lm_def->retain_back_color_on_select; lmp->drag_and_drop_rows = lm_def->drag_and_drop_rows; lmp->drag_rows_autoscroll = lm_def->drag_rows_autoscroll; lmp->button_on_cell_focus = lm_def->button_on_cell_focus; lmp->position_by_typing_cid = lm_def->position_by_typing_cid; lmp->max_lines_in_cell = lm_def->max_lines_in_cell; if ( !lmp->max_lines_in_cell ) lmp->max_lines_in_cell = ( int ) xi_get_pref( XI_PREF_DEFAULT_MAX_LINES_IN_CELL ); XinWindowFontMap( win, lm_def->font ); XinFontMetricsGet( lm_def->font, &leading, &ascent, &descent ); lmp->cur_font = lm_def->font; lmp->leading = leading; lmp->ascent = ascent; lmp->descent = descent; font_height = lmp->ascent + lmp->leading + lmp->descent; /* we add two to the following line so that there is room in the cell for the * row focus border */ #if XIWS == XIWS_WM lmp->pix_cell_height = font_height; #else lmp->pix_cell_height = font_height + CELL_VERTICAL_MARGIN; #endif #if XIWS == XIWS_WM mch = 8; #else mch = lm_def->min_cell_height; #endif lmp->pix_cell_height = max( lmp->pix_cell_height, mch ); lmp->pix_row_spacing = lmp->pix_cell_height + RULE_WIDTH_H; lmp->pix_top = lm_def->pnt.v; #if XIWS == XIWS_WM lmp->pix_hdr_bottom = lm_def->pnt.v + lmp->leading + lmp->ascent + lmp->descent + BORDER_WIDTH + RULE_Y_OFFSET_BOTTOM + RULE_Y_OFFSET_TOP; lmp->pix_hdr_bottom = max( lmp->pix_hdr_bottom, ( lm_def->pnt.v + lmp->min_heading_height - BORDER_WIDTH ) ); #else lmp->pix_hdr_bottom = lm_def->pnt.v + lmp->leading + lmp->ascent + lmp->descent + BORDER_WIDTH + RULE_Y_OFFSET_BOTTOM + RULE_Y_OFFSET_TOP + 1; lmp->pix_hdr_bottom = max( lmp->pix_hdr_bottom, ( lm_def->pnt.v + lmp->min_heading_height ) ); #endif if ( lmp->no_heading ) lmp->pix_row1_top = lm_def->pnt.v + BORDER_WIDTH; else lmp->pix_row1_top = lmp->pix_hdr_bottom + BORDER_WIDTH; lmp->pix_char_width = lm_def->pix_char_width; lmp->nbr_columns = 0; lmp->lm_column_data = NULL; /* compute number of rows */ lmp->nbr_rows = lm_def->nbr_rows; if ( lm_def->nbr_rows ) lmp->nbr_rows = lm_def->nbr_rows; else lmp->nbr_rows = ( lm_def->pixel_height - ( lmp->pix_row1_top - lm_def->pnt.v ) - BORDER_WIDTH ) / lmp->pix_row_spacing; if ( lm_def->one_row_list ) { lmp->nbr_rows = 1; lm_def->resize_with_window = FALSE; } lmp->rct.left = lm_def->pnt.h; lmp->rct.top = lm_def->pnt.v; #if XIWS != XIWS_WM lmp->rct.right = lm_def->pnt.h + 2 * BORDER_WIDTH; if ( lm_def->resize_with_window ) lmp->rct.bottom = lm_def->pnt.v + lm_def->pixel_height; else lmp->rct.bottom = lmp->pix_row1_top + lmp->nbr_rows * lmp->pix_row_spacing + ( BORDER_WIDTH - RULE_WIDTH_H ); #else lmp->rct.right = lm_def->pnt.h + BORDER_WIDTH; if ( lm_def->resize_with_window ) lmp->rct.bottom = lm_def->pnt.v + lm_def->pixel_height; else lmp->rct.bottom = lmp->pix_row1_top + lmp->nbr_rows * lmp->pix_row_spacing + BORDER_WIDTH; #endif lmp->vir_left = lmp->rct.left + BORDER_WIDTH; lmp->vir_right = lmp->vir_left + lmp->pixel_width; realized_rows_array_len = lm_def->realized_rows_array_len; if ( !realized_rows_array_len ) realized_rows_array_len = lmp->nbr_rows + 1; lm_allocate_rec_info( lmp, realized_rows_array_len ); /* calculate mathematical list rectangle */ lmp->mlr.top = lmp->pix_row1_top; #if XIWS != XIWS_WM lmp->mlr.bottom = lmp->rct.bottom - BORDER_WIDTH; lmp->mlr.left = lmp->rct.left + BORDER_WIDTH; lmp->mlr.right = lmp->rct.right - BORDER_WIDTH; #else if ( lmp->pixel_width ) lmp->mlr.bottom = lmp->rct.bottom; else lmp->mlr.bottom = lmp->rct.bottom - BORDER_WIDTH; lmp->mlr.left = lmp->rct.left; lmp->mlr.right = lmp->rct.right; #endif lmp->mlr_height = lmp->mlr.bottom - lmp->mlr.top; for ( i = 0; i < lmp->realized_rows_array_len; ++i ) { lmp->cell_data[i] = ( LM_CELL_DATA * ) xi_tree_malloc( lmp->nbr_columns * sizeof( LM_CELL_DATA ), lmp ); lmp->row_attribs[i] = LM_ROW_ATR_ENABLED; } return ( ( LM ) ( long ) lmp ); } /*------------------------------------------------------------------------- function: lm_do_rec_event lmp: current lmp row: row to do event on type: XI_EVENT_TYPE notes: In this case, row is not really a row. It is an index into the realized rows array. -------------------------------------------------------------------------*/ void lm_do_rec_event( LM_DATA * lmp, int row, XI_EVENT_TYPE type ) { LM_CELL_DATA *cell_data; int i; if ( type == XIE_REC_FREE ) { if ( lmp->recs[row] != lm_focus_rec_get( lmp ) ) do_lm_cb( ( LM ) lmp, LM_CB_REC_FREE, row, 0, NULL, NULL, 0 ); cell_data = lmp->cell_data[row]; /* cell_data_destruct */ for ( i = 0; i < lmp->nbr_columns; ++i, ++cell_data ) { if ( cell_data->font ) XinFontDestroy( cell_data->font ); xi_bitmap_destroy( cell_data->bitmap ); xi_bitmap_destroy( cell_data->button_bitmap ); if ( cell_data->xi_text ) xi_text_destruct( cell_data->xi_text ); memset( ( char * ) cell_data, '\0', sizeof( LM_CELL_DATA ) ); } lmp->recs[row] = 0L; /* we technically do not need to do the following lines. it just keeps the * data structures clean. */ lmp->pix_heights[row] = 0; lmp->set_heights[row] = FALSE; lmp->pix_offsets[row] = 0; lmp->row_attribs[row] = 0L; lmp->row_colors[row] = 0L; } /* cell_data_construct */ if ( type == XIE_REC_ALLOCATE ) { do_lm_cb( ( LM ) lmp, LM_CB_REC_ALLOCATE, row, 0, NULL, NULL, 0 ); cell_data = lmp->cell_data[row]; for ( i = 0; i < lmp->nbr_columns; ++i, ++cell_data ) { memset( ( char * ) cell_data, '\0', sizeof( LM_CELL_DATA ) ); lm_xi_text_construct( lmp, row, i ); } } } /*------------------------------------------------------------------------- function: lm_row_copy lmp: current lmp source_row: row from which to copy dest_row: destination row -------------------------------------------------------------------------*/ void lm_row_copy( LM_DATA * lmp, int source_row, int dest_row ) { int idx; LM_CELL_DATA *dest_cell_data, *source_cell_data; lmp->recs[dest_row] = lmp->recs[source_row]; lmp->pix_heights[dest_row] = lmp->pix_heights[source_row]; lmp->set_heights[dest_row] = lmp->set_heights[source_row]; lmp->pix_offsets[dest_row] = lmp->pix_offsets[source_row]; lmp->row_attribs[dest_row] = lmp->row_attribs[source_row]; lmp->row_colors[dest_row] = lmp->row_colors[source_row]; for ( idx = 0, dest_cell_data = lmp->cell_data[dest_row], source_cell_data = lmp->cell_data[source_row]; idx < lmp->nbr_columns; ++idx, ++dest_cell_data, ++source_cell_data ) { *dest_cell_data = *source_cell_data; } } /*------------------------------------------------------------------------- function: init_row lmp: current lmp row: row to init -------------------------------------------------------------------------*/ static void init_row( LM_DATA * lmp, int row ) { int idx; LM_CELL_DATA *cell_data; lmp->recs[row] = 0L; lmp->pix_heights[row] = 0; lmp->set_heights[row] = 0; lmp->pix_offsets[row] = 0; lmp->row_attribs[row] = LM_ROW_ATR_ENABLED; lmp->row_colors[row] = 0; for ( idx = 0, cell_data = lmp->cell_data[row]; idx < lmp->nbr_columns; ++idx, ++cell_data ) { memset( ( char * ) cell_data, '\0', sizeof( LM_CELL_DATA ) ); } } /*------------------------------------------------------------------------- function: make_rec_available lmp: current lmp top: record to become available is at the top of the realized_row array, else the record to become available is at the bottom of the realized row array. allocate_rec: if TRUE, then send XIE_REC_ALLOCATE event notes: if, in order to get a record at the bottom of the realized row array, we must discard a record at the beginning of the rrr array, then we must change lmp->rrr_offset. whenever we make a record be available, make sure that we free fonts in cell_data. Also, clear cell_data. -------------------------------------------------------------------------*/ static void make_rec_available( LM_DATA * lmp, BOOLEAN top, BOOLEAN allocate_rec, BOOLEAN doing_scroll_first ) { int cnt; if ( top ) { int idx; if ( !lmp->get_all_records ) { int nbr_to_free; nbr_to_free = lmp->nbr_realized_rows - ( lmp->last_fully_vis + 2 ); if ( nbr_to_free > 0 ) { /* free as many records at the end of the list as possible */ for ( idx = lmp->last_fully_vis + 2; idx < lmp->nbr_realized_rows; ++idx ) lm_do_rec_event( lmp, idx, XIE_REC_FREE ); lmp->nbr_realized_rows -= nbr_to_free; } if ( lmp->nbr_realized_rows >= lmp->realized_rows_array_len ) lm_allocate_rec_info( lmp, lmp->realized_rows_array_len + 1 ); /* copy all the records, making room at the beginning of the realized_row * array */ ++lmp->nbr_realized_rows; for ( idx = lmp->nbr_realized_rows - 1; idx > 0; --idx ) lm_row_copy( lmp, idx - 1, idx ); lm_adjust_rows( lmp, 1 ); init_row( lmp, 0 ); if ( allocate_rec ) lm_do_rec_event( lmp, 0, XIE_REC_ALLOCATE ); return; } } else { if ( !lmp->get_all_records || doing_scroll_first ) { /* free as many records at the beginning of the list as possible */ cnt = lmp->first_fully_vis - 2; if ( cnt > 0 ) { int idx, jidx; for ( idx = 0; idx < cnt; ++idx ) { lmp->rrr_offset += lmp->pix_heights[idx]; lm_do_rec_event( lmp, idx, XIE_REC_FREE ); } lmp->nbr_realized_rows -= cnt; /* copy all the records, making room at the end of the realized row * array */ for ( idx = cnt, jidx = 0; jidx < lmp->nbr_realized_rows; ++idx, ++jidx ) lm_row_copy( lmp, idx, idx - cnt ); lm_adjust_rows( lmp, -cnt ); } if ( lmp->nbr_realized_rows >= lmp->realized_rows_array_len ) lm_allocate_rec_info( lmp, lmp->realized_rows_array_len + 1 ); ++lmp->nbr_realized_rows; init_row( lmp, lmp->nbr_realized_rows - 1 ); if ( allocate_rec ) lm_do_rec_event( lmp, lmp->nbr_realized_rows - 1, XIE_REC_ALLOCATE ); return; } } } /*------------------------------------------------------------------------- function: get_next_rec lmp: current lmp returns: TRUE if next record was available FALSE if not -------------------------------------------------------------------------*/ static BOOLEAN get_next_rec( LM_DATA * lmp, BOOLEAN doing_scroll_first ) { int idx, row_height; make_rec_available( lmp, REC_AT_BOTTOM, TRUE, doing_scroll_first ); idx = lmp->nbr_realized_rows - 1; if ( do_lm_cb_get( ( LM ) lmp, LM_CB_GET_NEXT, &lmp->recs[idx - 1], &lmp->recs[idx], 0, &lmp->row_colors[idx], &lmp->row_attribs[idx], &row_height ) ) { lm_do_rec_event( lmp, lmp->nbr_realized_rows - 1, XIE_REC_FREE ); --lmp->nbr_realized_rows; return FALSE; } if ( row_height ) { lmp->pix_heights[idx] = row_height; lmp->set_heights[idx] = TRUE; } else { lmp->pix_heights[idx] = 0; lmp->set_heights[idx] = FALSE; } lm_invalidate_rows_internal( ( LM ) lmp, idx, idx, FALSE, -1, TRUE ); return TRUE; } /*------------------------------------------------------------------------- function: get_previous_rec lmp: current lmp returns: TRUE if previous record was available FALSE if not -------------------------------------------------------------------------*/ static BOOLEAN get_previous_rec( LM_DATA * lmp ) { int row_height, cnt; make_rec_available( lmp, REC_AT_TOP, TRUE, FALSE ); if ( do_lm_cb_get( ( LM ) lmp, LM_CB_GET_PREV, &lmp->recs[1], &lmp->recs[0], 0, &lmp->row_colors[0], &lmp->row_attribs[0], &row_height ) ) { lm_do_rec_event( lmp, 0, XIE_REC_FREE ); for ( cnt = 0; cnt < lmp->nbr_realized_rows - 1; ++cnt ) lm_row_copy( lmp, cnt + 1, cnt ); lm_adjust_rows( lmp, -1 ); --lmp->nbr_realized_rows; return FALSE; } if ( row_height ) { lmp->pix_heights[0] = row_height; lmp->set_heights[0] = TRUE; } else { lmp->pix_heights[0] = 0; lmp->set_heights[0] = FALSE; } lm_invalidate_rows_internal( ( LM ) lmp, 0, 0, FALSE, -1, TRUE ); return TRUE; } /*------------------------------------------------------------------------- function: calculate_cell_height lmp: current lmp row: row number of cell to calculate column: column number of cell to calculate returns: height of cell in pixels notes: this function does not need to take into consideration the variable fixed_row_height. This function is only used by lm_calculate_row_height, and will only be called if fixed_row_height is false. -------------------------------------------------------------------------*/ static int calculate_cell_height( LM_DATA * lmp, int row, int column ) { int pix_height; /* pix_height does include the width of the * rules */ int nbr_lines; LM_CELL_DATA *cell_data; LM_COLUMN_DATA *lm_column_data; cell_data = &lmp->cell_data[row][column]; lm_column_data = lmp->lm_column_data[column]; if ( !cell_data->font_height ) { XinFont *font; int leading, ascent, descent, char_width; if ( lm_column_data->wrap_text && !cell_data->valid_data ) do_lm_cb_text( lmp, row, column, TRUE ); if ( cell_data->font ) font = cell_data->font; else font = lmp->font; xi_get_font_metrics_font( font, &leading, &ascent, &descent, &char_width ); cell_data->font_height = leading + ascent + descent; } if ( lm_column_data->wrap_text ) { int pix_spacing; BOOLEAN is_editing; int ip1, ip2, start_ip; if ( !cell_data->valid_data ) do_lm_cb_text( lmp, row, column, TRUE ); is_editing = xi_text_editing_is( cell_data->xi_text ); if ( is_editing ) xi_text_selection_get_internal( cell_data->xi_text, &ip1, &ip2, &start_ip ); xi_text_initialized_set( cell_data->xi_text, FALSE ); pix_spacing = lm_column_data->pix_width - ( cell_data->button ? lmp->pix_row_spacing : 0 ); xi_text_pix_width_set( cell_data->xi_text, pix_spacing ); if ( is_editing ) xi_text_selection_set_internal( cell_data->xi_text, ip1, ip2, start_ip, FALSE, FALSE ); nbr_lines = min( xi_text_nbr_lines_get( cell_data->xi_text ), lmp->max_lines_in_cell ); } else nbr_lines = 1; #if XIWS != XIWS_WM pix_height = nbr_lines * cell_data->font_height + CELL_VERTICAL_MARGIN + RULE_WIDTH_H; #else pix_height = nbr_lines * cell_data->font_height; #endif if ( lm_focus_state_get( lmp ) == LM_FOCUS_VERTICALLY_SCROLLED ) { int focus_row, focus_column; BOOLEAN v_scrolled; lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); if ( focus_column == column && lmp->recs[row] == lmp->focus_state->focus_rec ) pix_height = lmp->focus_state->focus_rec_height; } pix_height = max( pix_height, lmp->pix_row_spacing ); return pix_height; } /*------------------------------------------------------------------------- function: lm_calculate_row_height lmp: current lmp row: row number to calculate returns: height of row in pixels notes: if fixed_row_height is TRUE, then the calculation is easy. if fixed_row_height is FALSE, then the calculation must take word wrap into consideration, etc. row height includes the height of the horizontal rule below the row. -------------------------------------------------------------------------*/ int lm_calculate_row_height( LM_DATA * lmp, int row ) { int pix_height; if ( lmp->fixed_row_height ) pix_height = lmp->pix_row_spacing; else if ( lmp->set_heights[row] ) pix_height = lmp->pix_heights[row]; else { int cnt; int cell_height; pix_height = 0; for ( cnt = 0; cnt < lmp->nbr_columns; ++cnt ) { cell_height = calculate_cell_height( lmp, row, cnt ); pix_height = max( pix_height, cell_height ); } } if ( pix_height > lmp->mlr_height ) pix_height = lmp->mlr_height; return pix_height; } /*------------------------------------------------------------------------- function: calculate_visibles lmp: current lmp process: calculates lmp->first_fully_vis calculates lmp->last_fully_vis -------------------------------------------------------------------------*/ void calculate_visibles( LM_DATA * lmp ) { int i, idx; int *pix_offsetsp; int *pix_heightsp; BOOLEAN have_first; have_first = FALSE; lmp->first_fully_vis = 0; lmp->last_fully_vis = 0; for ( i = 0, pix_offsetsp = &lmp->pix_offsets[i], pix_heightsp = &lmp->pix_heights[i]; i < lmp->nbr_realized_rows; ++i, ++pix_offsetsp, ++pix_heightsp ) { if ( !have_first && *pix_offsetsp >= ( -lmp->rrr_offset ) ) { have_first = TRUE; lmp->first_fully_vis = i; } /* should be <, because we are dealing with mathematical rects, but we * don't care if the rule is not on the list. pix_offsetp + *pix_heightsp * is the mathematical rect of the row, in rrr terms. lmp->rrr_offset + * lmp->mlr_height is the mathematical rect of the list, in rrr terms. */ #if XIWS == XIWS_WM if ( *pix_offsetsp + *pix_heightsp + lmp->rrr_offset <= lmp->mlr_height ) lmp->last_fully_vis = i; #else if ( *pix_offsetsp + *pix_heightsp - 1 + lmp->rrr_offset <= lmp->mlr_height ) lmp->last_fully_vis = i; #endif } if ( lmp->nbr_realized_rows ) { idx = lmp->nbr_realized_rows - 1; lmp->rrr_bottom = lmp->pix_offsets[idx] + lmp->pix_heights[idx]; } else lmp->rrr_bottom = 0; } /*------------------------------------------------------------------------- function: calculate_pix_offsets lmp: current lmp process: calculates the lmp->pix_offsets array calculates lmp->first_fully_vis calculates lmp->last_fully_vis -------------------------------------------------------------------------*/ void calculate_pix_offsets( LM_DATA * lmp, BOOLEAN draw_changes ) { int i, cur_pix, idx; int *pix_offsetsp; int *pix_heightsp; BOOLEAN have_first; cur_pix = 0; have_first = FALSE; for ( i = 0, pix_offsetsp = &lmp->pix_offsets[i], pix_heightsp = &lmp->pix_heights[i]; i < lmp->nbr_realized_rows; ++i, ++pix_offsetsp, ++pix_heightsp ) { int new_height; new_height = lm_calculate_row_height( lmp, i ); if ( draw_changes && ( *pix_offsetsp != cur_pix || *pix_heightsp != new_height ) ) { XinRect rect; lm_get_row_rect( &rect, ( LM ) lmp, i ); lm_invalidate_rect( lmp, &rect, FALSE ); rect.top = cur_pix; rect.bottom = cur_pix + new_height; lm_invalidate_rect( lmp, &rect, FALSE ); } *pix_offsetsp = cur_pix; *pix_heightsp = new_height; if ( !have_first && cur_pix >= ( -lmp->rrr_offset ) ) { have_first = TRUE; lmp->first_fully_vis = i; } cur_pix += new_height; /* should be <=, because we are dealing with mathematical rects, but we * don't care if the rule is not on the list. pix_offsetp + *pix_heightsp * is the mathematical rect of the row, in rrr terms. lmp->rrr_offset + * lmp->mlr_height is the mathematical rect of the list, in rrr terms. */ #if XIWS == XIWS_WM if ( cur_pix + lmp->rrr_offset <= lmp->mlr_height ) lmp->last_fully_vis = i; #else if ( cur_pix - 1 + lmp->rrr_offset <= lmp->mlr_height ) lmp->last_fully_vis = i; #endif } if ( lmp->nbr_realized_rows ) { idx = lmp->nbr_realized_rows - 1; lmp->rrr_bottom = lmp->pix_offsets[idx] + lmp->pix_heights[idx]; } else lmp->rrr_bottom = 0; } /*------------------------------------------------------------------------- function: make_rrr_room_pix lmp: current lmp pixels: number of additional pixels desired in realized row rect pixels may be negative, in which case, the desired pixels are above the realized row rect pixels may be positive, in which case, the desired pixels are below the realized row rect returns: recs read -------------------------------------------------------------------------*/ int lm_make_rrr_room_pix( LM_DATA * lmp, int pixels, BOOLEAN do_redraw ) { int recs_read = 0; int idx; if ( lmp->nbr_realized_rows == 0 || lmp->nbr_columns == 0 ) { lmp->rrr_bottom = 0; calculate_pix_offsets( lmp, FALSE ); return 0; } idx = lmp->nbr_realized_rows - 1; lmp->rrr_bottom = lmp->pix_offsets[idx] + lmp->pix_heights[idx]; if ( pixels == 0 ) return recs_read; if ( pixels < 0 ) { while ( TRUE ) { if ( pixels >= lmp->rrr_offset ) return recs_read; if ( get_previous_rec( lmp ) ) { int row_height; row_height = lm_calculate_row_height( lmp, 0 ); lmp->rrr_offset -= row_height; calculate_pix_offsets( lmp, do_redraw ); calculate_visibles( lmp ); ++recs_read; } else return recs_read; } } else { while ( TRUE ) { if ( lmp->rrr_bottom - lmp->mlr_height + lmp->rrr_offset >= pixels ) return recs_read; if ( lmp->get_all_records ) return recs_read; if ( get_next_rec( lmp, FALSE ) ) { calculate_pix_offsets( lmp, do_redraw ); calculate_visibles( lmp ); ++recs_read; } else return recs_read; } } } /*------------------------------------------------------------------------- function: make_rrr_room_rows lmp: current lmp nbr_rows: nbr of rows to scroll returns: pixels to scroll -------------------------------------------------------------------------*/ static int make_rrr_room_rows( LM_DATA * lmp, int *rows, BOOLEAN do_redraw ) { int nbr_rows = *rows; if ( nbr_rows < 0 ) { /* convert nbr_rows to positive, for ease of programming */ nbr_rows = -nbr_rows; while ( TRUE ) { if ( !nbr_rows ) { *rows = 0; return 0; } if ( lmp->first_fully_vis - nbr_rows >= 0 ) { int pixels; pixels = -( lmp->pix_offsets[lmp->first_fully_vis] - lmp->pix_offsets[lmp->first_fully_vis - nbr_rows] ); lmp->update_rows_at_top = nbr_rows; *rows = nbr_rows; return pixels; } else { if ( !lmp->get_all_records && get_previous_rec( lmp ) ) { lmp->pix_heights[0] = lm_calculate_row_height( lmp, 0 ); lmp->rrr_offset -= lmp->pix_heights[0]; calculate_pix_offsets( lmp, do_redraw ); } else { calculate_pix_offsets( lmp, do_redraw ); nbr_rows = lmp->first_fully_vis; } } } } else { BOOLEAN get_next_accepted = TRUE; int pixels; pixels = lmp->pix_offsets[lmp->first_fully_vis + nbr_rows] - lmp->pix_offsets[lmp->first_fully_vis]; while ( TRUE ) { int delta_avail; if ( !nbr_rows ) { *rows = 0; return 0; } /* if get_all_records is true, then we never need to get more records if * there is a row beyond the last partially visible row, then we don't * need to get more records if a get next has been refused, then we have * all the records that we are going to get */ if ( lmp->get_all_records || lmp->last_fully_vis + 1 + nbr_rows < lmp->nbr_realized_rows || !get_next_accepted ) { delta_avail = lmp->pix_offsets[lmp->nbr_realized_rows - 1] + lmp->pix_heights[lmp->nbr_realized_rows - 1] + lmp->rrr_offset - lmp->mlr_height; if ( lmp->get_all_records || pixels <= delta_avail || !get_next_accepted ) { int nrows, cnt, pcnt; for ( nrows = 0, cnt = lmp->last_fully_vis + 1, pcnt = 0; pcnt < pixels && cnt < lmp->nbr_realized_rows; ++nrows, ++cnt, pcnt += lmp->pix_heights[cnt] ) ; lmp->update_rows_at_bottom = nrows; *rows = nbr_rows; return pixels; } } if ( !lmp->get_all_records && ( get_next_accepted = get_next_rec( lmp, FALSE ) ) != 0 ) calculate_pix_offsets( lmp, do_redraw ); else { calculate_pix_offsets( lmp, do_redraw ); nbr_rows = lmp->nbr_realized_rows - 1 - lmp->last_fully_vis; } } } } static int make_room_rows( LM_DATA * lm_data, int rows, BOOLEAN do_draw ) { return make_rrr_room_rows( lm_data, &rows, do_draw ); } /*------------------------------------------------------------------------- function: do_scroll_bar itf: current itf listdata: list_data list: list -------------------------------------------------------------------------*/ void do_scroll_bar( XI_LIST_DATA * listdata ) { LM_DATA *lmp; if ( listdata->scroll_bar ) { lmp = ( LM_DATA * ) listdata->lm; if ( lmp->get_all_records ) { int top, prop; if ( lmp->rrr_offset == 0 && lmp->rrr_bottom < lmp->mlr_height ) { top = 0; prop = 100; } else { top = ( int ) ( -lmp->rrr_offset * 100L / lmp->rrr_bottom ); if ( lmp->mlr_height - lmp->rrr_offset >= lmp->rrr_bottom - 1 ) prop = 100 - top; else prop = ( int ) ( lmp->mlr_height * 100L / lmp->rrr_bottom ); } prop = max( 0, min( 100, prop ) ); XinScrollBarSet( listdata->sb_win, XinScrollBarTypeEither, 0, 100, prop, top ); } else { int percent1 = 0; int percent2 = 100; int prop; if ( lmp->nbr_realized_rows > 1 ) { do_lm_cb( listdata->lm, LM_CB_GET_PERCENT, lmp->first_fully_vis, 0, NULL, &percent1, 0 ); do_lm_cb( listdata->lm, LM_CB_GET_PERCENT, lmp->last_fully_vis, 0, NULL, &percent2, 0 ); } prop = min( 100, max( 1, ( percent2 - percent1 ) ) ); XinScrollBarSet( listdata->sb_win, XinScrollBarTypeEither, 0, 100, prop, percent1 ); } } } /*------------------------------------------------------------------------- function: lm_remove_all_rows lm: current lm -------------------------------------------------------------------------*/ void lm_remove_all_rows( LM_DATA * lmp, BOOLEAN delete_focus ) { int i; LM_CB_DATA lm_cb_data; if ( lm_focus_rec_get( lmp ) && delete_focus ) { int idx; BOOLEAN found = FALSE; for ( idx = 0; idx < lmp->nbr_realized_rows; ++idx ) { if ( lm_focus_rec_get( lmp ) == lmp->recs[idx] ) { found = TRUE; break; } } if ( !found ) { lm_cb_data.lm = ( LM ) lmp; lm_cb_data.cb_type = LM_CB_REC_FREE; lm_cb_data.cid = lmp->cid; lm_cb_data.win = lmp->win; lm_cb_data.v.rec_free.record = lm_focus_rec_get( lmp ); ( *lmp->lm_cb ) ( &lm_cb_data ); } } for ( i = 0; i < lmp->nbr_realized_rows; ++i ) lm_do_rec_event( lmp, i, XIE_REC_FREE ); lmp->nbr_realized_rows = 0; lmp->rrr_bottom = 0; lmp->rrr_offset = 0; } BOOLEAN lm_row_has_focus( LM_DATA * lmp, int row, BOOLEAN is_vert_scrolled ) { XI_ITF_DATA *itf; XI_OBJ *focus_obj; XI_CELL_DATA *cd; #if 0 if ( lmp->txt_is_invisible && !lmp->horizontally_scrolling_list ) return FALSE; #endif itf = lmp->itf_obj->v.itf; focus_obj = itf->focus_obj; if ( focus_obj != NULL && focus_obj->parent == lmp->list_obj && focus_obj->type == XIT_CELL ) { cd = &focus_obj->v.cell; return ( cd->row == row && cd->is_vert_scrolled == is_vert_scrolled ); } return FALSE; } /*------------------------------------------------------------------------- function: fill_previous returns: # of records read -------------------------------------------------------------------------*/ static int fill_previous( LM_DATA * lmp ) { int *pix_heights, *pix_offsets; int offset, height; int i; int result = 0; while ( TRUE ) { if ( !get_previous_rec( lmp ) ) break; result++; /* recalculate the heights and offsets of every row */ lmp->pix_heights[0] = lm_calculate_row_height( lmp, 0 ); offset = 0; for ( i = 0, pix_heights = &lmp->pix_heights[0], pix_offsets = &lmp->pix_offsets[0]; i < lmp->nbr_realized_rows; ++i, ++pix_heights, ++pix_offsets ) { *pix_offsets = offset; offset += *pix_heights; } calculate_visibles( lmp ); /* if we finally have enough rows to display the entire list * * note: we subtract 1 from the space necessary because it is not necessary to * create space for the pixel at the bottom of the last row. */ height = lmp->pix_heights[lmp->nbr_realized_rows - 1]; if ( !height ) height = lmp->pix_row_spacing; if ( !lmp->get_all_records && ( lmp->pix_offsets[lmp->nbr_realized_rows - 1] + height - 1 >= lmp->mlr_height ) ) break; } calculate_pix_offsets( lmp, FALSE ); lmp->rrr_bottom = lmp->pix_offsets[lmp->nbr_realized_rows - 1] + lmp->pix_heights[lmp->nbr_realized_rows - 1]; /* subtract 1 from rrr_bottom because of the pixel at the bottom of the last * row. */ lmp->rrr_offset = lmp->mlr_height - ( lmp->rrr_bottom - 1 ); lmp->rrr_offset = min( 0, lmp->rrr_offset ); /* although we have adjusted pix_offsets, and pix_heights, it is still * necessary to calculate the first and last fully visible rows */ calculate_visibles( lmp ); /* if the page down does not make the first row editable, then scroll the * exact amount to make it editable */ if ( -lmp->rrr_offset != lmp->pix_offsets[lmp->first_fully_vis] ) { lmp->rrr_offset = -lmp->pix_offsets[lmp->first_fully_vis]; calculate_visibles( lmp ); } return result; } /*-------------------------------------------------------------------------*/ /* data structure for passing around lm_scroll functions */ /*-------------------------------------------------------------------------*/ typedef struct { int nbr_lines; int percent; long rec; BOOLEAN have_rec; XinColor color; unsigned long attrib; int row_height_arg; int scroll_type; int pix_overlap; int nbr_pixels; XinRect focus_cell_rct; BOOLEAN have_pixels; BOOLEAN list_had_focus; BOOLEAN is_first_or_last; int focus_row; int focus_column; BOOLEAN v_scrolled; int pixels_scrolled; } lm_scroll_data; /*-------------------------------------------------------------------------*/ /* lm_scroll_data_construct */ /*-------------------------------------------------------------------------*/ static void lm_scroll_data_construct( LM_DATA * lmp, lm_scroll_data * data, LM_SCROLL_ARG * arg ) { int pref_overlap; data->nbr_lines = arg->nbr_lines; data->percent = arg->percent; data->rec = arg->rec; data->have_rec = arg->have_rec; data->color = arg->color; data->attrib = arg->attrib; data->row_height_arg = arg->row_height; data->have_pixels = FALSE; data->list_had_focus = FALSE; data->is_first_or_last = FALSE; if ( arg->nbr_lines == XI_SCROLL_FIRST && arg->percent == 100 ) data->nbr_lines = XI_SCROLL_LAST; if ( !lmp->nbr_realized_rows ) data->nbr_lines = XI_SCROLL_FIRST; if ( arg->have_rec ) { if ( arg->rec_at_top ) data->nbr_lines = XI_SCROLL_FIRST; else data->nbr_lines = XI_SCROLL_LAST; } data->scroll_type = data->nbr_lines; pref_overlap = ( int ) xi_get_pref( XI_PREF_OVERLAP ); data->pix_overlap = pref_overlap * lmp->pix_row_spacing; data->pixels_scrolled = 0; } /*-------------------------------------------------------------------------*/ /* lm_scroll_remove focus */ /*-------------------------------------------------------------------------*/ static BOOLEAN lm_scroll_remove_focus( LM_DATA * lmp, lm_scroll_data * data, XI_OBJ * old_focus_obj ) { /* remove the focus from the list being scrolled */ if ( lm_focus_list_has( lmp ) ) { data->list_had_focus = TRUE; lm_focus_cell_get( lmp, &data->focus_row, &data->focus_column, &data->v_scrolled ); if ( xi_get_pref( XI_PREF_KEEP_FOCUS_FIXED ) ) { if ( lm_focus_state_get( lmp ) == LM_FOCUS_VISIBLE ) { lm_focus_cell_invis_make( lmp ); switch ( data->nbr_lines ) { case XI_SCROLL_FIRST: lm_focus_rec_is_above_set( lmp, FALSE ); case XI_SCROLL_LAST: lm_focus_rec_is_above_set( lmp, TRUE ); break; case XI_SCROLL_PGUP: lm_focus_rec_is_above_set( lmp, FALSE ); break; case XI_SCROLL_PGDOWN: lm_focus_rec_is_above_set( lmp, TRUE ); break; default: lm_focus_rec_is_above_set( lmp, ( BOOLEAN ) ( data->nbr_lines > 0 ) ); break; } } } else { xi_get_rect( old_focus_obj, &data->focus_cell_rct ); if ( !xi_move_focus( lmp->itf_obj ) ) return FALSE; if (data->nbr_lines == XI_SCROLL_FIRST || data->nbr_lines == XI_SCROLL_LAST) { /* The old data went away, so the old focus location is no longer valid. */ lmp->list_obj->v.list->focus_cell->v.cell.row = 0; lmp->list_obj->v.list->focus_cell->v.cell.column = 0; lmp->list_obj->v.list->focus_cell->v.cell.is_vert_scrolled = FALSE; } } } return TRUE; } /*------------------------------------------------------------------------- */ /* lm_scroll_calc_lines */ /* if nbr_lines is not an integral, and instead is one of XI_SCROLL_FIRST, */ /* XI_SCROLL_LAST, XI_SCROLL_PGUP, or XI_SCROLL_PGDOWN, then calculate */ /* the number of lines. */ /*------------------------------------------------------------------------- */ static void lm_scroll_calc_lines( LM_DATA * lmp, lm_scroll_data * data ) { switch ( data->nbr_lines ) { case XI_SCROLL_FIRST: case XI_SCROLL_LAST: data->is_first_or_last = TRUE; break; case XI_SCROLL_PGUP: #if XIWS == XIWS_WM data->nbr_pixels = -( lmp->mlr_height - data->pix_overlap ); #else data->nbr_pixels = -( lmp->mlr_height - data->pix_overlap + 1 ); #endif data->have_pixels = TRUE; break; case XI_SCROLL_PGDOWN: #if XIWS == XIWS_WM data->nbr_pixels = lmp->mlr_height - data->pix_overlap; #else data->nbr_pixels = lmp->mlr_height - data->pix_overlap + 1; #endif data->have_pixels = TRUE; break; } } /*------------------------------------------------------------------------- */ /* lm_scroll_get_initial_rec */ /*------------------------------------------------------------------------- */ static BOOLEAN lm_scroll_get_initial_rec( LM_DATA * lmp, lm_scroll_data * data, int record_location, LM_CB_TYPE event_type ) { int row_height; BOOLEAN refused; /* first, get rid of all rows, except the focus row */ lm_remove_all_rows( lmp, FALSE ); make_rec_available( lmp, ( BOOLEAN ) record_location, ( BOOLEAN ) ! data->have_rec, TRUE ); if ( data->have_rec ) { LM_CELL_DATA *cell_data; int i; lmp->recs[0] = data->rec; lmp->row_colors[0] = data->color; lmp->row_attribs[0] = data->attrib; row_height = data->row_height_arg; refused = FALSE; /* cell_data_construct */ cell_data = lmp->cell_data[0]; for ( i = 0; i < lmp->nbr_columns; ++i, ++cell_data ) { memset( ( char * ) cell_data, '\0', sizeof( LM_CELL_DATA ) ); lm_xi_text_construct( lmp, 0, i ); } } else { refused = do_lm_cb_get( ( LM ) lmp, event_type, &lmp->recs[0], &lmp->recs[0], data->percent, &lmp->row_colors[0], &lmp->row_attribs[0], &row_height ); if ( refused ) { lm_do_rec_event( lmp, lmp->nbr_realized_rows - 1, XIE_REC_FREE ); --lmp->nbr_realized_rows; calculate_visibles( lmp ); } } if ( !refused ) { if ( row_height ) { lmp->pix_heights[0] = row_height; lmp->set_heights[0] = TRUE; } else { lmp->pix_heights[0] = 0; lmp->set_heights[0] = FALSE; } lmp->pix_offsets[0] = 0; lmp->pix_heights[0] = lm_calculate_row_height( lmp, 0 ); lm_invalidate_rows_internal( ( LM ) lmp, 0, 0, FALSE, -1, TRUE ); } return !refused; } /*------------------------------------------------------------------------- */ /* lm_scroll_fill_forward */ /* returns # of records read if fill previous called, otherwise 0 */ /*------------------------------------------------------------------------- */ static int lm_scroll_fill_forward( LM_DATA * lmp, BOOLEAN have_rec ) { BOOLEAN refused; while ( TRUE ) { int idx; refused = !get_next_rec( lmp, TRUE ); if ( refused ) break; idx = lmp->nbr_realized_rows - 1; lmp->pix_heights[idx] = lm_calculate_row_height( lmp, idx ); if ( idx > 0 ) lmp->pix_offsets[idx] = lmp->pix_offsets[idx - 1] + lmp->pix_heights[idx - 1]; else lmp->pix_offsets[idx] = 0; /* if we finally have enough rows to display the entire list */ if ( !lmp->get_all_records && ( lmp->pix_offsets[idx] + lmp->pix_heights[idx] >= lmp->mlr_height ) ) break; } /* although we have adjusted pix_offsets, and pix_heights, it is still * necessary to calculate the first and last fully visible rows */ calculate_visibles( lmp ); /* If we have the first record and the list is not full, try getting previous * records. */ if ( have_rec && refused ) return fill_previous( lmp ); return 0; } /*------------------------------------------------------------------------- */ /* lm_scroll_first */ /* returns row # of first record requested - i.e. for xi_scroll_rec, the */ /* row # of the new record. for scroll first with a percentage, the */ /* row # of that percentage. Normally 0, but with back-fill, can be */ /* a positive number. */ /*------------------------------------------------------------------------- */ static int lm_scroll_first( LM_DATA * lmp, lm_scroll_data * data ) { int result = 0; if ( lmp->get_all_records && lmp->nbr_realized_rows > 0 ) { /* have_rec */ lmp->rrr_offset = -( int ) ( ( long ) lmp->rrr_bottom * data->percent / 100 ); calculate_visibles( lmp ); lmp->rrr_offset = -lmp->pix_offsets[lmp->first_fully_vis]; } else { if ( lm_scroll_get_initial_rec( lmp, data, REC_AT_BOTTOM, LM_CB_GET_FIRST ) ) result = lm_scroll_fill_forward( lmp, data->have_rec ); if ( !lmp->get_all_records ) { int nbr_to_free, cnt, idx; nbr_to_free = lmp->first_fully_vis - 1; if ( nbr_to_free > 0 ) { for ( cnt = 0; cnt < nbr_to_free; cnt++ ) { lmp->rrr_offset += lmp->pix_heights[cnt]; lm_do_rec_event( lmp, cnt, XIE_REC_FREE ); } for ( idx = nbr_to_free; idx < lmp->nbr_realized_rows; ++idx ) lm_row_copy( lmp, idx, idx - nbr_to_free ); result -= nbr_to_free; lm_adjust_rows( lmp, -nbr_to_free ); lmp->nbr_realized_rows -= nbr_to_free; } /* free any unnecessary records at the end of the list */ nbr_to_free = lmp->nbr_realized_rows - ( lmp->last_fully_vis + 2 ); for ( idx = lmp->nbr_realized_rows - nbr_to_free, cnt = 0; cnt < nbr_to_free; idx++, cnt++ ) lm_do_rec_event( lmp, idx, XIE_REC_FREE ); lmp->nbr_realized_rows -= cnt; calculate_pix_offsets( lmp, FALSE ); { XI_OBJ *focus_obj; focus_obj = lmp->itf_obj->v.itf->focus_obj; if ( focus_obj && focus_obj->cid == lmp->cid ) { if ( focus_obj && focus_obj->type == XIT_CELL ) { focus_obj->v.cell.is_vert_scrolled = TRUE; } } } } } return result; } /*------------------------------------------------------------------------- */ /* lm_scroll_last */ /* returns row # of requested row */ /*------------------------------------------------------------------------- */ static int lm_scroll_last( LM_DATA * lmp, lm_scroll_data * data ) { /* scroll last */ int result = 0; if ( lmp->get_all_records ) { if ( lmp->rrr_bottom < lmp->mlr_height ) lmp->rrr_offset = 0; else #if XIWS == XIWS_WM lmp->rrr_offset = lmp->mlr_height - lmp->rrr_bottom; #else lmp->rrr_offset = lmp->mlr_height - lmp->rrr_bottom + 1; #endif calculate_visibles( lmp ); /* if the page up does not make the first row editable, then scroll the * exact amount to make it editable */ if ( -lmp->rrr_offset != lmp->pix_offsets[lmp->first_fully_vis] ) { int p; p = lmp->pix_offsets[lmp->first_fully_vis] + lmp->rrr_offset; data->nbr_pixels += p; lmp->rrr_offset -= p; calculate_visibles( lmp ); } } else { data->percent = 0; if ( lm_scroll_get_initial_rec( lmp, data, REC_AT_TOP, LM_CB_GET_LAST ) ) { result = fill_previous( lmp ); /* get one record after the last record, so that there will not be a gray * space below the last record, unless there are no more records. */ get_next_rec( lmp, FALSE ); calculate_pix_offsets( lmp, FALSE ); } } return result; } /*------------------------------------------------------------------------- */ /* lm_scroll_calc_pixels */ /*------------------------------------------------------------------------- */ static void lm_scroll_calc_pixels( LM_DATA * lmp, lm_scroll_data * data ) { /* calculate the max number of pixels that we should scroll */ if ( data->nbr_pixels < 0 ) { int p; data->nbr_pixels = max( data->nbr_pixels, lmp->rrr_offset ); lmp->rrr_offset -= data->nbr_pixels; calculate_visibles( lmp ); /* if the page up does not make the first row editable, then scroll the * exact amount to make it editable */ if ( -lmp->rrr_offset != lmp->pix_offsets[lmp->first_fully_vis] ) { p = lmp->pix_offsets[lmp->first_fully_vis - 1] + lmp->rrr_offset; data->nbr_pixels += p; lmp->rrr_offset -= p; calculate_visibles( lmp ); } } else { int p; calculate_pix_offsets( lmp, FALSE ); /* following line puts row coming onto the list at the bottom of the list * completly on the list. new behavior is to have the row at the top of * the list to always be completely on the list. nbr_pixels = * min(nbr_pixels, lmp->rrr_bottom - lmp->mlr_height + lmp->rrr_offset); */ data->nbr_pixels = max( data->nbr_pixels, 0 ); lmp->rrr_offset -= data->nbr_pixels; calculate_visibles( lmp ); /* if the page down does not make the first row editable, then scroll the * exact amount to make it editable */ if ( -lmp->rrr_offset != lmp->pix_offsets[lmp->first_fully_vis] ) { p = lmp->pix_offsets[lmp->first_fully_vis - 1] + lmp->rrr_offset; data->nbr_pixels += p; lmp->rrr_offset -= p; calculate_visibles( lmp ); } /* if the page down leaves too much room at the bottom, then make more rows * visible. */ while ( TRUE ) { int h, pix_left; if ( lmp->first_fully_vis == 0 ) break; h = lmp->pix_heights[lmp->first_fully_vis - 1]; /* Add 1 to pix_left (pixels left in mlr), because it is ok for the pixel * required for the rule at the bottom of the row to fall below the * visible portion of the list. */ pix_left = -( lmp->pix_offsets[lmp->nbr_realized_rows - 1] + lmp->pix_heights[lmp->nbr_realized_rows - 1] + lmp->rrr_offset - lmp->mlr_height ) + 1; if ( pix_left < h ) break; data->nbr_pixels -= h; lmp->rrr_offset += h; calculate_visibles( lmp ); } } } /*------------------------------------------------------------------------- function: lm_get_scroll_rct lmp: current lmp r: pointer to rectangle to be filled in returns: r -------------------------------------------------------------------------*/ XinRect * lm_get_scroll_rct( LM_DATA * lmp, XinRect * r ) { *r = lmp->mlr; if ( lmp->pixel_width ) r->right = r->left + lmp->pixel_width + BORDER_WIDTH - 1; return r; } /*------------------------------------------------------------------------- */ /* lm_scroll_middle */ /*------------------------------------------------------------------------- */ static int lm_scroll_middle( LM_DATA * lmp, lm_scroll_data * data, BOOLEAN invalidate ) { int recs_read; recs_read = 0; if ( data->have_pixels ) recs_read = lm_make_rrr_room_pix( lmp, data->nbr_pixels, FALSE ); else if ( data->nbr_lines ) { recs_read = data->nbr_lines; data->nbr_pixels = make_rrr_room_rows( lmp, &recs_read, FALSE ); } lm_scroll_calc_pixels( lmp, data ); if ( !lmp->get_all_records ) { int nbr_to_free, cnt, idx; nbr_to_free = lmp->first_fully_vis; if ( nbr_to_free ) { for ( cnt = 0; cnt < nbr_to_free; cnt++ ) { lmp->rrr_offset += lmp->pix_heights[cnt]; lm_do_rec_event( lmp, cnt, XIE_REC_FREE ); } for ( cnt = nbr_to_free; cnt < lmp->nbr_realized_rows; ++cnt ) lm_row_copy( lmp, cnt, cnt - nbr_to_free ); lm_adjust_rows( lmp, -nbr_to_free ); lmp->nbr_realized_rows -= nbr_to_free; } calculate_pix_offsets( lmp, FALSE ); /* free any unnecessary records at the end of the list */ nbr_to_free = lmp->nbr_realized_rows - ( lmp->last_fully_vis + 2 ); for ( idx = lmp->nbr_realized_rows - nbr_to_free, cnt = 0; cnt < nbr_to_free; idx++, cnt++ ) lm_do_rec_event( lmp, idx, XIE_REC_FREE ); lmp->nbr_realized_rows -= cnt; for ( idx = lmp->nbr_realized_rows; idx < lmp->realized_rows_array_len; ++idx ) init_row( lmp, idx ); calculate_pix_offsets( lmp, FALSE ); } if ( invalidate ) { XinRect r; lm_get_scroll_rct( lmp, &r ); xi_set_clip( lmp->win, NULL ); if ( data->nbr_pixels ) { XinWindowPaintForce( lmp->win ); xi_set_update_obj( lmp->list_obj ); lmp->update_cells_only = TRUE; xi_scroll_rect( lmp->win, &r, 0, -data->nbr_pixels ); data->pixels_scrolled = data->nbr_pixels; } } return recs_read; } /*------------------------------------------------------------------------- */ /* lm_scroll_replace_focus */ /*------------------------------------------------------------------------- */ static void lm_scroll_replace_focus( LM_DATA * lmp, lm_scroll_data * data, XI_OBJ * old_focus_obj, BOOLEAN same_cell ) { /* put the focus back */ if ( data->list_had_focus ) { if ( old_focus_obj->type == XIT_CELL ) { if ( xi_get_pref( XI_PREF_KEEP_FOCUS_FIXED ) ) lm_focus_cell_visible_attempt( lmp ); else { if ( same_cell ) { int col = old_focus_obj->v.cell.column; int row, t, b; int max_intersect, intersect_row; XI_OBJ cell; XinRect cell_rct; int tr; max_intersect = 0; intersect_row = -1; tr = min( lmp->nbr_realized_rows, lmp->last_fully_vis + 1 ); for ( row = lmp->first_fully_vis; row < tr; ++row ) { XI_MAKE_CELL( &cell, lmp->list_obj, ( unsigned char ) row, ( unsigned char ) col ); xi_get_rect( &cell, &cell_rct ); t = max( cell_rct.top, data->focus_cell_rct.top ); b = min( cell_rct.bottom, data->focus_cell_rct.bottom ); if ( ( b - t ) > max_intersect ) { max_intersect = b - t; intersect_row = row; } } if ( intersect_row != -1 ) { XI_MAKE_CELL( &cell, lmp->list_obj, ( unsigned char ) intersect_row, ( unsigned char ) col ); xi_move_focus( &cell ); } } else { switch ( data->scroll_type ) { case XI_SCROLL_FIRST: case XI_SCROLL_PGUP: data->focus_row = lmp->first_fully_vis; break; case XI_SCROLL_LAST: case XI_SCROLL_PGDOWN: data->focus_row = lmp->last_fully_vis; break; default: data->focus_row = clip( data->focus_row, lmp->first_fully_vis, lmp->last_fully_vis ); break; } } } } else xi_move_focus( old_focus_obj ); } } /*------------------------------------------------------------------------- function: lm_scroll lm: current lm nbr_lines: nbr of lines to scroll, may be positive or negative, may be set to XI_SCROLL_FIRST, XI_SCROLL_LAST, XI_SCROLL_PGUP, or XI_SCROLL_PGDN percent: passed with XI_SCROLL_FIRST event same_cell: sometimes the focus goes back onto the same cell. In other cases, for instance, XI_SCROLL_FIRST, XI_SCROLL_LAST, XI_SCROLL_PGUP, and XI_SCROLL_PGDN, we don't want to put the focus back on the same cell, but instead, want to put the focus on the first or last fully visible row. invalidate: indicates whether to invalidate the list. This is only set to FALSE when xi_scroll_internal is called when the interface is created. rec: if have_rec is TRUE, then use rec for the first record, and don't do get first -------------------------------------------------------------------------*/ int lm_scroll( LM_SCROLL_ARG * arg ) { lm_scroll_data data; int result = 0; XI_OBJ *old_focus_obj; LM_DATA *lmp = ( LM_DATA * ) arg->lm; XI_OBJ *itf = lmp->itf_obj; BOOLEAN invalidate = !itf->v.itf->half_baked; BOOLEAN v_scrolled, focus; int new_row_height; int row, column; if ( ( xi_get_attrib( lmp->list_obj ) & XI_ATR_VISIBLE ) == 0 ) invalidate = FALSE; lm_scroll_data_construct( lmp, &data, arg ); /* If the focus is on the first row, and scrolling up, or the focus is on the * last row and scrolling down, return 0. */ if ( lmp->get_all_records ) { if ( lmp->first_fully_vis == 0 && data.nbr_lines < 0 ) return 0; if ( lmp->last_fully_vis == lmp->nbr_realized_rows && data.nbr_lines > 0 && data.nbr_lines < XI_SCROLL_PGUP ) return 0; } if ( !lmp->itf_obj->v.itf->half_baked && invalidate ) XinWindowPaintForce( lmp->win ); old_focus_obj = itf->v.itf->focus_obj; if ( lm_focus_state_get( lmp ) == LM_FOCUS_VISIBLE ) { focus = lm_focus_cell_get( lmp, &row, &column, &v_scrolled ); if ( focus && !v_scrolled ) { new_row_height = lm_calculate_row_height( lmp, row ); if ( new_row_height != lmp->pix_heights[row] ) lm_set_row_height( ( LM ) lmp, row, new_row_height, FALSE, 0, FALSE ); } } if ( !lm_scroll_remove_focus( lmp, &data, old_focus_obj ) ) return 0; lm_scroll_calc_lines( lmp, &data ); if ( data.is_first_or_last ) { if ( data.nbr_lines == XI_SCROLL_FIRST ) result = lm_scroll_first( lmp, &data ); else result = lm_scroll_last( lmp, &data ); if ( invalidate ) { XinRect r; xi_invalidate_rect( lmp->win, lm_get_scroll_rct( lmp, &r ) ); } } else { result = lm_scroll_middle( lmp, &data, invalidate ); arg->pixels_scrolled = data.pixels_scrolled; } lm_scroll_replace_focus( lmp, &data, old_focus_obj, arg->same_cell ); if ( lmp->row_focus_border && lmp->nbr_realized_rows > 0 ) { int focus_row, focus_column; BOOLEAN is_vert_scrolled; BOOLEAN is_hscrolled; BOOLEAN is_vis; lm_focus_cell_get( lmp, &focus_row, &focus_column, &is_vert_scrolled ); is_vis = lm_focus_cell_is_visible( lmp, &is_hscrolled ); if ( ( is_vis || is_hscrolled ) && !is_vert_scrolled ) { lm_redraw_row( lmp, focus_row, FALSE ); if ( lm_focus_state_get( lmp ) == LM_FOCUS_VISIBLE ) set_focus_cell_rct( lmp, focus_row, focus_column, FALSE ); } /* is vis */ } /* focus border */ do_scroll_bar( lmp->list_obj->v.list ); if ( invalidate ) XinWindowPaintForce( lmp->win ); return result; } /*------------------------------------------------------------------------- function: lm_set_text lm: current lm s: string to set row: column: -------------------------------------------------------------------------*/ void lm_set_text( LM lm, char *s, int row, int column, BOOLEAN v_scrolled ) { BOOLEAN was_suspended = FALSE; BOOLEAN preserve_focus_text; LM_COLUMN_DATA *lmcd; XinRect rct; LM_DATA *lmp = LMP( lm ); lmcd = lmp->lm_column_data[column]; if ( row == LM_HEADING_TEXT ) { lmcd->heading_text = ( char * ) xi_tree_realloc( lmcd->heading_text, strlen( s ) + 1 ); strcpy( lmcd->heading_text, s ); lm_get_cell_rect( &rct, lm, 1, column, FALSE, TRUE ); rct.top = lmp->pix_hdr_bottom - max( lmp->pix_cell_height, lmp->min_heading_height + 2 * BORDER_WIDTH ); rct.bottom = lmp->pix_hdr_bottom; if ( ( lmp->attrib & LM_ATR_VISIBLE ) != 0 ) xi_invalidate_rect( lmp->win, &rct ); } else { int new_row_height; LM_CELL_DATA *cell_data; cell_data = &lmp->cell_data[row][column]; preserve_focus_text = FALSE; if ( lm_focus_cell_has( lmp, row, column, v_scrolled ) && lmp->itf_obj->v.itf->chg_flag ) { was_suspended = TRUE; lm_focus_cell_invis_make( lmp ); preserve_focus_text = TRUE; } /* If the cell has focus the XI_TEXT string needs updated */ if ( lm_focus_cell_has( lmp, row, column, v_scrolled ) ) { xi_text_set( cell_data->xi_text, s ); } else if ( !v_scrolled ) xi_text_set( cell_data->xi_text, s ); if ( lm_focus_state_get( lmp ) == LM_FOCUS_VISIBLE ) lm_focus_cell_text_set( lmp, preserve_focus_text, s, row, column, v_scrolled ); if ( !v_scrolled ) { new_row_height = lm_calculate_row_height( lmp, row ); if ( new_row_height != lmp->pix_heights[row] ) lm_set_row_height( lm, row, new_row_height, FALSE, 0, FALSE ); if ( LMP( lm )->attrib & XI_ATR_VISIBLE && LMP( lm )->cell_data[row][column].valid_data ) redraw_cell( lm, row, column, FALSE ); } if ( was_suspended ) lm_focus_cell_visible_attempt( lmp ); } } /*------------------------------------------------------------------------- function: next cell lm: current lm c: character causing the focus to change next_row: int pointer to be filled in with next row next_col: int pointer to be filled in with next column -------------------------------------------------------------------------*/ static void next_cell( LM lm, int c, int *next_row, int *next_col, BOOLEAN v_scrolled ) { LM_DATA *lmp = LMP( lm ); switch ( c ) { case XI_KEY_BTAB: --*next_col; if ( *next_col < 0 ) { *next_col = lmp->nbr_columns - 1; if ( ( lmp->attrib & LM_ATR_TABWRAP ) ) --* next_row; } break; case '\t': ++*next_col; if ( *next_col >= lmp->nbr_columns ) { *next_col = 0; if ( ( lmp->attrib & LM_ATR_TABWRAP ) ) { if ( *next_row == 255 && v_scrolled ) *next_row = 0; else ++* next_row; } } break; case XI_KEY_UP: --*next_row; break; case XI_KEY_DOWN: if ( *next_row == 255 && v_scrolled ) *next_row = 0; else ++* next_row; break; case XI_KEY_WLEFT: case XI_KEY_LEFT: --*next_col; if ( *next_col < 0 ) *next_col = lmp->nbr_columns - 1; break; case XI_KEY_WRIGHT: case XI_KEY_RIGHT: ++*next_col; if ( *next_col >= lmp->nbr_columns ) *next_col = 0; break; } } /*------------------------------------------------------------------------- function: do_lm_cb lm: current lm cb_reason: one of LM_CB_CHAR, LM_CB_CHANGE, LM_CB_FOCUS, LM_CB_REC_ALLOCATE, LM_CB_REC_FREE row: relevent row column: relevent column ep: xvt event that caused the LM event, used to pass on shift and control -------------------------------------------------------------------------*/ BOOLEAN do_lm_cb( LM lm, LM_CB_TYPE cb_reason, int row, int column, XinEvent * ep, int *percent, int pixels ) { LM_CB_DATA lm_cb_data; LM_DATA *lmp = LMP( lm ); lm_cb_data.lm = lm; lm_cb_data.cb_type = cb_reason; lm_cb_data.cid = lmp->cid; lm_cb_data.win = lmp->win; lm_cb_data.row = ( unsigned char ) row; lm_cb_data.column = ( unsigned char ) column; switch ( cb_reason ) { case LM_CB_REC_ALLOCATE: lm_cb_data.v.rec_allocate.record = lmp->recs[row]; if ( lm_cb_data.v.rec_allocate.record ) return FALSE; break; case LM_CB_REC_FREE: lm_cb_data.v.rec_free.record = lmp->recs[row]; if ( lmp->have_last_rec && lm_cb_data.v.rec_free.record == lmp->last_rec ) lmp->have_last_rec = FALSE; if ( !lm_cb_data.v.rec_allocate.record ) return FALSE; break; case LM_CB_CHAR: lm_cb_data.v.chr.ch = ep->v.character.ch; lm_cb_data.v.chr.shift = ep->v.character.shift; lm_cb_data.v.chr.control = ep->v.character.control; lm_cb_data.v.chr.alt = ep->v.character.alt; lm_cb_data.v.chr.is_paste = FALSE; lm_cb_data.v.chr.refused = FALSE; break; case LM_CB_CELL_BTN: if ( ep->type == XinEventCharacter ) { lm_cb_data.v.cell_btn.shift = ep->v.character.shift; lm_cb_data.v.cell_btn.control = ep->v.character.control; } else { lm_cb_data.v.cell_btn.shift = ep->v.mouse.shift; lm_cb_data.v.cell_btn.control = ep->v.mouse.control; } break; case LM_CB_GET_PERCENT: lm_cb_data.v.get_percent.record = lmp->recs[row]; break; case LM_CB_ROW_SIZE: lm_cb_data.v.row_size.new_row_height = pixels; break; default: lm_cb_data.v.refused = FALSE; break; } ( *lmp->lm_cb ) ( &lm_cb_data ); /* retval = FALSE if event refused */ switch ( cb_reason ) { case LM_CB_REC_ALLOCATE: lmp->recs[row] = lm_cb_data.v.rec_allocate.record; return FALSE; case LM_CB_REC_FREE: lmp->recs[row] = 0L; return FALSE; case LM_CB_CHAR: if ( !lm_cb_data.v.chr.refused ) ep->v.character.ch = lm_cb_data.v.chr.ch; return ( !lm_cb_data.v.chr.refused ); case LM_CB_GET_PERCENT: *percent = lm_cb_data.v.get_percent.percent; return FALSE; case LM_CB_ROW_SIZE: return ( !lm_cb_data.v.row_size.refused ); default: return ( !lm_cb_data.v.refused ); } } /*------------------------------------------------------------------------- function: do_lm_cb_get lm: current lm cb_reason: one of LM_CB_GET_FIRST, LM_CB_GET_LAST, LM_CB_GET_NEXT, LM_CB_GET_PREV spec_rec: data_rec: percent: returns: TRUE if event refused -------------------------------------------------------------------------*/ BOOLEAN do_lm_cb_get( LM lm, LM_CB_TYPE cb_reason, long *spec_rec, long *data_rec, int percent, XinColor * color, unsigned long *attrib, int *row_height ) { LM_CB_DATA lm_cb_data; LM_DATA *lmp = LMP( lm ); lm_cb_data.lm = lm; lm_cb_data.cb_type = cb_reason; lm_cb_data.cid = lmp->cid; lm_cb_data.win = lmp->win; lm_cb_data.v.rec_request.spec_rec = *spec_rec; lm_cb_data.v.rec_request.data_rec = *data_rec; lm_cb_data.v.rec_request.percent = percent; lm_cb_data.v.rec_request.color = *color; lm_cb_data.v.rec_request.attrib = *attrib; lm_cb_data.v.rec_request.refused = FALSE; ( *lmp->lm_cb ) ( &lm_cb_data ); if ( cb_reason == LM_CB_GET_NEXT && lm_cb_data.v.rec_request.refused ) { lmp->have_last_rec = TRUE; lmp->last_rec = lm_cb_data.v.rec_request.spec_rec; } if ( !lm_cb_data.v.rec_request.refused ) { if ( cb_reason == LM_CB_GET_LAST ) { lmp->have_last_rec = TRUE; lmp->last_rec = lm_cb_data.v.rec_request.data_rec; } *data_rec = lm_cb_data.v.rec_request.data_rec; *color = lm_cb_data.v.rec_request.color; *attrib = lm_cb_data.v.rec_request.attrib; #if XIWS != XIWS_WM *row_height = lm_cb_data.v.rec_request.row_height; #else *row_height = 0; #endif if ( lm_cb_data.v.rec_request.has_focus && xi_get_pref( XI_PREF_KEEP_FOCUS_FIXED ) ) { if ( lm_focus_rec_get( lmp ) ) { lm_cb_data.lm = ( LM ) lmp; lm_cb_data.cb_type = LM_CB_REC_FREE; lm_cb_data.cid = lmp->cid; lm_cb_data.win = lmp->win; lm_cb_data.v.rec_free.record = lm_focus_rec_get( lmp ); ( *lmp->lm_cb ) ( &lm_cb_data ); } lm_focus_rec_set( lmp, *data_rec ); } } return lm_cb_data.v.rec_request.refused; } static void calculate_next_cell( LM_DATA * lmp, int *next_row, int *next_col, BOOLEAN * use_key, int ch, BOOLEAN * off_top, BOOLEAN * off_bottom ) { BOOLEAN keep_looking; int focus_row, focus_column; BOOLEAN v_scrolled; keep_looking = *use_key; lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); *next_row = focus_row; *next_col = focus_column; while ( keep_looking ) { next_cell( ( LM ) lmp, ch, next_row, next_col, v_scrolled ); if ( *next_row < 0 ) { /* off top logic */ if ( lmp->get_all_records ) { *use_key = FALSE; break; } else { keep_looking = FALSE; *off_top = TRUE; } } else if ( *next_row >= lmp->nbr_realized_rows ) { /* off bottom logic */ if ( lmp->get_all_records ) { *use_key = FALSE; break; } else { keep_looking = FALSE; *off_bottom = TRUE; } } else { LM_CELL_DATA *cell_data; cell_data = &lmp->cell_data[*next_row][*next_col]; if ( !cell_data->valid_data ) do_lm_cb_text( lmp, *next_row, *next_col, TRUE ); if ( CELL_IS_ENABLED( lmp, *next_row, *next_col ) && !CELL_IS_SELECTABLE( lmp, *next_row, *next_col ) && lmp->cell_data[*next_row][*next_col].icon_rid == 0 && lmp->cell_data[*next_row][*next_col].bitmap == NULL ) /* found a valid cell */ keep_looking = FALSE; } } } void lm_adjust_rows( LM_DATA * lmp, int delta ) { XI_OBJ *focus_obj; focus_obj = lmp->itf_obj->v.itf->focus_obj; if ( focus_obj && focus_obj->cid == lmp->cid ) { if ( focus_obj && focus_obj->type == XIT_CELL ) { if ( (int)focus_obj->v.cell.row + delta < 0 ) { focus_obj->v.cell.is_vert_scrolled = TRUE; focus_obj->v.cell.row += delta; return; } focus_obj->v.cell.row += delta; if ( !focus_obj->v.cell.is_vert_scrolled && ( long ) focus_obj->v.cell.row >= ( long ) lmp->nbr_realized_rows ) focus_obj->v.cell.is_vert_scrolled = TRUE; } if ( focus_obj && focus_obj->type == XIT_ROW ) focus_obj->v.row += delta; } } /*------------------------------------------------------------------------- function: navigate_char_event lm: current lm ep: xvt event returns: TRUE if key was used. -------------------------------------------------------------------------*/ BOOLEAN navigate_char_event( LM lm, XinEvent * ep ) { BOOLEAN use_key = FALSE; BOOLEAN off_top = FALSE; BOOLEAN off_bottom = FALSE; BOOLEAN should_redraw = TRUE; int next_row, next_col, row_inc, col_inc; LM_DATA *lmp = LMP( lm ); short c = ep->v.character.ch; if ( !lm_focus_list_has( lmp ) ) return FALSE; if ( ( c == '\r' || c == '\n' ) && lmp->itf_obj->v.itf->tab_on_enter) { int row, col; BOOLEAN is_vert_scrolled; lm_focus_cell_get( lmp, &row, &col, &is_vert_scrolled ); if (!lmp->lm_column_data[col]->cr_ok) { c = (short)xi_get_pref( XI_PREF_FORM_TAB_CHAR ); ep->v.character.ch = c; } } switch ( c ) { case '\t': case XI_KEY_BTAB: case XI_KEY_UP: case XI_KEY_DOWN: case XI_KEY_PREV: case XI_KEY_NEXT: use_key = TRUE; break; case XI_KEY_WRIGHT: case XI_KEY_WLEFT: case XI_KEY_LEFT: case XI_KEY_RIGHT: if ( ( lmp->attrib & LM_ATR_NAVIGATE ) || ep->v.character.control ) use_key = TRUE; break; } switch ( c ) { case XI_KEY_UP: case XI_KEY_DOWN: case XI_KEY_PREV: case XI_KEY_NEXT: { if ( lm_focus_state_get( lmp ) == LM_FOCUS_VISIBLE ) { XI_TEXT *text = xi_text_focus_get( lmp->win ); if ( text != 0 && text->multi_line && !ep->v.character.control && !( lmp->attrib & LM_ATR_NAVIGATE ) ) return FALSE; } switch ( c ) { case XI_KEY_PREV: xi_control_event_scroll( lmp->list_obj, XI_SCROLL_PGUP, 0, TRUE ); return TRUE; case XI_KEY_NEXT: xi_control_event_scroll( lmp->list_obj, XI_SCROLL_PGDOWN, 0, TRUE ); return TRUE; } break; } case XI_KEY_WRIGHT: case XI_KEY_WLEFT: case XI_KEY_LEFT: case XI_KEY_RIGHT: if ( !use_key ) return FALSE; } calculate_next_cell( lmp, &next_row, &next_col, &use_key, c, &off_top, &off_bottom ); if ( use_key ) { XI_LIST_DATA *list_data; list_data = lmp->list_obj->v.list; if ( off_top || off_bottom ) { int old_row_height, focus_row, focus_column; BOOLEAN v_scrolled; BOOLEAN do_redraw; lm_focus_cell_invis_make( lmp ); lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); old_row_height = lmp->pix_heights[focus_row]; do_redraw = ( lm_calculate_row_height( lmp, focus_row ) != old_row_height ); if ( off_top ) make_room_rows( lmp, -1, do_redraw ); else make_room_rows( lmp, 1, do_redraw ); /* calculate next cell again, because the desired next cell may have * changed. */ calculate_next_cell( lmp, &next_row, &next_col, &use_key, c, &off_top, &off_bottom ); lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); row_inc = next_row - focus_row; col_inc = next_col - focus_column; next_row = focus_row + row_inc; if ( next_row >= lmp->nbr_realized_rows ) next_row = lmp->nbr_realized_rows - 1; if ( next_row < 0 ) next_row = 0; next_col = focus_column + col_inc; while ( TRUE ) { LM_CELL_DATA *cell_data; cell_data = &lmp->cell_data[next_row][next_col]; if ( !cell_data->valid_data ) do_lm_cb_text( lmp, next_row, next_col, TRUE ); if ( CELL_IS_ENABLED( lmp, next_row, next_col ) && !CELL_IS_SELECTABLE( lmp, next_row, next_col ) && lmp->cell_data[next_row][next_col].icon_rid == 0 && lmp->cell_data[next_row][next_col].bitmap == NULL ) break; next_cell( lm, off_bottom ? '\t' : XI_KEY_BTAB, &next_row, &next_col, v_scrolled ); } if ( list_data->vert_sync_list && !list_data->scrolling_in_progress ) { XI_OBJ *other_list; list_data->scrolling_in_progress = TRUE; other_list = xi_get_obj( lmp->itf_obj, list_data->vert_sync_list ); if ( other_list ) xi_scroll( other_list, off_top ? -1 : 1 ); XinWindowPaintForce( lmp->win ); list_data->scrolling_in_progress = FALSE; } XinWindowPaintForce( lmp->win ); lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); if ( focus_row > lmp->last_fully_vis ) { LM_FOCUS_CELL_VISIBLE_FORCE_ARGS args; MEMCLEAR( args ); args.lmp = lmp; args.row = focus_row; args.column = focus_column; args.vert_scrolled = v_scrolled; lm_focus_cell_visible_force( &args ); lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); next_row = focus_row; calculate_pix_offsets( lmp, FALSE ); } lm_focus_cell_visible_attempt( lmp ); } else if ( next_row < lmp->first_fully_vis || next_row > lmp->last_fully_vis ) { if ( list_data->vert_sync_list && !list_data->scrolling_in_progress ) { XI_OBJ *other_list; list_data->scrolling_in_progress = TRUE; other_list = xi_get_obj( lmp->itf_obj, list_data->vert_sync_list ); if ( other_list ) xi_scroll( other_list, next_row < lmp->first_fully_vis ? -1 : 1 ); XinWindowPaintForce( lmp->win ); list_data->scrolling_in_progress = FALSE; } } if ( next_row < lmp->first_fully_vis ) { int delta, idx2, pix2, old_pix, cnt, height; XinRect tmp_rct; if ( lmp->first_fully_vis == 0 ) return use_key; lm_focus_cell_invis_make( lmp ); idx2 = lmp->first_fully_vis - 1; pix2 = lmp->pix_offsets[idx2]; old_pix = -lmp->rrr_offset; delta = old_pix - pix2; lmp->rrr_offset += delta; calculate_pix_offsets( lmp, FALSE ); if ( !lmp->itf_obj->v.itf->half_baked ) XinWindowPaintForce( lmp->win ); xi_set_update_obj( lmp->list_obj ); lm_get_scroll_rct( lmp, &tmp_rct ); /* calculate lmp->update_rows_at_top to speed up scrolling the list */ height = delta; for ( cnt = 0; cnt < lmp->nbr_realized_rows && height > 0; ++cnt ) height -= lmp->pix_heights[cnt]; lmp->update_rows_at_top = cnt; xi_scroll_rect( lmp->win, &tmp_rct, 0, delta ); do_scroll_bar( lmp->list_obj->v.list ); lm_focus_cell_visible_attempt( lmp ); should_redraw = FALSE; } if ( next_row > lmp->last_fully_vis ) { int delta, delta2, diff, cnt, height, first, old_row_height, focus_row; int idx, missing; int focus_column, new_row_height; BOOLEAN v_scrolled; XinRect tmp_rct; if ( lmp->last_fully_vis >= lmp->nbr_realized_rows ) return use_key; lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); old_row_height = lmp->pix_heights[focus_row]; lm_focus_cell_invis_make( lmp ); delta = lmp->pix_heights[lmp->first_fully_vis]; lm_make_rrr_room_pix( lmp, delta, TRUE ); idx = lmp->last_fully_vis + 1; missing = lmp->mlr_height - ( lmp->pix_offsets[idx] + lmp->pix_heights[idx] ); if ( lmp->last_fully_vis + 1 < lmp->nbr_realized_rows ) delta = max( delta, missing ); lmp->rrr_offset -= delta; calculate_pix_offsets( lmp, TRUE ); calculate_visibles( lmp ); if ( -lmp->rrr_offset != lmp->pix_offsets[lmp->first_fully_vis] ) { delta2 = lmp->pix_offsets[lmp->first_fully_vis] + lmp->rrr_offset; lmp->rrr_offset -= delta2; calculate_pix_offsets( lmp, FALSE ); calculate_visibles( lmp ); delta += delta2; } diff = lmp->pix_offsets[lmp->nbr_realized_rows - 1] + lmp->pix_heights[lmp->nbr_realized_rows - 1] - lmp->mlr_height + lmp->rrr_offset; if ( !lmp->itf_obj->v.itf->half_baked ) XinWindowPaintForce( lmp->win ); if ( diff < 0 ) lm_make_rrr_room_pix( lmp, -diff, FALSE ); xi_set_update_obj( lmp->list_obj ); lm_get_scroll_rct( lmp, &tmp_rct ); /* calculate lmp->update_rows_at_bottom to speed up scrolling the list */ height = lmp->mlr.bottom - lmp->mlr.top - delta; for ( cnt = 0; cnt < lmp->nbr_realized_rows && height > 0; ++cnt ) height -= lmp->pix_heights[cnt]; first = cnt - 1; height += delta; for ( ; height > 0; ++cnt ) height -= lmp->pix_heights[cnt]; lmp->update_rows_at_bottom = cnt - first; /* scroll the list */ xi_scroll_rect( lmp->win, &tmp_rct, 0, -delta ); do_scroll_bar( lmp->list_obj->v.list ); /* if the row height changed, then we need to redraw the row */ lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); new_row_height = lmp->pix_heights[focus_row]; if ( new_row_height != old_row_height ) { lm_redraw_row( lmp, focus_row, FALSE ); calculate_pix_offsets( lmp, TRUE ); } should_redraw = FALSE; } if ( !lmp->get_all_records ) { int nbr_to_free, cnt, i, idx; nbr_to_free = lmp->first_fully_vis; if ( nbr_to_free > 0 ) { int rheight; rheight = 0; for ( cnt = 0; cnt < nbr_to_free; cnt++ ) { lmp->rrr_offset += lmp->pix_heights[cnt]; rheight += lmp->pix_heights[cnt]; lm_do_rec_event( lmp, cnt, XIE_REC_FREE ); } for ( idx = nbr_to_free; idx < lmp->nbr_realized_rows; ++idx ) lm_row_copy( lmp, idx, idx - nbr_to_free ); lm_adjust_rows( lmp, -nbr_to_free ); /* calculate next cell again, because the desired next cell may have * changed. */ calculate_next_cell( lmp, &next_row, &next_col, &use_key, c, &off_top, &off_bottom ); lmp->nbr_realized_rows -= cnt; calculate_pix_offsets( lmp, should_redraw ); calculate_visibles( lmp ); lm_make_rrr_room_pix( lmp, rheight, TRUE ); calculate_pix_offsets( lmp, should_redraw ); calculate_visibles( lmp ); } /* free any unnecessary records at the end of the list */ nbr_to_free = lmp->nbr_realized_rows - ( lmp->last_fully_vis + 2 ); if ( nbr_to_free > 0 ) { for ( i = lmp->nbr_realized_rows - nbr_to_free, cnt = 0; cnt < nbr_to_free; i++, cnt++ ) lm_do_rec_event( lmp, i, XIE_REC_FREE ); lmp->nbr_realized_rows -= cnt; } calculate_pix_offsets( lmp, should_redraw ); lm_make_rrr_room_pix( lmp, 0, FALSE ); } do_lm_cb( lm, LM_CB_FOCUS, next_row, next_col, NULL, NULL, 0 ); lm_focus_cell_visible_attempt( lmp ); } return use_key; } /*------------------------------------------------------------------------- function: lm_insert_row lm: current lm row: row to insert -------------------------------------------------------------------------*/ BOOLEAN lm_insert_row( LM lm, int row ) { LM_DATA *lmp = LMP( lm ); if ( lmp->get_all_records ) XinError( 20919, XinSeverityFatal, 0L ); if ( xi_move_focus( lmp->itf_obj ) ) { if ( row > 0 && lmp->nbr_realized_rows ) { int idx, row_height, pix; XinRect r, row_rect; BOOLEAN is_visible; row = min( row, lmp->nbr_realized_rows ); pix = lmp->pix_offsets[row - 1] + lmp->pix_heights[row - 1] + 1; is_visible = ( pix <= lmp->mlr_height - lmp->rrr_offset ); if ( is_visible || lmp->get_all_records ) { LM_CELL_DATA *cell_data; int i; make_rec_available( lmp, REC_AT_BOTTOM, FALSE, FALSE ); for ( i = lmp->nbr_realized_rows - 2; i >= row; i-- ) lm_row_copy( lmp, i, i + 1 ); idx = row; /* cell_data_construct */ cell_data = lmp->cell_data[idx]; for ( i = 0; i < lmp->nbr_columns; ++i, ++cell_data ) { memset( ( char * ) cell_data, '\0', sizeof( LM_CELL_DATA ) ); lm_xi_text_construct( lmp, idx, i ); } lmp->recs[idx] = 0L; lm_do_rec_event( lmp, idx, XIE_REC_ALLOCATE ); if ( do_lm_cb_get( ( LM ) lmp, LM_CB_GET_NEXT, &lmp->recs[idx - 1], &lmp->recs[idx], 0, &lmp->row_colors[idx], &lmp->row_attribs[idx], &row_height ) ) { lm_do_rec_event( lmp, lmp->nbr_realized_rows - 1, XIE_REC_FREE ); for ( i = row; i < lmp->nbr_realized_rows - 2; ++i ) lm_row_copy( lmp, i + 1, i ); --lmp->nbr_realized_rows; return FALSE; } if ( row_height ) { lmp->pix_heights[idx] = row_height; lmp->set_heights[idx] = TRUE; } else { lmp->pix_heights[idx] = lm_calculate_row_height( lmp, idx ); lmp->set_heights[idx] = FALSE; } lm_invalidate_rows_internal( ( LM ) lmp, idx, idx, FALSE, -1, TRUE ); } if ( is_visible ) { lm_get_scroll_rct( lmp, &r ); row_height = lm_calculate_row_height( lmp, idx ); calculate_pix_offsets( lmp, FALSE ); lm_get_rect( lm, LM_ROW, idx, &row_rect ); r.top = row_rect.top; xi_scroll_rect( lmp->win, &r, 0, row_height ); } do_scroll_bar( lmp->list_obj->v.list ); return TRUE; } else { int idx, row_height, cnt; XinRect r; BOOLEAN need_first = !lmp->nbr_realized_rows; make_rec_available( lmp, REC_AT_TOP, TRUE, FALSE ); idx = 0; if ( do_lm_cb_get( ( LM ) lmp, need_first ? LM_CB_GET_FIRST : LM_CB_GET_PREV, &lmp->recs[idx + 1], &lmp->recs[idx], 0, &lmp->row_colors[idx], &lmp->row_attribs[idx], &row_height ) ) { lm_do_rec_event( lmp, 0, XIE_REC_FREE ); for ( cnt = 1; cnt < lmp->nbr_realized_rows; ++cnt ) lm_row_copy( lmp, cnt, cnt - 1 ); --lmp->nbr_realized_rows; return FALSE; } if ( row_height ) { lmp->pix_heights[idx] = row_height; lmp->set_heights[idx] = TRUE; } else { lmp->pix_heights[idx] = lm_calculate_row_height( lmp, idx ); lmp->set_heights[idx] = FALSE; } lm_invalidate_rows_internal( ( LM ) lmp, idx, idx, FALSE, -1, TRUE ); lm_get_scroll_rct( lmp, &r ); row_height = lm_calculate_row_height( lmp, idx ); calculate_pix_offsets( lmp, FALSE ); xi_scroll_rect( lmp->win, &r, 0, row_height ); do_scroll_bar( lmp->list_obj->v.list ); return TRUE; } } return FALSE; } /*------------------------------------------------------------------------- function: lm_delete_row lm: current lm row: row to delete -------------------------------------------------------------------------*/ BOOLEAN lm_delete_row( LM lm, int row ) { LM_DATA *lmp = LMP( lm ); if ( lmp->get_all_records ) XinError( 20920, XinSeverityFatal, 0L ); if ( xi_move_focus( lmp->itf_obj ) ) { int pix_height, i; int recs_read; XinRect r, row_rct; pix_height = lmp->pix_heights[row]; lm_get_rect( lm, LM_ROW, row, &row_rct ); lm_do_rec_event( lmp, row, XIE_REC_FREE ); for ( i = row + 1; i < lmp->nbr_realized_rows; i++ ) lm_row_copy( lmp, i, i - 1 ); lmp->recs[--lmp->nbr_realized_rows] = 0; calculate_pix_offsets( lmp, FALSE ); recs_read = lm_make_rrr_room_pix( lmp, pix_height, FALSE ); pix_height = max( pix_height, 0 ); lm_get_scroll_rct( lmp, &r ); r.top = max( r.top, row_rct.top ); if ( recs_read ) xi_invalidate_rect( lmp->win, &r ); else xi_scroll_rect( lmp->win, &r, 0, -pix_height ); XinWindowPaintForce( lmp->win ); do_scroll_bar( lmp->list_obj->v.list ); } return TRUE; } /*------------------------------------------------------------------------- function: lm_set_row_height lm: current lm row: row to set height: height -------------------------------------------------------------------------*/ void lm_set_row_height( LM lm, int row, int height, BOOLEAN set_height, int old_height, BOOLEAN only_update ) { LM_DATA *lmp = ( LM_DATA * ) lm; int idx = row; int old_row_height, delta; XinRect r, r2; if ( !lmp->itf_obj->v.itf->half_baked ) XinWindowPaintForce( lmp->win ); if ( only_update ) old_row_height = old_height; else { old_row_height = lmp->pix_heights[idx]; lmp->pix_heights[idx] = height; lmp->set_heights[idx] = set_height; } calculate_pix_offsets( lmp, FALSE ); /* Adjust row information */ if ( !lmp->get_all_records ) { if ( height < old_row_height ) lm_make_rrr_room_pix( lmp, 0, FALSE ); else { /* Free rows at bottom */ int i, cnt; int nbr_to_free = lmp->nbr_realized_rows - ( lmp->last_fully_vis + 2 ); for ( i = lmp->nbr_realized_rows - nbr_to_free, cnt = 0; cnt < nbr_to_free; i++, cnt++ ) lm_do_rec_event( lmp, i, XIE_REC_FREE ); if ( nbr_to_free > 0 ) lmp->nbr_realized_rows -= nbr_to_free; } } lm_get_scroll_rct( lmp, &r ); if ( row < lmp->nbr_realized_rows - 1 ) { r.top = lmp->pix_offsets[idx + 1] + lmp->rrr_offset + lmp->mlr.top; delta = height - old_row_height; if ( r.top < r.bottom ) xi_scroll_rect( lmp->win, &r, 0, delta ); } lm_get_rect( lm, LM_ROW, row, &r ); if ( row == lmp->nbr_realized_rows - 1 ) { r2 = r; r2.top = r2.bottom; r2.bottom = lmp->mlr.bottom; if ( r2.top < r2.bottom ) xi_invalidate_rect( lmp->win, &r2 ); } xi_invalidate_rect( lmp->win, &r ); } /*------------------------------------------------------------------------- function: lm_set_list_size lm: current lm height: new height width: new width -------------------------------------------------------------------------*/ void lm_set_list_size( LM lm, int height, int width ) { LM_DATA *lmp = ( LM_DATA * ) lm; XinRect vert_scrollbar_rect; XinRect horz_scrollbar_rect; XinRect ir, old_rct; int old_height; int col_offset; int max_row_height; int row_height; int heading_height; int i; int row, col; XI_LIST_DATA *list_data; int nbr_to_free, cnt; list_data = lmp->list_obj->v.list; xi_get_rect( lmp->list_obj, &old_rct ); col_offset = ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); xi_get_hsb_rect( lmp->list_obj, &horz_scrollbar_rect ); xi_get_sb_rect( lmp->list_obj, &vert_scrollbar_rect ); /* calculate minumum height */ if ( !list_data->one_row_list ) { { int leading; int ascent; int descent; int char_width; int font_height; xi_get_font_metrics_font( lmp->font, &leading, &ascent, &descent, &char_width ); font_height = leading + ascent + descent; max_row_height = lmp->max_lines_in_cell * font_height + RULE_Y_OFFSET_TOP + RULE_Y_OFFSET_BOTTOM + RULE_WIDTH_H; } for ( i = 0; i < lmp->nbr_realized_rows; ++i ) { row_height = lmp->pix_heights[i]; max_row_height = max( row_height, max_row_height ); } heading_height = ( lmp->pix_row1_top - lmp->rct.top ) + BORDER_WIDTH; i = max_row_height + heading_height + ( horz_scrollbar_rect.bottom - horz_scrollbar_rect.top ); height = max( height, i ); /* calculate new list metrics */ lmp->pixel_height = height; lmp->rct.bottom = lmp->rct.top + height - ( ( list_data->hsb_win ) ? ( horz_scrollbar_rect.bottom - horz_scrollbar_rect.top - 1 ) : 0 ); #if XIWS == XIWS_WM lmp->mlr.bottom = lmp->rct.bottom; #else lmp->mlr.bottom = lmp->rct.bottom - BORDER_WIDTH; #endif old_height = lmp->mlr_height; lmp->mlr_height = lmp->mlr.bottom - lmp->mlr.top; calculate_pix_offsets( lmp, FALSE ); if ( !lmp->get_all_records ) { /* fill in with more records */ if ( old_height < lmp->mlr_height ) lm_make_rrr_room_pix( lmp, lmp->mlr_height - old_height, FALSE ); } calculate_visibles( lmp ); } if ( width && lmp->pixel_width ) { int i, min_width, max_col_width; max_col_width = 0; for ( i = lmp->fixed_columns; i < lmp->nbr_columns; ++i ) max_col_width = max( lmp->lm_column_data[i]->pix_width, max_col_width ); max_col_width += BORDER_WIDTH; min_width = ( lmp->vir_left - lmp->rct.left ) + max_col_width + ( vert_scrollbar_rect.right - vert_scrollbar_rect.left ) + BORDER_WIDTH + 2 * col_offset; if ( list_data->sb_win ) min_width += ( vert_scrollbar_rect.right - vert_scrollbar_rect.left ); width = max( min_width, width ); lmp->pixel_width = width - ( 2 * BORDER_WIDTH ); if ( list_data->sb_win ) lmp->pixel_width -= ( vert_scrollbar_rect.right - vert_scrollbar_rect.left - 1 ); lmp->vir_right = lmp->rct.left + lmp->pixel_width + BORDER_WIDTH; } /* move scroll bars */ list_data->have_sb_rct = FALSE; list_data->have_hsb_rct = FALSE; xi_move_list_scroll_bar( lmp->list_obj ); xi_move_list_hscroll_bar( lmp->list_obj ); lm_set_hscroll_range( lm ); lm_set_hscroll_bar( lm ); ir = lmp->rct; xi_get_hsb_rect( lmp->list_obj, &horz_scrollbar_rect ); ir.bottom = horz_scrollbar_rect.bottom + BORDER_WIDTH; xi_get_sb_rect( lmp->list_obj, &vert_scrollbar_rect ); ir.right = vert_scrollbar_rect.right + BORDER_WIDTH; ir.right = max( ir.right, old_rct.right ); ir.bottom = max( ir.bottom, old_rct.bottom ); xi_invalidate_rect( lmp->win, &ir ); do_scroll_bar( lmp->list_obj->v.list ); if ( width && lmp->pixel_width ) { /* calculate visible columns */ i = lm_get_left_most_far_right_col( lmp, lmp->nbr_columns ); if ( i < lmp->first_vis ) { XinWindowPaintForce( lmp->win ); lm_hscroll( lm, lmp->first_vis - i, 0 ); } else { lm_calc_last_vis( lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } } if ( !lmp->get_all_records ) { /* free any unnecessary records at the end of the list */ nbr_to_free = lmp->nbr_realized_rows - ( lmp->last_fully_vis + 2 ); for ( i = lmp->nbr_realized_rows - nbr_to_free, cnt = 0; cnt < nbr_to_free; i++, cnt++ ) lm_do_rec_event( lmp, i, XIE_REC_FREE ); lmp->nbr_realized_rows -= cnt; calculate_visibles( lmp ); } for ( col = 0; col < lmp->nbr_columns; ++col ) { for ( row = 0; row < lmp->nbr_realized_rows; ++row ) { LM_CELL_DATA *cell_data; cell_data = &lmp->cell_data[row][col]; if ( cell_data->xi_text ) { XinRect r; r = lmp->mlr; if ( lmp->pixel_width ) r.right = r.left + lmp->pixel_width; xi_text_clip_set( cell_data->xi_text, &r ); } } } } static void calc_lmp_rct_right( LM_DATA * lmp ) { /* TODO for XINCH mode, need to duplicate algorythm in lm_create */ /* single-column lists do not have a vertical rule seperating columns */ if ( lmp->nbr_columns == 0 ) lmp->rct.right = lmp->rct.left + 2 * BORDER_WIDTH; else { LM_COLUMN_DATA *cell_data = lmp->lm_column_data[lmp->nbr_columns - 1]; lmp->rct.right = lmp->rct.left + cell_data->x_pix_pos + cell_data->pix_width + 2 * BORDER_WIDTH + ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); } lmp->mlr.right = lmp->rct.right - BORDER_WIDTH; } void lm_recalc_metrics( LM lm ) { LM_DATA *lmp = ( LM_DATA * ) lm; int leading, ascent, descent, font_height, mch, i; XinWindow win; LM_COLUMN_DATA **cell_data; int old_char_width; XI_LIST_DATA *list_data; XinPoint pnt; XinRect rct; list_data = lmp->list_obj->v.list; pnt = list_data->xi_pnt; xi_fu_to_pu( lmp->itf_obj, &pnt, 1 ); lmp->rct.top = pnt.v; lmp->rct.left = pnt.h; if ( !lmp->resize_with_window ) { lmp->pixel_width = list_data->width * xi_get_fu_width( lmp->itf_obj ) / XI_FU_MULTIPLE; } if ( !lmp->is_list_font ) lmp->font = lmp->itf_obj->v.itf->font; if (!lmp->font) lmp->font = xi_get_system_font(); win = lmp->win; XinWindowFontMap( win, lmp->font ); XinFontMetricsGet( lmp->font, &leading, &ascent, &descent ); lmp->leading = leading; lmp->ascent = ascent; lmp->descent = descent; font_height = lmp->ascent + lmp->leading + lmp->descent; lmp->pix_cell_height = font_height + ( RULE_Y_OFFSET_TOP + RULE_Y_OFFSET_BOTTOM ); #if XIWS == XIWS_WM mch = 8; #else mch = lmp->min_cell_height; #endif lmp->pix_cell_height = max( lmp->pix_cell_height, mch ); lmp->pix_row_spacing = lmp->pix_cell_height + RULE_WIDTH_H; lmp->pix_top = lmp->rct.top; lmp->pix_hdr_bottom = lmp->rct.top + lmp->leading + lmp->ascent + lmp->descent + BORDER_WIDTH + RULE_Y_OFFSET_BOTTOM + RULE_Y_OFFSET_TOP + 1; lmp->pix_hdr_bottom = max( lmp->pix_hdr_bottom, ( lmp->rct.top + lmp->min_heading_height + 2 * BORDER_WIDTH ) ); if ( lmp->no_heading ) lmp->pix_row1_top = lmp->rct.top + BORDER_WIDTH; else lmp->pix_row1_top = lmp->pix_hdr_bottom + BORDER_WIDTH; old_char_width = lmp->pix_char_width; lmp->pix_char_width = xi_get_fu_width( lmp->itf_obj ); calculate_pix_offsets( lmp, FALSE ); for ( i = 0, cell_data = lmp->lm_column_data; i < lmp->nbr_columns; ++i, ++cell_data ) ( *cell_data )->pix_width = ( *cell_data )->pix_width * lmp->pix_char_width / old_char_width; for ( i = 0; i < lmp->nbr_columns; ++i ) calc_x_pix_pos( lm, lmp->lm_column_data[i], i ); calc_lmp_rct_right( lmp ); if ( !lmp->resize_with_window ) { #if XIWS != XIWS_WM lmp->rct.bottom = lmp->pix_row1_top + lmp->nbr_rows * lmp->pix_row_spacing + ( BORDER_WIDTH - RULE_WIDTH_H ); #else lmp->rct.bottom = lmp->pix_row1_top + lmp->nbr_rows * lmp->pix_row_spacing + BORDER_WIDTH; #endif } if ( lmp->fixed_columns >= lmp->nbr_columns ) lmp->delta_x = 0; else lmp->delta_x = lmp->lm_column_data[lmp->first_vis]->x_pix_pos - lmp->lm_column_data[lmp->fixed_columns]->x_pix_pos; lmp->mlr.top = lmp->pix_row1_top; #if XIWS != XIWS_WM lmp->mlr.bottom = lmp->rct.bottom - BORDER_WIDTH; #else if ( lmp->pixel_width ) lmp->mlr.bottom = lmp->rct.bottom; else lmp->mlr.bottom = lmp->rct.bottom - BORDER_WIDTH; #endif lmp->mlr_height = lmp->mlr.bottom - lmp->mlr.top; if ( lmp->rrr_bottom < lmp->mlr_height ) lm_make_rrr_room_pix( lmp, lmp->mlr_height - lmp->rrr_bottom, FALSE ); { XinRect vert_scrollbar_rect; xi_get_sb_rect( lmp->list_obj, &vert_scrollbar_rect ); if ( list_data->sb_win ) lmp->pixel_width -= ( vert_scrollbar_rect.right - vert_scrollbar_rect.left - 1 ); lmp->pixel_width = max( lmp->pixel_width, 0 ); lmp->vir_right = lmp->rct.left + lmp->pixel_width + BORDER_WIDTH; } list_data->have_sb_rct = FALSE; list_data->have_hsb_rct = FALSE; xi_get_sb_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 ); if ( list_data->sb_win ) XinWindowRectSet( list_data->sb_win, &rct ); 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 ); if ( list_data->hsb_win ) XinWindowRectSet( list_data->hsb_win, &rct ); lm_calc_last_vis( lmp ); } void lm_text_scrolling( XI_OBJ * xi_obj ) { LM_DATA *lmp = ( LM_DATA * ) xi_obj->v.list->lm; lmp->text_scrolling = TRUE; } void lm_set_hscroll_range( LM lm ) { LM_DATA *lmp = ( LM_DATA * ) lm; int hscroll_width, fixed_width, cnt; XinRect mlr; XI_LIST_DATA *listdata; if ( lmp->list_obj->nbr_children <= 0 ) return; listdata = lmp->list_obj->v.list; lm_get_scroll_rct( lmp, &mlr ); if ( lmp->pixel_width ) { int prop; hscroll_width = 0; fixed_width = 0; for ( cnt = lmp->fixed_columns; cnt < lmp->nbr_columns; ++cnt ) hscroll_width += lmp->lm_column_data[cnt]->pix_width + lm_get_col_spacing( ); for ( cnt = 0; cnt < lmp->fixed_columns; ++cnt ) fixed_width += lmp->lm_column_data[cnt]->pix_width + lm_get_col_spacing( ); prop = mlr.right - mlr.left - fixed_width - BORDER_WIDTH; prop = min( prop, hscroll_width ); prop = max( prop, 0 ); XinScrollBarSet( listdata->hsb_win, XinScrollBarTypeEither, 0, hscroll_width, prop, 0 ); } } /*------------------------------------------------------------------------- function: lm_column_set_pixel_width lm: current lm idx: column number width: in pixels -------------------------------------------------------------------------*/ void lm_column_set_pixel_width( LM lm, int idx, int width ) { LM_DATA *lmp = ( LM_DATA * ) lm; LM_COLUMN_DATA *column; XinRect invalid_rct, old_rct; int i, old_width, new_row_height, cnt; old_rct = lmp->rct; column = lmp->lm_column_data[idx]; old_width = column->pix_width; column->width = width / lmp->pix_char_width; column->pix_width = width; for ( i = idx + 1; i < lmp->nbr_columns; ++i ) calc_x_pix_pos( lm, lmp->lm_column_data[i], i ); /* adjust bounding rectangle */ calc_lmp_rct_right( lmp ); if ( idx < lmp->fixed_columns ) lmp->vir_left += column->pix_width - old_width; /* The x_pix_pos of the first visible column may have changed so recalculate * delta_x */ if ( lmp->fixed_columns >= lmp->nbr_columns ) lmp->delta_x = 0; else lmp->delta_x = lmp->lm_column_data[lmp->first_vis]->x_pix_pos - lmp->lm_column_data[lmp->fixed_columns]->x_pix_pos; invalid_rct = lmp->rct; invalid_rct.left = column->x_pix_pos; invalid_rct.right = max( invalid_rct.right, old_rct.right ); if ( lmp->pixel_width ) invalid_rct.right = lmp->rct.left + lmp->pixel_width + 2 * BORDER_WIDTH; lmp->list_obj->v.list->have_sb_rct = FALSE; lmp->list_obj->v.list->have_hsb_rct = FALSE; lm_set_hscroll_range( ( LM ) lmp ); lm_set_hscroll_bar( ( LM ) lmp ); lm_calc_last_vis( lmp ); if ( column->wrap_text ) { int first_fully_vis = lmp->first_fully_vis; int last_row = lmp->nbr_realized_rows - 1; int height; for ( cnt = 0; cnt < lmp->nbr_realized_rows; ++cnt ) { int old_row_height; old_row_height = lmp->pix_heights[cnt]; new_row_height = lm_calculate_row_height( lmp, cnt ); lmp->pix_heights[cnt] = new_row_height; if ( old_row_height != new_row_height ) invalid_rct.left = lmp->rct.left; } calculate_pix_offsets( lmp, FALSE ); height = lmp->pix_offsets[last_row] + lmp->pix_heights[last_row] - lmp->pix_offsets[first_fully_vis]; if ( height < lmp->mlr_height ) lm_make_rrr_room_pix( lmp, lmp->mlr_height - height, FALSE ); if ( !lmp->get_all_records ) { int nbr_to_free; /* free any unnecessary records at the end of the list */ nbr_to_free = lmp->nbr_realized_rows - ( lmp->last_fully_vis + 2 ); for ( i = lmp->nbr_realized_rows - nbr_to_free, cnt = 0; cnt < nbr_to_free; i++, cnt++ ) lm_do_rec_event( lmp, i, XIE_REC_FREE ); lmp->nbr_realized_rows -= cnt; calculate_visibles( lmp ); } } lm_invalidate_rect2( lmp, &invalid_rct, TRUE ); } /*------------------------------------------------------------------------- function: lm_set_column_width lm: current lm idx: column number width: in characters -------------------------------------------------------------------------*/ void lm_set_column_width( LM lm, int idx, int width ) { lm_column_set_pixel_width( lm, idx, width * ( ( LM_DATA * ) lm )->pix_char_width ); } /*------------------------------------------------------------------------- function: lm_get_visible_columns lm: current lm first_vis: last_vis: -------------------------------------------------------------------------*/ void lm_get_visible_columns( LM lm, int *first_vis, int *last_vis ) { LM_DATA *lmp = ( LM_DATA * ) lm; *first_vis = lmp->first_vis; *last_vis = lmp->last_vis; } /*------------------------------------------------------------------------- function: lm_get_list_info lm: current lm nbr_recs: number of records returns: array of record handles -------------------------------------------------------------------------*/ long * lm_get_list_info( LM lm, int *nbr_recs ) { LM_DATA *lmp = ( LM_DATA * ) lm; *nbr_recs = lmp->nbr_realized_rows; return &lmp->recs[0]; } /*------------------------------------------------------------------------- function: lm_cell_request lm: current lm lm_part: may be LM_LIST, LM_ROW, LM_COLUMN, or LM_CELL ?? -------------------------------------------------------------------------*/ void lm_cell_request( LM lm, LM_PART lm_part, int idx1, int idx2 ) { LM_DATA *lmp = LMP( lm ); BOOLEAN redraw; redraw = ( BOOLEAN ) ( lmp->attrib & ( LM_ATR_VISIBLE ) ); lm_focus_cell_invis_make( lmp ); switch ( lm_part ) { case LM_LIST: lm_invalidate_rows_internal( lm, 0, lmp->nbr_realized_rows - 1, redraw, -1, FALSE ); lm_focus_cell_visible_attempt( lmp ); return; case LM_ROW: lm_invalidate_rows_internal( lm, idx1, idx1, redraw, -1, FALSE ); lm_focus_cell_visible_attempt( lmp ); return; case LM_COLUMN: lm_invalidate_rows_internal( lm, 0, lmp->nbr_realized_rows - 1, redraw, idx1, FALSE ); lm_focus_cell_visible_attempt( lmp ); return; case LM_CELL: lm_invalidate_rows_internal( lm, idx1, idx1, redraw, idx2, FALSE ); lm_focus_cell_visible_attempt( lmp ); return; } XinError( 20918, XinSeverityFatal, 0L ); } /*------------------------------------------------------------------------- function: lm_get_visible_rows ------------------------------------------------------------------------- */ int lm_get_visible_rows( LM lm, int *first_vis, int *last_vis ) { LM_DATA *lmp = ( LM_DATA * ) lm; if ( first_vis ) *first_vis = lmp->first_fully_vis; if ( last_vis ) *last_vis = lmp->last_fully_vis; if ( !lmp->nbr_realized_rows ) return 0; else return lmp->last_fully_vis - lmp->first_fully_vis + 1; } /*------------------------------------------------------------------------- function: lm_get_list_obj lm: current lm returns: XI_OBJ * for list -------------------------------------------------------------------------*/ XI_OBJ * lm_get_list_obj( LM lm ) { LM_DATA *lmp = LMP( lm ); return ( lmp->list_obj ); } /*------------------------------------------------------------------------- function: lm_set_buf_size_internal lm: current lm part: must be LM_COLUMN idx: column number size: the new buffer size redraw: if TRUE, then redraw the cell -------------------------------------------------------------------------*/ static void lm_set_buf_size_internal( LM lm, LM_PART part, int idx, int size, BOOLEAN redraw ) { int i; LM_COLUMN_DATA *lmcd; LM_DATA *lmp = LMP( lm ); if ( part != LM_COLUMN ) XinError( 20917, XinSeverityFatal, 0L ); lmcd = lmp->lm_column_data[idx]; lmcd->text_size = size; if ( ! lmcd->var_len_text ) { for ( i = 0; i < lmp->nbr_realized_rows; i++ ) { LM_CELL_DATA *cell_data = &lmp->cell_data[i][idx]; xi_text_buffer_size_set( cell_data->xi_text, size ); if ( redraw && i < lmp->nbr_realized_rows ) redraw_cell( lm, i, idx, FALSE ); } } } /*------------------------------------------------------------------------- function: lm_set_buf_size lm: current lm part: must be LM_COLUMN idx: column number size: the new buffer size -------------------------------------------------------------------------*/ void lm_set_buf_size( LM lm, LM_PART part, int idx, int size ) { lm_set_buf_size_internal( lm, part, idx, size, TRUE ); } /* ------------------------------------------------------------------------ */ /* adjust_focus_for_column_delete */ /* if the focus is on a column after the deleted column, adjust the focus */ /* objects. */ /* if the focus is on the deleted column, move the focus to the next */ /* column, previous column, or interface. */ /* ------------------------------------------------------------------------ */ static BOOLEAN adjust_focus_for_column_delete( LM_DATA * lmp, int column_nbr, BOOLEAN adjust_focus_column ) { XI_OBJ *focus_obj; focus_obj = lmp->itf_obj->v.itf->focus_obj; if ( focus_obj == NULL || focus_obj->parent != lmp->list_obj || focus_obj->type != XIT_CELL ) return TRUE; if ( focus_obj->v.cell.column == column_nbr ) { XI_OBJ new_cell; int row, col; BOOLEAN temp; temp = TRUE; calculate_next_cell( lmp, &row, &col, &temp, '\t', &temp, &temp ); if ( row != focus_obj->v.cell.row ) { temp = TRUE; calculate_next_cell( lmp, &row, &col, &temp, XI_KEY_BTAB, &temp, &temp ); } if ( row == focus_obj->v.cell.row ) { XI_MAKE_CELL( &new_cell, lmp->list_obj, row, col ); if ( !xi_move_focus( &new_cell ) ) return FALSE; if ( adjust_focus_column && ( int ) focus_obj->v.cell.column > ( int ) column_nbr ) { focus_obj = lmp->itf_obj->v.itf->focus_obj; focus_obj->v.cell.column--; } return TRUE; } return xi_move_focus( lmp->itf_obj ); } if ( adjust_focus_column && ( int ) focus_obj->v.cell.column > ( int ) column_nbr ) focus_obj->v.cell.column--; return TRUE; } /*------------------------------------------------------------------------- function: lm_delete_column lm: current lm column_nbr: column to delete adjust_hscrolling: if TRUE, then make more horizontal columns visible -------------------------------------------------------------------------*/ void lm_delete_column( LM lm, int column_nbr, BOOLEAN adjust_hscrolling ) { LM_DATA *lmp = LMP( lm ); LM_COLUMN_DATA *lmcd; XinRect invalid_rct, old_rct; int i, old_width, old_left, col_spacing; int old_x_pix_pos; LM_CELL_DATA *cell_data; if ( !adjust_focus_for_column_delete( lmp, column_nbr, TRUE ) ) return; col_spacing = lm_get_col_spacing( ); if ( column_nbr >= lmp->nbr_columns ) XinError( 20915, XinSeverityFatal, 0L ); old_rct = lmp->rct; lmcd = lmp->lm_column_data[column_nbr]; if ( lmcd->font ) XinFontDestroy( lmcd->font ); xi_bitmap_destroy( lmcd->bitmap ); old_left = lmcd->x_pix_pos - lmp->delta_x; old_x_pix_pos = lmcd->x_pix_pos; old_width = lmcd->pix_width; xi_tree_free( lmp->lm_column_data[column_nbr] ); /* cell_data_destruct */ for ( i = 0; i < lmp->nbr_realized_rows; ++i ) { cell_data = &( lmp->cell_data[i][column_nbr] ); if ( cell_data->font ) XinFontDestroy( cell_data->font ); xi_bitmap_destroy( cell_data->bitmap ); xi_bitmap_destroy( cell_data->button_bitmap ); if ( cell_data->xi_text ) xi_text_destruct( cell_data->xi_text ); } for ( i = 0; i < lmp->realized_rows_array_len; ++i ) { int j; for ( j = column_nbr; j < lmp->nbr_columns - 1; ++j ) lmp->cell_data[i][j] = lmp->cell_data[i][j + 1]; lmp->cell_data[i] = ( LM_CELL_DATA * ) xi_tree_realloc( lmp->cell_data[i], ( lmp->nbr_columns - 1 ) * sizeof( LM_CELL_DATA ) ); } for ( i = column_nbr; i < lmp->nbr_columns - 1; ++i ) lmp->lm_column_data[i] = lmp->lm_column_data[i + 1]; --lmp->nbr_columns; { /* Check focus cell, to see if it is now out of range */ XI_OBJ* focus_cell; focus_cell = lmp->list_obj->v.list->focus_cell; if ( (int)focus_cell->v.cell.column >= lmp->nbr_columns ) focus_cell->v.cell.column = lmp->nbr_columns - 1; } for ( i = column_nbr; i < lmp->nbr_columns; ++i ) calc_x_pix_pos( lm, lmp->lm_column_data[i], i ); calc_lmp_rct_right( lmp ); /* adjust left border of horizontal virtual space */ if ( column_nbr < lmp->fixed_columns ) lmp->vir_left -= ( old_width + col_spacing ); /* if the column is invisible */ calculate_pix_offsets( lmp, TRUE ); if ( column_nbr >= lmp->fixed_columns && old_x_pix_pos < lmp->delta_x ) { lmp->delta_x -= ( old_width + lm_get_col_spacing( ) ); --lmp->first_vis; lm_set_hscroll_range( ( LM ) lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } else { if ( column_nbr < lmp->fixed_columns ) { --lmp->first_vis; --lmp->fixed_columns; lm_set_hscroll_range( ( LM ) lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } invalid_rct = lmp->rct; invalid_rct.left = old_left; invalid_rct.right = max( invalid_rct.right, old_rct.right ); if ( lmp->pixel_width ) invalid_rct.right = lmp->rct.left + lmp->pixel_width + BORDER_WIDTH; /* if we add the following two lines in, it prevents flashing of the top * and bottom borders when moving columns, however, the top and bottom * borders need to get invalidated invalid_rct.top += BORDER_WIDTH; * invalid_rct.bottom -= BORDER_WIDTH; */ lm_invalidate_rect2( lmp, &invalid_rct, FALSE ); lmp->list_obj->v.list->have_sb_rct = FALSE; lmp->list_obj->v.list->have_hsb_rct = FALSE; i = lm_get_left_most_far_right_col( lmp, lmp->nbr_columns ); /* All columns have been deleted */ if ( i == 0 ) { lmp->delta_x = 0; lmp->last_vis = lmp->first_vis = i; lm_calc_last_vis( lmp ); lm_set_hscroll_bar( ( LM ) lmp ); return; } if ( lmp->first_vis >= lmp->nbr_columns ) { XinRect r; XinWindowPaintForce( lmp->win ); if ( i < lmp->nbr_columns ) lmp->delta_x = lmp->lm_column_data[i]->x_pix_pos - lmp->vir_left; else lmp->delta_x = 0; xi_set_update_obj( lmp->list_obj ); lmp->last_vis = lmp->first_vis = i; lm_calc_last_vis( lmp ); r.left = lmp->vir_left; r.right = lmp->vir_right; #if XIWS != XIWS_WM r.top = lmp->rct.top + BORDER_WIDTH; r.bottom = lmp->rct.bottom - BORDER_WIDTH; #else r.top = lmp->rct.top; r.bottom = lmp->rct.bottom; #endif xi_invalidate_rect( lmp->win, &r ); XinWindowPaintForce( lmp->win ); lm_set_hscroll_bar( ( LM ) lmp ); } else { if ( i < lmp->first_vis && adjust_hscrolling ) { XinWindowPaintForce( lmp->win ); lm_hscroll( lm, i - lmp->first_vis, 0 ); lm_set_hscroll_range( ( LM ) lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } else { lm_calc_last_vis( lmp ); lm_set_hscroll_range( ( LM ) lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } } } } /* TODO this function will change. pix_row_spacing will no longer be used. instead, min_row_height, and absolute_height will be used. */ /*------------------------------------------------------------------------- lm_get_vertical_metrics -------------------------------------------------------------------------*/ void lm_get_vertical_metrics( XI_OBJ_DEF * obj_def, int *first_row_y, int *row_spacing, int *client_height, int *title_height ) { XI_LIST_DEF *list_def; XI_OBJ_DEF *itf_def; int leading, ascent, descent, char_width, font_height, pix_cell_height, pix_hdr_bottom, min_cell_height; XinPoint p; XinFont *fontp = NULL; list_def = obj_def->v.list; #if XIWS != XIWS_WM itf_def = obj_def->parent; if ( itf_def && itf_def->v.itf->font ) fontp = itf_def->v.itf->font; if ( list_def->font ) xi_get_font_metrics_font( list_def->font, &leading, &ascent, &descent, &char_width ); else if ( fontp ) xi_get_font_metrics_font( fontp, &leading, &ascent, &descent, &char_width ); else xi_get_font_metrics_font( xi_get_system_font( ), &leading, &ascent, &descent, &char_width ); #else leading = 0; ascent = 8; descent = 0; #endif font_height = ascent + leading + descent; #if XIWS == XIWS_WM min_cell_height = 8; pix_cell_height = font_height; #else min_cell_height = list_def->min_cell_height; pix_cell_height = font_height + CELL_VERTICAL_MARGIN; #endif pix_cell_height = max( pix_cell_height, min_cell_height ); #if XIWS == XIWS_WM *row_spacing = pix_cell_height; #else *row_spacing = pix_cell_height + RULE_WIDTH_H; #endif itf_def = obj_def->parent; if ( itf_def && itf_def->v.itf->font ) fontp = itf_def->v.itf->font; if ( xi_def_get_xil_pref( itf_def ) ) { p = list_def->xi_pnt; } else { p = list_def->pixel_origin; if ( !p.v && !p.h ) { p = list_def->xi_pnt; if ( list_def->font ) xi_fu_to_pu_font( list_def->font, &p, 1 ); else if ( fontp ) xi_fu_to_pu_font( fontp, &p, 1 ); else xi_fu_to_pu_font( xi_get_system_font( ), &p, 1 ); } } #if XIWS != XIWS_WM pix_hdr_bottom = p.v + leading + ascent + descent + BORDER_WIDTH + RULE_Y_OFFSET_BOTTOM + RULE_Y_OFFSET_TOP + 1; pix_hdr_bottom = max( pix_hdr_bottom, p.v + list_def->min_heading_height + BORDER_WIDTH ); #else pix_hdr_bottom = p.v + leading + ascent + descent + BORDER_WIDTH; pix_hdr_bottom = max( pix_hdr_bottom, p.v + list_def->min_heading_height ); #endif if ( list_def->no_heading ) { int first_row_y_pos = p.v + BORDER_WIDTH; *first_row_y = first_row_y_pos; } else { *first_row_y = pix_hdr_bottom + BORDER_WIDTH; } if ( xi_def_get_xil_pref( itf_def ) ) { *client_height = list_def->height; } else { if ( list_def->pixel_height ) *client_height = list_def->pixel_height; else { int height; if ( list_def->font ) height = xi_get_fu_height_font( list_def->font ); else if ( fontp ) height = xi_get_fu_height_font( fontp ); else height = xi_get_fu_height_font( xi_get_system_font( ) ); *client_height = list_def->height * height / XI_FU_MULTIPLE; } } *title_height = *first_row_y - p.v; *client_height -= *title_height + BORDER_WIDTH; } /*------------------------------------------------------------------------- function: lm_get_metrics lm_def: definition of a list hborder: to be filled in with the horizontal border width column_div: to be filled in with the column spacing in pixels list_bottom: bottom of the list, in pixels -------------------------------------------------------------------------*/ void lm_get_metrics( XI_OBJ_DEF * obj_def, int *hborder, int *column_div, int *list_bottom ) { if ( hborder != NULL ) *hborder = BORDER_WIDTH; if ( column_div != NULL ) *column_div = lm_get_col_spacing( ); if ( obj_def->v.list ) { int pix_row_spacing, nbr_rows, height, pix_row1_top, title_height; lm_get_vertical_metrics( obj_def, &pix_row1_top, &pix_row_spacing, &height, &title_height ); if ( obj_def->v.list->one_row_list ) nbr_rows = 1; else nbr_rows = height / pix_row_spacing; #if XIWS == XIWS_WM *list_bottom = pix_row1_top + nbr_rows * pix_row_spacing + BORDER_WIDTH; #else *list_bottom = pix_row1_top + nbr_rows * pix_row_spacing + ( BORDER_WIDTH - RULE_WIDTH_H ); #endif } } /*------------------------------------------------------------------------- function: lm_set_hscroll_bar lm: current lm notes: Calculate the percentage for the horizontal scroll bar, and set the thumb position on the horizontal scroll bar. -------------------------------------------------------------------------*/ void lm_set_hscroll_bar( LM lm ) { XI_OBJ *list_obj; int p; LM_DATA *lmp; int rng1, rng2; int prop; lmp = ( LM_DATA * ) lm; list_obj = lmp->list_obj; if ( list_obj->v.list->hsb_win ) { XinScrollBarRangeGet( list_obj->v.list->hsb_win, XinScrollBarTypeEither, &rng1, &rng2 ); prop = XinScrollBarProportionGet( list_obj->v.list->hsb_win, XinScrollBarTypeEither ); p = lmp->delta_x; if ( p > rng2 - prop ) p = rng2 - prop; XinScrollBarPositionSet( list_obj->v.list->hsb_win, XinScrollBarTypeEither, p ); } } /*------------------------------------------------------------------------- function: lm_create_column lcdef: column definition not_creating_list: if TRUE, do all of the cell requests for the column in_hscrolling: if TRUE, and if on border between fixed and scrolling columns -------------------------------------------------------------------------*/ void lm_create_column( LM lm, LM_COLUMN_DEF * lcdef, BOOLEAN not_creating_list, BOOLEAN in_hscrolling ) { LM_COLUMN_DATA *lcdata; LM_COLUMN_DATA **column_data; LM_DATA *lmp = LMP( lm ); XinRect rct_to_invalidate; int i; int position, col_spacing; int focus_row, focus_column; BOOLEAN made_invis = FALSE; col_spacing = lm_get_col_spacing( ); position = min( lcdef->position, lmp->nbr_columns ); if ( lm_focus_list_has( lmp ) ) { BOOLEAN v_scrolled; lm_focus_cell_get( lmp, &focus_row, &focus_column, &v_scrolled ); if ( focus_column >= position ) ++focus_column; lm_focus_cell_set( lmp, focus_row, focus_column, v_scrolled ); if ( lm_focus_state_get( lmp ) != LM_FOCUS_VISIBLE ) { lm_focus_cell_invis_make( lmp ); made_invis = TRUE; } } lcdata = ( LM_COLUMN_DATA * ) xi_tree_malloc( sizeof( LM_COLUMN_DATA ), ( char * ) lm ); lcdata->attrib = lcdef->attrib; /* lcdata->pix_width = lcdef->width * lmp->pix_char_width; lcdata->width = lcdef->width; */ lcdata->pix_width = lcdef->pix_width; lcdata->width = lcdef->pix_width / lmp->pix_char_width; calc_x_pix_pos( lm, lcdata, position ); lcdata->text_size = lcdef->text_size; lcdata->center_heading = lcdef->center_heading; lcdata->heading_well = lcdef->heading_well; lcdata->heading_platform = lcdef->heading_platform; lcdata->column_well = lcdef->column_well; lcdata->column_platform = lcdef->column_platform; lcdata->heading_text = ( char * ) xi_tree_malloc( strlen( lcdef->heading_text ) + 1, ( char * ) lm ); strcpy( lcdata->heading_text, lcdef->heading_text ); if ( lcdef->font ) lcdata->font = lcdef->font; lcdata->icon_rid = lcdef->icon_rid; lcdata->icon_x = lcdef->icon_x; lcdata->icon_y = lcdef->icon_y; lcdata->bitmap = lcdef->bitmap; lcdata->size_rows = lcdef->size_rows; lcdata->suppress_update_heading = lcdef->suppress_update_heading; lcdata->suppress_update_cells = lcdef->suppress_update_cells; lcdata->vertical_align_center = lcdef->vertical_align_center; lcdata->vertical_align_bottom = lcdef->vertical_align_bottom; lcdata->wrap_text = lcdef->wrap_text; lcdata->wrap_text_scrollbar = lcdef->wrap_text_scrollbar; lcdata->cr_ok = lcdef->cr_ok; lcdata->var_len_text = lcdef->var_len_text; lcdata->auto_tab = lcdef->auto_tab; lcdata->icon_mode = lcdef->icon_mode; /* allocate new columns */ column_data = lmp->lm_column_data = ( LM_COLUMN_DATA * * ) xi_tree_realloc2( ( char * ) lmp->lm_column_data, sizeof( LM_COLUMN_DATA * ) * ( lmp->nbr_columns + 1 ), ( char * ) lm ); /* move column pointers around in the column list */ for ( i = lmp->nbr_columns; i > position; i-- ) { column_data[i] = column_data[i - 1]; column_data[i]->x_pix_pos += lcdata->pix_width + col_spacing; } column_data[position] = lcdata; lmp->nbr_columns++; /* create space for cell data */ /* cell_data_construct */ for ( i = 0; i < lmp->realized_rows_array_len; i++ ) { int j; lmp->cell_data[i] = ( LM_CELL_DATA * ) xi_tree_realloc( lmp->cell_data[i], ( lmp->nbr_columns + 1 ) * sizeof( LM_CELL_DATA ) ); for ( j = lmp->nbr_columns; j > position; j-- ) lmp->cell_data[i][j] = lmp->cell_data[i][j - 1]; memset( ( char * ) &lmp->cell_data[i][position], '\0', sizeof( LM_CELL_DATA ) ); } if ( not_creating_list ) { for ( i = 0; i < lmp->nbr_realized_rows; ++i ) lm_xi_text_construct( lmp, i, position ); } if ( ( not_creating_list ) && ( position < lmp->fixed_columns ) ) { lmp->first_vis++; lmp->fixed_columns++; } if ( not_creating_list && ( !in_hscrolling ) && position == lmp->fixed_columns ) { lmp->first_vis++; lmp->fixed_columns++; } if ( lmp->first_vis < lmp->fixed_columns && lmp->nbr_columns > lmp->fixed_columns ) lmp->first_vis = lmp->fixed_columns; /* adjust bounding rectangle */ calc_lmp_rct_right( lmp ); /* 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; } if ( not_creating_list ) { lm_invalidate_rows_internal( lm, 0, INT_MAX, FALSE, position, TRUE ); calculate_pix_offsets( lmp, TRUE ); } if ( position >= lmp->fixed_columns && column_data[position]->x_pix_pos < lmp->delta_x ) { lmp->delta_x += ( column_data[position]->pix_width + lm_get_col_spacing( ) ); ++lmp->first_vis; lm_set_hscroll_range( ( LM ) lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } else { /* invalidate changed rectangle */ lm_get_list_rct( lmp, &rct_to_invalidate ); /* if we add the following two lines in, it prevents flashing of the top * and bottom borders when moving columns, however, the top and bottom * borders need to get invalidated rct_to_invalidate.top += BORDER_WIDTH; * rct_to_invalidate.bottom -= BORDER_WIDTH; */ rct_to_invalidate.left = lcdata->x_pix_pos; lm_adj_h( lmp, &rct_to_invalidate.left ); if ( rct_to_invalidate.bottom > rct_to_invalidate.top && rct_to_invalidate.right > rct_to_invalidate.left ) lm_invalidate_rect2( lmp, &rct_to_invalidate, FALSE ); } lm_calc_last_vis( lmp ); lmp->list_obj->v.list->have_sb_rct = FALSE; if ( not_creating_list && lmp->list_obj->v.list->hsb_win ) { XinRect rct; lmp->list_obj->v.list->have_hsb_rct = FALSE; 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 ); } if ( xi_get_pref( XI_PREF_KEEP_FOCUS_FIXED ) ) { if ( not_creating_list && made_invis ) lm_focus_cell_visible_attempt( lmp ); } } /*------------------------------------------------------------------------- function: do_lm_cb_column lm: current lm cb_reason: one of LM_CB_COL_DELETE, LM_CB_COL_MOVE, LM_CB_COL_SIZE col_nbr: column being deleted, moved, or re-sized new_col_nbr: new column number on LM_CB_COL_MOVE new_col_width: new column width on LM_CB_COL_SIZE new_col_pixel_width: new column pixel width on LM_CB_COL_SIZE in_fixed: if TRUE, then column is being moved to fixed portion of list returns: TRUE if event refused -------------------------------------------------------------------------*/ static BOOLEAN do_lm_cb_column( LM lm, LM_CB_TYPE cb_reason, int col_nbr, int new_col_nbr, int new_col_width, int new_col_pixel_width, BOOLEAN in_fixed ) { LM_CB_DATA lm_cb_data; lm_cb_data.lm = lm; lm_cb_data.cb_type = cb_reason; lm_cb_data.cid = LMP( lm )->cid; lm_cb_data.win = LMP( lm )->win; lm_cb_data.column = ( unsigned char ) col_nbr; lm_cb_data.v.column.new_col_nbr = new_col_nbr; lm_cb_data.v.column.in_fixed = in_fixed; lm_cb_data.v.column.new_col_width = new_col_width; lm_cb_data.v.column.new_col_pixel_width = new_col_pixel_width; lm_cb_data.v.column.refused = FALSE; ( *LMP( lm )->lm_cb ) ( &lm_cb_data ); return ( lm_cb_data.v.column.refused ); } /*------------------------------------------------------------------------- function: get_rubber_rect lmp: current lmp rctp: rectangle to be filled in x: current mouse x position y: current mouse y position -------------------------------------------------------------------------*/ static void get_rubber_rect( LM_DATA * lmp, XinRect * rctp, int x, int y, BOOLEAN last_in_hscrolling ) { XinRect rct; if ( lmp->moving_column ) { int col, delta_x, delta_y; col = lmp->column_being_moved; /* lm_get_cell_rect returns the exact physical rectangle of the cell - not * shifted to the right or to the left. */ lm_get_cell_rect( &rct, ( LM ) lmp, 1, col, FALSE, TRUE ); rct.top = lmp->pix_top + BORDER_WIDTH; rct.bottom = lmp->pix_hdr_bottom; delta_x = x - lmp->org_x; if ( lmp->down_in_hscrolling && !last_in_hscrolling ) delta_x += lmp->delta_x; if ( !lmp->down_in_hscrolling && last_in_hscrolling ) delta_x -= lmp->delta_x; delta_y = y - lmp->org_y; rct.left += delta_x; rct.right += delta_x; rct.top += delta_y; rct.bottom += delta_y; } else { lm_get_rect( ( LM ) lmp, LM_LIST, 0, &rct ); rct.top = y; rct.bottom = rct.top + lmp->drag_row_height; rct.right += x - rct.left; rct.left = x; } *rctp = rct; } /*------------------------------------------------------------------------- function: rubber_rect lmp: current lmp -------------------------------------------------------------------------*/ static void rubber_rect( LM_DATA * lmp ) { #if XIWS != XIWS_WM XinRect rct; XinDrawTools new_ctools; XinWindow win = lmp->win; if ( lmp->last_itf ) win = xi_get_window( lmp->last_itf ); XinWindowDrawToolsNormalGet( &new_ctools ); XinWindowDrawToolsSet( win, &new_ctools ); XinWindowPenSet( win, &rubber_cpen ); XinWindowBrushSet( win, &hollow_cbrush ); XinWindowDrawModeSet( win, XinDrawModeXor ); xi_set_clip( win, NULL ); get_rubber_rect( lmp, &rct, lmp->last_x, lmp->last_y, lmp->last_in_hscrolling ); xi_draw_rect( win, &rct ); #else NOREF( lmp ); #endif } /*------------------------------------------------------------------------- function: lm_move_event lmp: current lmp ep: xvt event -------------------------------------------------------------------------*/ #define XI_AUTOSCROLL_COUNT 8 /* Number of move events before autoscroll * occurs */ #define XI_AUTOSCROLL_FAST 50 /* Number of pixels to get to fast autoscroll */ void lm_move_event( LM_DATA * lmp, XinEvent * ep ) { XinPoint where; int col_offset; col_offset = ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); where = ep->v.mouse.where; switch ( ep->type ) { case XinEventMouseDown: case XinEventMouseDouble: lmp->last_x = where.h; lmp->org_x = lmp->last_x; lmp->last_y = where.v; lmp->org_y = lmp->last_y; lmp->last_in_hscrolling = lmp->in_hscrolling; rubber_rect( lmp ); break; case XinEventMouseMove: { static autoscroll_count = 0; BOOLEAN scrolled = FALSE; if ( lmp->pixel_width != 0 ) { /* Check for autoscroll */ if ( where.h - lmp->delta_x > lmp->vir_right ) { if ( where.h - lmp->delta_x > lmp->vir_right + XI_AUTOSCROLL_FAST || ++autoscroll_count >= XI_AUTOSCROLL_COUNT ) { autoscroll_count = 0; rubber_rect( lmp ); lm_hscroll( ( LM ) lmp, 1, 0 ); scrolled = TRUE; } } else if ( where.h < lmp->vir_left ) { if ( where.h < lmp->vir_left - XI_AUTOSCROLL_FAST || ++autoscroll_count >= XI_AUTOSCROLL_COUNT ) { autoscroll_count = 0; rubber_rect( lmp ); lm_hscroll( ( LM ) lmp, -1, 0 ); scrolled = TRUE; } } } else autoscroll_count = 0; if ( scrolled || where.h != lmp->last_x || where.v != lmp->last_y ) { if ( !scrolled ) rubber_rect( lmp ); lmp->last_x = where.h; lmp->last_y = where.v; lmp->last_in_hscrolling = lmp->in_hscrolling; rubber_rect( lmp ); } break; } case XinEventMouseUp: { int column; XI_OBJ *list_obj, **members; int nbr_members, col_cnt, temp1, temp2, dist1, dist2, min_dist, position; XinRect rct; XinPoint where; where = ep->v.mouse.where; rubber_rect( lmp ); column = lmp->column_being_moved; list_obj = lmp->list_obj; if ( list_obj ) get_rubber_rect( lmp, &rct, where.h, where.v, lmp->in_hscrolling ); if ( lmp->drop_and_delete && ( rct.bottom < ( lmp->pix_top - 8 ) || rct.top > ( lmp->pix_row1_top + 8 ) ) ) { /* If the focus is in the column to be deleted, we will move focus as if the tab key had been pressed before deleting the col. This is so the Bridge doesn't try to reference a deleted column object in the OFF_CELL event. */ XI_OBJ *old_focus; BOOLEAN deleteable = TRUE; BOOLEAN return_focus = FALSE; old_focus = lmp->itf_obj->v.itf->focus_obj; if (old_focus->type == XIT_CELL && old_focus->parent == lmp->list_obj && old_focus->v.cell.column == column) { if (adjust_focus_for_column_delete( lmp, column, FALSE )) return_focus = TRUE; else deleteable = FALSE; } if (deleteable && do_lm_cb_column( ( long ) lmp, LM_CB_COL_DELETE, column, 0, 0, 0, FALSE ) == FALSE ) { members = xi_get_member_list( list_obj, &nbr_members ); xi_delete( members[column] ); lm_set_hscroll_range( ( LM ) lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } else if (return_focus) xi_move_focus( old_focus ); } if ( where.v > lmp->pix_top && where.v < lmp->pix_row1_top && where.h >= lmp->rct.left ) { BOOLEAN in_hscrolling, old_in_hscrolling; /* determine where to move the column, if anywhere */ in_hscrolling = FALSE; old_in_hscrolling = ( column >= lmp->fixed_columns ); min_dist = INT_MAX; position = 0; for ( col_cnt = 0; col_cnt < lmp->nbr_columns; col_cnt++ ) { LM_COLUMN_DATA *column_data; if ( col_cnt >= lmp->fixed_columns && col_cnt < lmp->first_vis ) continue; column_data = lmp->lm_column_data[col_cnt]; temp1 = column_data->x_pix_pos; temp2 = temp1 + 2 * col_offset + column_data->pix_width; dist1 = abs( temp1 - where.h ); dist2 = abs( temp2 - where.h ); if ( dist1 < min_dist ) { position = col_cnt; min_dist = dist1; } if ( dist2 < min_dist ) { position = col_cnt + 1; min_dist = dist2; } if ( col_cnt == lmp->fixed_columns ) { if ( where.h < column_data->x_pix_pos ) in_hscrolling = FALSE; else in_hscrolling = TRUE; } } /* if we need to move the column, then move it */ if ( position < column || position > column + 1 || position == lmp->fixed_columns ) { if ( column == position && position == lmp->fixed_columns && in_hscrolling && ( !old_in_hscrolling ) ) position -= 1; else if ( position > column ) position -= 1; /* if there is only one fixed column left, then don't move it */ if ( column != 0 || lmp->fixed_columns != 1 ) { int widest_remaining, col_width, width_remaining, new_fixed_width, cnt; BOOLEAN do_move = TRUE; LM_COLUMN_DATA *column_data, *cell_data2; XinRect r; if ( lmp->fixed_columns < lmp->nbr_columns ) { /* if there is not room in the horizontal scrolling portion of * the list for the widest column remaining in the horizontal * scrolling portion of the list, then don't move the column */ widest_remaining = 0; for ( cnt = lmp->fixed_columns; cnt < lmp->nbr_columns; ++cnt ) { if ( cnt == column ) continue; column_data = lmp->lm_column_data[cnt]; col_width = column_data->pix_width + 2 * col_offset; if ( col_width > widest_remaining ) widest_remaining = col_width; } column_data = lmp->lm_column_data[lmp->fixed_columns]; cell_data2 = lmp->lm_column_data[column]; new_fixed_width = column_data->x_pix_pos; if ( position < lmp->fixed_columns || !in_hscrolling ) new_fixed_width += cell_data2->pix_width + 2 * col_offset; if ( column < lmp->fixed_columns ) new_fixed_width -= cell_data2->pix_width + 2 * col_offset; lm_get_list_rct( lmp, &r ); width_remaining = r.right - r.left - new_fixed_width; if ( widest_remaining > width_remaining ) do_move = FALSE; } if ( do_move ) { BOOLEAN in_fixed = !in_hscrolling; if ( position > lmp->fixed_columns ) in_fixed = FALSE; if ( do_lm_cb_column( ( long ) lmp, LM_CB_COL_MOVE, column, position, 0, 0, in_fixed ) == FALSE ) { members = xi_get_member_list( list_obj, &nbr_members ); xi_move_column_internal( members[column], position, in_hscrolling ); lm_calc_last_vis( lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } } } } } lm_focus_cell_visible_attempt( lmp ); break; } } } /*------------------------------------------------------------------------- function: lm_get_drag_rect lmp: current lmp prct: returns rectangle where row or "after-all" events will be generated -------------------------------------------------------------------------*/ #define DROP_AFTER_MARGIN 10 static void lm_get_drag_rect( LM_DATA * lmp, XinRect * prct ) { lm_get_rect( ( LM ) lmp, LM_LIST, 0, prct ); if ( lmp->nbr_rows == 0 || ( lmp->have_last_rec && lmp->recs[lmp->last_fully_vis] == lmp->last_rec ) ) { if ( lmp->pixel_width != 0 ) { XinRect hsb_rct; xi_get_hsb_rect( lmp->list_obj, &hsb_rct ); prct->bottom = hsb_rct.bottom; } else prct->bottom += DROP_AFTER_MARGIN; } } /*------------------------------------------------------------------------- function: lm_get_no_event_rect lmp: current lmp prct: returns rectangle where no drag event will be generated -------------------------------------------------------------------------*/ static void lm_get_no_event_rect( LM_DATA * lmp, XinRect * prct ) { lm_get_rect( ( LM ) lmp, LM_LIST, 0, prct ); if ( lmp->list_obj->v.list->sb_win ) { XinRect rct; xi_get_sb_rect( lmp->list_obj, &rct ); prct->right = rct.right; } if ( lmp->pixel_width != 0 && ( !lmp->have_last_rec || lmp->recs[lmp->last_fully_vis] != lmp->last_rec ) ) { XinRect rct; xi_get_hsb_rect( lmp->list_obj, &rct ); prct->bottom = rct.bottom; } } /*------------------------------------------------------------------------- function: lm_check_interface itf: current interface where: current mouse position, will be translated if new interface is returned returns: new interface, or NULL if no change -------------------------------------------------------------------------*/ static XI_OBJ * lm_check_interface( XI_OBJ * itf, XinPoint * where ) { XinRect rct; XinWindow win; win = xi_get_window( itf ); XinWindowRectGet( win, &rct ); if ( !XinRectPointContained( &rct, where ) ) { XinRect temp; XinPoint pt; XI_OBJ *new_itf; temp.top = temp.bottom = where->v; temp.left = temp.right = where->h; XinWindowRectTranslate( win, XinWindowTaskGet( ), &temp ); pt.v = temp.top; pt.h = temp.left; new_itf = xi_get_itf_containing( &pt ); if ( new_itf == itf ) itf = NULL; else itf = new_itf; if ( itf != NULL ) { win = xi_get_window( itf ); XinWindowRectTranslate( XinWindowTaskGet( ), win, &temp ); where->v = temp.top; where->h = temp.left; XinWindowMouseRelease( ); XinWindowFrontSet( win ); XinWindowPaintForce( win ); XinWindowMouseTrap( win, TRUE ); } return itf; } return NULL; } /*------------------------------------------------------------------------- function: lm_drag_row_hit_test lmp: current lmp itf: current interface where: current mouse position, will be translated if new interface is returned returns: new interface, or NULL if no change -------------------------------------------------------------------------*/ static BOOLEAN lm_drag_row_hit_test( LM_DATA * lmp, XinPoint * where, int *rowp ) { int tmp_v, i, first, last; int *pix_offsetsp; int *pix_heightsp; /* 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 ) ) { *rowp = i; break; } } return ( i <= last ); } /*------------------------------------------------------------------------- function: lm_drag_row_event itf: interface where event actually occured lmp: current lmp ep: xin event oevp: orginal xin event -------------------------------------------------------------------------*/ void lm_drag_row_event( XI_OBJ * itf, LM_DATA * lmp, XinEvent * ep, XinEvent * oevp ) { XinPoint where; where = oevp->v.mouse.where; switch ( ep->type ) { case XinEventMouseMove: { static autoscroll_count = 0; BOOLEAN scrolled = FALSE; XI_OBJ *new_itf; if ( !lmp->dragging_row ) { lmp->dragging_row = TRUE; lmp->last_itf = itf; lmp->last_x = where.h; lmp->org_x = lmp->last_x; lmp->last_y = where.v; lmp->org_y = lmp->last_y; lmp->last_in_hscrolling = lmp->in_hscrolling; xi_set_drag_list_obj( lmp->list_obj ); rubber_rect( lmp ); break; } new_itf = lm_check_interface( itf, &where ); if ( new_itf == NULL && lmp->itf_obj == itf && lmp->drag_rows_autoscroll ) { /* Test for autoscroll */ XinRect list_rct; lm_get_rect( ( LM ) lmp, LM_LIST, 0, &list_rct ); list_rct.top = lmp->pix_row1_top; if ( where.v > list_rct.bottom ) { if ( where.v > list_rct.bottom + XI_AUTOSCROLL_FAST || ++autoscroll_count >= XI_AUTOSCROLL_COUNT ) { LM_SCROLL_ARG arg; autoscroll_count = 0; rubber_rect( lmp ); MEMCLEAR( arg ); arg.lm = ( LM ) lmp; arg.nbr_lines = 1; arg.percent = 0; arg.same_cell = TRUE; arg.rec_at_top = TRUE; lm_scroll( &arg ); scrolled = TRUE; } } else if ( where.v < list_rct.top ) { if ( where.v < list_rct.top - XI_AUTOSCROLL_FAST || ++autoscroll_count >= XI_AUTOSCROLL_COUNT ) { LM_SCROLL_ARG arg; autoscroll_count = 0; rubber_rect( lmp ); MEMCLEAR( arg ); arg.lm = ( LM ) lmp; arg.nbr_lines = -1; arg.percent = 0; arg.same_cell = TRUE; arg.rec_at_top = TRUE; lm_scroll( &arg ); scrolled = TRUE; } } else autoscroll_count = 0; } if ( scrolled || where.h != lmp->last_x || where.v != lmp->last_y ) { if ( !scrolled ) rubber_rect( lmp ); if ( new_itf != NULL ) lmp->last_itf = new_itf; lmp->last_x = where.h; lmp->last_y = where.v; lmp->last_in_hscrolling = lmp->in_hscrolling; rubber_rect( lmp ); } break; } case XinEventMouseUp: { int obj_num; XI_OBJ **objlist; XI_OBJ *dest_list; LM_DATA *dest_lmp; rubber_rect( lmp ); lmp->last_itf = NULL; lmp->dragging_row = FALSE; xi_set_drag_list_obj( NULL ); /* Check to see if window changed */ { XI_OBJ *new_itf; new_itf = lm_check_interface( itf, &where ); if ( new_itf != NULL ) itf = new_itf; } /* Find list object in current position */ dest_list = lmp->list_obj; objlist = xi_get_member_list( itf, &obj_num ); for ( ; obj_num > 0; obj_num--, objlist++ ) if ( ( *objlist )->type == XIT_LIST ) { XinRect rct; lm_get_drag_rect( ( LM_DATA * ) ( *objlist )->v.list->lm, &rct ); if ( XinRectPointContained( &rct, &where ) ) { dest_list = *objlist; break; } } dest_lmp = ( LM_DATA * ) dest_list->v.list->lm; /* Match coordinates to window */ if ( dest_lmp->itf_obj != itf ) { XinRect temp; temp.top = temp.bottom = where.v; temp.left = temp.right = where.h; XinWindowRectTranslate( xi_get_window( itf ), xi_get_window( lmp->itf_obj ), &temp ); where.v = temp.top; where.h = temp.left; } { /* Generate drop rows event */ int row; int column; LM_CB_DATA lm_cb_data; XinRect list_rct; lm_cb_data.v.drop_row.src_rec = lmp->rec_being_moved; lm_cb_data.v.drop_row.after_all_rows = FALSE; lm_cb_data.v.drop_row.delete_row = FALSE; lm_cb_data.rec = 0; lm_get_drag_rect( dest_lmp, &list_rct ); if ( !XinRectPointContained( &list_rct, &where ) ) { lm_get_no_event_rect( dest_lmp, &list_rct ); if ( XinRectPointContained( &list_rct, &where ) ) break; row = column = 0; lm_cb_data.v.drop_row.delete_row = TRUE; } else { if ( where.v < dest_lmp->pix_row1_top ) break; if ( lm_drag_row_hit_test( dest_lmp, &where, &row ) ) lm_cb_data.rec = dest_lmp->recs[row]; else lm_cb_data.v.drop_row.after_all_rows = TRUE; } lm_cb_data.lm = ( LM ) dest_lmp; lm_cb_data.cb_type = LM_CB_DROP_ROW; lm_cb_data.cid = lmp->cid; lm_cb_data.win = lmp->win; lm_cb_data.row = ( unsigned char ) row; lm_cb_data.column = ( unsigned char ) column; lm_cb_data.v.drop_row.shift = ep->v.mouse.shift; lm_cb_data.v.drop_row.control = ep->v.mouse.control; lm_cb_data.v.drop_row.src_list = lmp->list_obj; ( *lmp->lm_cb ) ( &lm_cb_data ); } break; } } } /*------------------------------------------------------------------------- function: calc_x lmp: current lmp ep: xvt event returns: Calculates and returns the X pixel position of the rubber band line. Used only when sizing columns. -------------------------------------------------------------------------*/ static int calc_x( LM_DATA * lmp, XinEvent * ep ) { int column = lmp->column_being_sized; int temp, temp2, widest_remaining; int min_width_in_pix = 16; int col_offset; col_offset = ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); /* temp is the min width position of the column, relative to the left edge of * the list. */ temp = lmp->lm_column_data[column]->x_pix_pos + col_offset + min_width_in_pix; if ( lmp->lm_column_data[column]->wrap_text_scrollbar ) temp += (int)XinMetricGet( XinMetricVerticalScrollBarWidth ); temp = max( ep->v.mouse.where.h, temp ); widest_remaining = 0; if ( lmp->pixel_width ) { int col_offset, col_width, cnt; LM_COLUMN_DATA *column_data; col_offset = ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); if ( column < lmp->fixed_columns ) { /* figure out the widest column in the horizontal scrolling portion of * the list */ for ( cnt = lmp->fixed_columns; cnt < lmp->nbr_columns; ++cnt ) { column_data = lmp->lm_column_data[cnt]; col_width = column_data->pix_width + 2 * col_offset; if ( col_width > widest_remaining ) widest_remaining = col_width; } /* add the widths of all of the columns to the right of this column in * the fixed portion of the list */ temp2 = 0; for ( cnt = column + 1; cnt < lmp->fixed_columns; ++cnt ) { column_data = lmp->lm_column_data[cnt]; col_width = column_data->pix_width + 2 * col_offset; temp2 += col_width; } temp = min( ( lmp->pixel_width - widest_remaining - temp2 ) - BORDER_WIDTH, temp ); } else temp = min( ( lmp->pixel_width + lmp->delta_x ) - BORDER_WIDTH, temp ); } return temp; } /*------------------------------------------------------------------------- function: rubber_x lmp: current lmp x: draw a rubber line at position x -------------------------------------------------------------------------*/ static void rubber_x( LM_DATA * lmp, int x ) { #if XIWS != XIWS_WM int top, bottom; XinDrawTools new_ctools; XinPoint pnt; XinWindow win = lmp->win; top = lmp->pix_top; bottom = lmp->mlr.bottom; XinWindowDrawToolsNormalGet( &new_ctools ); XinWindowDrawToolsSet( win, &new_ctools ); XinWindowPenSet( win, &rubber_cpen ); XinWindowDrawModeSet( win, XinDrawModeXor ); xi_set_clip( win, NULL ); pnt.h = x; pnt.v = top; if ( !lmp->down_in_hscrolling ) pnt.h += lmp->rct.left; lm_move_to( lmp, pnt, FALSE, lmp->down_in_hscrolling ); pnt.v = bottom; lm_draw_line( lmp, pnt, FALSE, lmp->down_in_hscrolling ); #else NOREF( lmp ); NOREF( x ); #endif } /*------------------------------------------------------------------------- function: lm_size_event lmp: current lmp ep: xvt event -------------------------------------------------------------------------*/ void lm_size_event( LM_DATA * lmp, XinEvent * ep ) { int x; switch ( ep->type ) { case XinEventMouseDown: case XinEventMouseDouble: x = calc_x( lmp, ep ); lmp->last_x = x; rubber_x( lmp, x ); break; case XinEventMouseMove: x = calc_x( lmp, ep ); rubber_x( lmp, lmp->last_x ); lmp->last_x = x; rubber_x( lmp, x ); break; case XinEventMouseUp: { int col_offset; int column; int temp; int width_in_chars; int pixel_width; XI_OBJ *list_obj, **members; int nbr_members; int char_width; LM_COLUMN_DATA *lm_column_data; col_offset = ( int ) xi_get_pref( XI_PREF_COLUMN_OFFSET ); x = calc_x( lmp, ep ); rubber_x( lmp, lmp->last_x ); column = lmp->column_being_sized; lm_column_data = lmp->lm_column_data[column]; temp = lm_column_data->x_pix_pos + col_offset; pixel_width = ( x - temp ) - col_offset; width_in_chars = ( int ) ( pixel_width / ( long ) xi_get_fu_width( lmp->itf_obj ) ); list_obj = lmp->list_obj; char_width = width_in_chars * XI_FU_MULTIPLE; if ( xi_get_xil_pref( list_obj->itf ) ) char_width = width_in_chars; if ( do_lm_cb_column( ( long ) lmp, LM_CB_COL_SIZE, column, 0, char_width, pixel_width, FALSE ) == FALSE ) { int i; members = xi_get_member_list( list_obj, &nbr_members ); xi_column_set_pixel_width( members[column], pixel_width ); i = lm_get_left_most_far_right_col( lmp, lmp->nbr_columns ); if ( i < lmp->first_vis ) { XinWindowPaintForce( lmp->win ); lm_hscroll( ( LM ) lmp, lmp->first_vis - i, 0 ); } else { lm_calc_last_vis( lmp ); lm_set_hscroll_bar( ( LM ) lmp ); } } lm_focus_cell_visible_attempt( lmp ); break; } } } /*------------------------------------------------------------------------- function: lm_set_bitmap lm: current lm bitmap: bitmap object row: column: -------------------------------------------------------------------------*/ void lm_set_bitmap( LM lm, XI_BITMAP* bitmap, int row, int column ) { LM_DATA *lmp = LMP( lm ); lmp->cell_data[row][column].bitmap = xi_bitmap_copy( bitmap ); if ( LMP( lm )->attrib & XI_ATR_VISIBLE ) redraw_cell( lm, row, column, FALSE ); } /*------------------------------------------------------------------------- function: lm_set_icon lm: current lm icon_rid: icon resource id row: column: -------------------------------------------------------------------------*/ void lm_set_icon( LM lm, int icon_rid, int row, int column ) { LM_DATA *lmp = LMP( lm ); lmp->cell_data[row][column].icon_rid = icon_rid; if ( LMP( lm )->attrib & XI_ATR_VISIBLE ) redraw_cell( lm, row, column, FALSE ); } /*------------------------------------------------------------------------- function: lm_set_column_bitmap lm: current lm bitmap: bitmap object cid: cid of the column to set -------------------------------------------------------------------------*/ void lm_set_column_bitmap( LM lm, XI_BITMAP* bitmap, int cid ) { LM_DATA *lmp = LMP( lm ); XI_OBJ *list = lmp->list_obj; int column; for ( column = 0; column < list->nbr_children; column++ ) { if ( list->children[column]->cid == cid ) { XinRect rect; lmp->lm_column_data[column]->bitmap = xi_bitmap_copy( bitmap ); lm_get_rect( lm, LM_COLUMN, column, &rect ); xi_invalidate_rect( lmp->win, &rect ); break; } } } /*------------------------------------------------------------------------- function: lm_set_column_icon lm: current lm icon_rid: icon resource id cid: cid of the column to set -------------------------------------------------------------------------*/ void lm_set_column_icon( LM lm, int icon_rid, int cid ) { LM_DATA *lmp = LMP( lm ); XI_OBJ *list = lmp->list_obj; int column; for ( column = 0; column < list->nbr_children; column++ ) { if ( list->children[column]->cid == cid ) { XinRect rect; lmp->lm_column_data[column]->icon_rid = icon_rid; lm_get_rect( lm, LM_COLUMN, column, &rect ); xi_invalidate_rect( lmp->win, &rect ); break; } } } /*------------------------------------------------------------------------- function: lm_set_sel lm: current lm row: row column: column c1: starting position in selection c2: ending position -------------------------------------------------------------------------*/ void lm_set_sel( LM lm, int row, int column, BOOLEAN v_scrolled, int c1, int c2 ) { LM_DATA *lmp = LMP( lm ); if ( !lm_focus_list_has( lmp ) ) { lm_focus_cell_set( lmp, row, column, FALSE ); } else { int focus_row, focus_column; BOOLEAN is_v_scrolled; lm_focus_cell_get( lmp, &focus_row, &focus_column, &is_v_scrolled ); if ( focus_row != row || focus_column != column || v_scrolled != is_v_scrolled ) lm_focus_cell_set( lmp, row, column, FALSE ); } if ( v_scrolled ) { lm_focus_cell_selection_set( lmp, c1, c2 ); } else { XI_TEXT *text; text = xi_text_focus_get( lmp->win ); if ( text ) xi_text_selection_set( text, c1, c2 ); } } /*------------------------------------------------------------------------- function: scroll_list lmp: current lmp left_col: right_col: -------------------------------------------------------------------------*/ static void scroll_list( LM_DATA * lmp, int new_first_col ) { XinRect r; int new_pos; int dist; new_pos = lmp->lm_column_data[new_first_col]->x_pix_pos - lmp->lm_column_data[lmp->fixed_columns]->x_pix_pos; if ( new_pos == lmp->delta_x ) { if ( lmp->nbr_columns == lmp->fixed_columns ) lmp->delta_x = 0; return; } dist = new_pos - lmp->delta_x; lmp->delta_x += dist; r.left = lmp->vir_left; r.right = lmp->vir_right; #if XIWS != XIWS_WM r.top = lmp->rct.top + BORDER_WIDTH; r.bottom = lmp->rct.bottom - BORDER_WIDTH; #else r.top = lmp->rct.top; r.bottom = lmp->rct.bottom; #endif if ( !lmp->itf_obj->v.itf->half_baked ) XinWindowPaintForce( lmp->win ); xi_set_update_obj( lmp->list_obj ); lmp->last_vis = lmp->first_vis; lm_calc_last_vis( lmp ); xi_scroll_rect( lmp->win, &r, -dist, 0 ); if ( !lmp->itf_obj->v.itf->half_baked ) XinWindowPaintForce( lmp->win ); lm_set_hscroll_bar( ( LM ) lmp ); } /*------------------------------------------------------------------------- function: lm_hscroll lm: current lm nbr_columns: number of columns to scroll pos: if set, pos is a percentage based on where the thumb was dropped. -------------------------------------------------------------------------*/ void lm_hscroll( LM lm, int nbr_columns, int pos ) { LM_DATA *lmp = LMP( lm ); int last_vis, lmfrc, starting_column; XinWindowPaintForce( lmp->win ); if ( lmp->nbr_columns == 0 ) return; lmfrc = lm_get_left_most_far_right_col( LMP( lm ), lmp->nbr_columns ); last_vis = min( lmp->last_vis, lmp->nbr_columns - 1 ); if ( xi_get_pref( XI_PREF_KEEP_FOCUS_FIXED ) ) lm_focus_cell_invis_make( lmp ); else { if ( !lmp->itf_obj->v.itf->moving_focus ) if ( !xi_move_focus( lmp->itf_obj ) ) return; } if ( nbr_columns == XI_SCROLL_FIRST ) { starting_column = lmfrc; if ( starting_column ) { starting_column = starting_column - lmp->fixed_columns; starting_column = ( int ) ( ( ( long ) pos * ( long ) starting_column ) / ( long ) 100 ); } starting_column += lmp->fixed_columns; lmp->first_vis = starting_column; lmp->first_vis = min( lmp->first_vis, lmp->nbr_columns - 1 ); scroll_list( lmp, lmp->first_vis ); lm_focus_cell_visible_attempt( lmp ); return; } if ( nbr_columns == XI_SCROLL_PGDOWN ) { starting_column = min( last_vis + 1, lmfrc ); starting_column = min( starting_column, lmp->nbr_columns - 1 ); lmp->first_vis = starting_column; lmp->first_vis = min( lmp->first_vis, lmp->nbr_columns - 1 ); scroll_list( lmp, lmp->first_vis ); lm_focus_cell_visible_attempt( lmp ); return; } if ( nbr_columns == XI_SCROLL_PGUP ) { starting_column = lm_get_left_most_far_right_col( lmp, lmp->first_vis ); starting_column = min( starting_column, lmp->nbr_columns - 1 ); lmp->first_vis = starting_column; lmp->first_vis = min( lmp->first_vis, lmp->nbr_columns - 1 ); scroll_list( lmp, lmp->first_vis ); lm_focus_cell_visible_attempt( lmp ); return; } lmp->first_vis += nbr_columns; if ( nbr_columns > 0 ) { lmp->first_vis = min( lmp->first_vis, lm_get_left_most_far_right_col( lmp, lmp->nbr_columns ) ); lmp->first_vis = min( lmp->first_vis, lmp->nbr_columns - 1 ); } else { lmp->first_vis = max( lmp->first_vis, lmp->fixed_columns ); lmp->first_vis = min( lmp->first_vis, lmp->nbr_columns - 1 ); } scroll_list( lmp, lmp->first_vis ); lm_focus_cell_visible_attempt( lmp ); } void lm_calculate_pix_offsets( LM lm ) { LM_DATA *lmp = LMP( lm ); calculate_pix_offsets( lmp, TRUE ); lm_make_rrr_room_pix( lmp, 0, FALSE ); } BOOLEAN lm_cr_is_ok( LM lm, int row, int col, BOOLEAN v_scrolled ) { LM_DATA *lmp = LMP( lm ); LM_COLUMN_DATA *lm_column_data; LM_CELL_DATA *cell_data; lm_column_data = lmp->lm_column_data[col]; if ( lm_column_data->cr_ok ) return TRUE; if ( v_scrolled ) { if ( lm_focus_cell_is_button_full_cell( lmp ) ) return TRUE; else return FALSE; } cell_data = &lmp->cell_data[row][col]; if ( cell_data->button_full_cell ) return TRUE; else return FALSE; } BOOLEAN lm_is_button_full_cell( LM lm, int row, int col ) { LM_DATA *lmp = LMP( lm ); LM_CELL_DATA *cell_data = &lmp->cell_data[row][col]; return cell_data->button_full_cell; } void lm_set_rect( LM lm, XinRect *rect ) { LM_DATA *lmp; XinRect fu_rect; lmp = (LM_DATA*)lm; fu_rect = *rect; fu_rect.right -= 2 * BORDER_WIDTH; if (lmp->list_obj->v.list->hsb_win) { XinRect hsb_rect; xi_get_hsb_rect( lmp->list_obj, & hsb_rect ); fu_rect.bottom += hsb_rect.bottom - hsb_rect.top; } xi_pu_to_fu( lmp->itf_obj, (XinPoint*)&fu_rect, 2 ); lmp->list_obj->v.list->xi_pnt = *(XinPoint*)&fu_rect; if (lmp->list_obj->v.list->height != 0) lmp->list_obj->v.list->height = fu_rect.bottom - fu_rect.top; if (lmp->list_obj->v.list->width != 0 ) lmp->list_obj->v.list->width = fu_rect.right - fu_rect.left; /* TODO - lm_recalc_metrics gets the proper height for the list not including the hsb, then puts the hsb inside that rect instead of below it. Therefore, the first time you resize, the list gets shorter by a scrollbar height, and thereafter it doesn't change. */ lm_recalc_metrics( lm ); }