/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #ifdef OS2 #include #include #endif #include #if defined(solaris) || defined(sgi) || defined (hp700) || defined(aviion) #include #endif #include #include "NumberP.h" #include "Number.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif /* * Declare constant values at limits for fitting text in string fields */ /* Limits of 32 bit signed integers */ #define maxlong INT_MAX #define minlong INT_MIN #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif static double emax[] = { 1.0, 10.0, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24 }; static double emin[] = { 0.0, -1.0, -10.0, -1e2, -1e3, -1e4, -1e5, -1e6, -1e7, -1e8, -1e9, -1e10, -1e11, -1e12, -1e13, -1e14, -1e15, -1e16, -1e17, -1e18, -1e19, -1e20, -1e21, -1e22, -1e23, -1e24 }; /* * Declare locally defined and used subroutines */ /* Prepare a string of the value in integer format */ static int IString (XmNumberWidget nw, double ival, char* string, int width, char *format); /* Prepare a string of the value in fixed point format */ static int FString (XmNumberWidget nw, double fval, char* string, int width, int points, char* format); /* Prepare a string of the value in whatever format shows optimal precision */ static int GString (XmNumberWidget nw, double gval, char* string, int width); /* Prepare a string with an indication that the value cannot be represented */ static void MarkOverflow (char* string, int string_width, char* label, int label_width); /* Subroutine: XmChangeNumberValue * Purpose: Input a new value and update the display (don't bother with * a full-blown SetValues). */ void XmChangeNumberValue( XmNumberWidget nw, double value ) { /* Update all three value types */ nw->number.value.d = value; if ( (nw->number.data_type == INTEGER) || (nw->number.data_type == FLOAT) ) nw->number.value.f = (float)value; if (nw->number.data_type == INTEGER) { if( value >= 0 ) nw->number.value.i = (int)(value + 0.5); else nw->number.value.i = (int)(value - 0.5); } XmShowNumberValue(nw); } /* Subroutine: XmShowNumberValue * Effect: Draws the number string on the screen */ void XmShowNumberValue( XmNumberWidget nw ) { int pad, char_cnt; char string[32]; GC gc; nw->number.visible = True; if( !XtIsRealized(nw) ) return; if( nw->number.decimal_places == 0 ) pad = IString(nw, nw->number.value.d, string, nw->number.char_places, nw->number.format); else if( nw->editor.is_fixed ) pad = FString(nw, nw->number.value.d, string, nw->number.char_places, nw->number.decimal_places, nw->number.format); else pad = GString(nw, nw->number.value.d, string, nw->number.char_places); /* Check for error return */ if( pad < 0 ) { /* Negative indicates display not possible in requested format */ if( pad == -1 ) /* -1 returned when format successfully switched to G */ pad = 0; else /* <-1 is -(number of chars in error display: e.g. "ovfl") */ pad += nw->number.char_places; } char_cnt = nw->number.char_places - pad; XClearArea(XtDisplay(nw), XtWindow(nw), nw->number.x, nw->number.y, nw->number.width + nw->number.h_margin, nw->number.height, False); /* Calculate the text position x coordinate and print */ if (nw->core.sensitive && nw->core.ancestor_sensitive) { gc = nw->number.gc; } else { gc = nw->number.inv_gc; } if( nw->number.center && (pad > 0) ) XDrawImageString(XtDisplay(nw), XtWindow(nw), gc, nw->number.x + ((pad * nw->number.char_width) / 2), nw->number.y + nw->number.font_struct->ascent + 1, string + pad, nw->number.char_places - pad); else XDrawImageString(XtDisplay(nw), XtWindow(nw), gc, nw->number.x, nw->number.y + nw->number.font_struct->ascent + 1, string, nw->number.char_places); nw->number.last_char_cnt = char_cnt; } /* Subroutine: HideNumberValue * Effect: Makes value display dissappear by clearing to background color */ void XmHideNumberValue( XmNumberWidget nw ) { nw->number.visible = False; XClearArea(XtDisplay(nw), XtWindow(nw), nw->number.x, nw->number.y, nw->number.width + nw->number.h_margin, nw->number.height, False); } /* Subroutine: SetNumberSize * Purpose: Check and adjust all sizes for consistency with value range * and set dimension and format paramters. */ void SetNumberSize( XmNumberWidget nw, Dimension* width, Dimension* height ) { register int max_chars, min_chars; if( !nw->number.font_struct ) { nw->number.width = 0; nw->number.height = 0; *width = 0; *height = 0; return; } /* Figure out how many places must be represented and make sure * * we have enough space */ if( (!nw->editor.is_fixed) && (nw->number.recompute_size) && (nw->number.decimal_places < 0) ) { /* Specified equivalent of %g notation (best fit floating format) */ if( nw->number.value_minimum.d <= -1e10 ) nw->number.char_places = 9; else if( (nw->number.value_minimum.d < 0.0) || (nw->number.value_maximum.d >= 1e10) ) nw->number.char_places = 8; else if( nw->number.char_places < 7 ) nw->number.char_places = 7; } else if( (!nw->editor.is_fixed) && (nw->number.recompute_size) && (nw->number.decimal_places >= 0) ) { if( nw->number.value_minimum.d <= -1e10 ) nw->number.char_places = nw->number.decimal_places + 8; else if( (nw->number.value_minimum.d < 0.0) || (nw->number.value_maximum.d >= 1e10) ) nw->number.char_places = nw->number.decimal_places + 7; else nw->number.char_places = nw->number.decimal_places + 6; } else { if (nw->number.recompute_size) { /* Find the number of digits occupied by the two extremes */ max_chars = 1; while (TRUE) { if (emax[max_chars-1] > nw->number.value_maximum.d) break; max_chars++; } max_chars--; min_chars = 1; while (TRUE) { if (emin[min_chars-1] < nw->number.value_minimum.d) break; min_chars++; } min_chars--; /* Characters needed is value + decimal places, + dot (if used) */ if( min_chars > max_chars ) max_chars = min_chars; if( nw->number.decimal_places > 0 ) max_chars += (1 + nw->number.decimal_places); /*If application did not specify enough places, use minimum needed*/ nw->number.char_places = MAX(3,max_chars); } } /* Check reasonableness of decimal places specification */ if( nw->number.decimal_places > 0 ) { if (nw->number.char_places < (nw->number.decimal_places + 3)) nw->number.char_places = nw->number.decimal_places + 3; (void)sprintf(nw->number.format, "%%%1d.%1df", nw->number.char_places, nw->number.decimal_places); } else if( nw->number.decimal_places == 0 ) (void)sprintf(nw->number.format, "%%%1dd", nw->number.char_places ); /* Figure out the sizes of things */ nw->number.char_width = XTextWidth(nw->number.font_struct, "5", 1); nw->number.dot_width = XTextWidth(nw->number.font_struct, ".", 1); if( nw->number.char_width != nw->number.dot_width ) nw->number.proportional = True; nw->number.width = nw->number.char_places * nw->number.char_width; /* Height is that of the font's bitmap */ nw->number.height = nw->number.font_struct->ascent + nw->number.font_struct->descent + 2; nw->number.v_margin = 1; nw->number.h_margin = (nw->number.char_width)/2; { register int offset; /* Compute overall rectangle size resulting following parameters */ offset = nw->primitive.highlight_thickness + nw->primitive.shadow_thickness + nw->number.h_margin; *width = nw->number.width + offset + offset; offset = nw->primitive.highlight_thickness + nw->primitive.shadow_thickness + nw->number.v_margin; *height = 1 + nw->number.height + offset + offset; } } /* Subroutine: IString * Purpose: Create an integer string, include clipping mark if appropriate * Returns: -4=ovfl, -1=Gformat, else the number of leading blank spaces. * Note: to be called with full column width * Note: 2<=width<=10 */ static int IString( XmNumberWidget nw, double ival, char* string, int width, char *format ) #ifdef EXPLAIN double ival; /* Value to be printed */ char* string; /* The output (ival as string) */ int width; /* The number of character places in the output */ char* format; /* String with default %d format */ #endif { int blank; if( ival > 0 ) { if( (ival >= emax[width]) || (ival > maxlong) ) { if( (width >= 6) || (ival < emax[width]) || ((ival < emax[10]) && (width >= 5)) ) { GString(nw, (double)ival, string, width); return -1; } else { MarkOverflow(string, width, "ovfl", 4); return -4; } } } else if( (ival <= emin[width]) || (ival < minlong) ) { if( (width >= 7) || (ival > emin[width]) || ((ival > emin[9]) && (width >= 6)) ) { GString(nw, (double)ival, string, width); return -1; } else { MarkOverflow(string, width, "ovfl", 4); return -4; } } if( ival < 0.0 ) (void)sprintf(string, format, (long)(ival - 0.5)); else (void)sprintf(string, format, (long)(ival + 0.5)); for( blank=0; string[blank] == ' '; blank++ ); return blank; } /* Subroutine: FString * Effect: Place string of the value in fixed point format. If the number * cannot be properly represented, fall back to G format if * possible, else represent an error state (ovfl). * Returns: -4=ovfl, -1=Gformat, else the number of leading blank spaces. */ static int FString( XmNumberWidget nw, double fval, char* string, int width, int points, char* format ) #ifdef EXPLAIN double fval; /* Value to be printed */ char *string; /* The output (ival as string) */ int width; /* The number of character places in the output */ #endif { int blank, size; size = width - (points - 1); if( fval > 0 ) { if( fval >= emax[size] ) { if( (width >= 6) || (fval < emax[width]) || ((fval < emax[10]) && (width >= 5)) ) { GString(nw, fval, string, width); return -1; } else { MarkOverflow(string, width, "ovfl", 4); return -4; } } } else if( fval <= emin[size] ) { if( (width >= 7) || (fval > emin[width]) || ((fval > emin[9]) && (width >= 6)) ) { GString(nw, fval, string, width); return -1; } else { MarkOverflow(string, width, "ovfl", 4); return -4; } } (void)sprintf(string, format, fval); string[width] = '\0'; for( blank=0; string[blank] == ' '; blank++ ); return blank; } static char *fform[] = { "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f", "%.7f", "%.8f", "%.9f", "%.10f", "%.11f", "%.12f", "%.13f", "%.14f", "%.15f", "%.16f", "%.17f", "%.18f", "%.19f", "%.20f", "%.21f", "%.22f", "%.23f", "%.24f", "%.25f" }; static char *eform[] = { "%.0e", "%.1e", "%.2e", "%.3e", "%.4e", "%.5e", "%.6e", "%.7e", "%.8e", "%.9e", "%.10e", "%.11e", "%.12e", "%.13e", "%.14e", "%.15e", "%.16e", "%.17e", "%.18e", "%.19e", "%.20e", "%.21e", "%.22e", "%.23e", "%.24e", "%.25e" }; /* Subroutine: GString * Purpose: Print the real value in best format for character space given * Returns: 0 (no leading space). Returning a value is for compatability. * Note: 8<=width<=15 (7 makes -1e-10\0, 16 goes beyond lookup arrays) * Note: width characters beyond string[width] may get messed up * Method: For speed, compares and formats get values through lookups */ static int GString( XmNumberWidget nw, double gval, char* string, int width ) #ifdef EXPLAIN double gval; /* value to print */ char *string; /* string to recieve number (see note 2 above) */ int width; /* i: number of characters to use */ #endif { int len; int pad; int i; char format[128]; double epsilon; if (gval == 0.0) { if (nw->number.decimal_places < 0) { (void)strcpy(string, "0.0"); } else { (void)sprintf(format, "%%.%df", nw->number.decimal_places); (void)sprintf(string, format, gval); } pad = 0; len = strlen(string); if (len < width) { pad = width - len; for (i = 0; i < len+1; i++) { string[(len - i) + pad] = string[len - i]; } for (i = 0; i < pad; i++) { string[i] = ' '; } } string[width] = '\0'; return (pad); } epsilon = nw->number.cross_under * nw->number.cross_under; if( gval >= 0.0 ) { if((gval + epsilon < nw->number.cross_under) || (gval >= nw->number.cross_over )) { /* Outside range for simple %f format */ if ((nw->editor.is_fixed == False) && (nw->number.decimal_places >= 0)) { (void)sprintf(string, eform[nw->number.decimal_places], gval); pad = 0; len = strlen(string); if (len < width) { pad = width - len; for (i = 0; i < len+1; i++) { string[(len - i) + pad] = string[len - i]; } for (i = 0; i < pad; i++) { string[i] = ' '; } } string[width] = '\0'; return (pad); } else { if( (gval >= 1e-9) && (gval < 1e10) ) { /* Inside range for 1 digit exponent (move digit 2 over) */ if( gval >= 1.0 ) { /* Positive number with 1 digit positive exponent */ (void)sprintf(string, eform[width-4], gval); string[width-1] = string[width+1]; } else { /* Positive number with 1 digit negative exponent */ (void)sprintf(string, eform[width-5], gval); string[width-1] = string[width]; } } else if( gval >= 1.0 ) { /* Positive number with 2 digit positive exponent */ (void)sprintf(string, eform[width-5], gval); string[width-2] = string[width-1]; string[width-1] = string[width]; } else /* Positive number with 2 digit negative exponent */ (void)sprintf(string, eform[width-6], gval); } } else /* Positive number in fixed point notation */ if (nw->number.decimal_places >= 0) { (void)sprintf(string, fform[nw->number.decimal_places], gval); pad = 0; len = strlen(string); if (len < width) { pad = width - len; for (i = 0; i < len+1; i++) { string[(len - i) + pad] = string[len - i]; } for (i = 0; i < pad; i++) { string[i] = ' '; } } string[width] = '\0'; return (pad); } else (void)sprintf(string, fform[width], gval); } else { if((gval - epsilon > -nw->number.cross_under) || (gval <= -nw->number.cross_over)) { if ((nw->editor.is_fixed == False) && (nw->number.decimal_places >= 0)) { (void)sprintf(string, eform[nw->number.decimal_places], gval); pad = 0; len = strlen(string); if (len < width) { pad = width - len; for (i = 0; i < len+1; i++) { string[(len - i) + pad] = string[len - i]; } for (i = 0; i < pad; i++) { string[i] = ' '; } } string[width] = '\0'; return (pad); } else { if( (gval <= -1e-99) && (gval > -1e100) ) { /* Inside range for 1 digit exponent (move digit 2 over) */ if( gval > -1.0 ) { /* Negative number with 1 digit negative exponent */ (void)sprintf(string, eform[width-6], gval); string[width-1] = string[width]; } else { /* Negative number with 1 digit positive exponent */ (void)sprintf(string, eform[width-5], gval); string[width-1] = string[width+1]; } } if( (gval <= -1e-9) && (gval > -1e10) ) { /* Inside range for 1 digit exponent (move digit 2 over) */ if( gval > -1.0 ) { /* Negative number with 1 digit negative exponent */ (void)sprintf(string, eform[width-6], gval); string[width-1] = string[width]; } else { /* Negative number with 1 digit positive exponent */ (void)sprintf(string, eform[width-5], gval); string[width-1] = string[width+1]; } } else if( gval <= -1.0 ) { /* Negative number with 2 digit positive exponent */ (void)sprintf(string, eform[width-6], gval); string[width-2] = string[width-1]; string[width-1] = string[width]; } else /* Negative number with 2 digit negative exponent */ (void)sprintf(string, eform[width-7], gval); } } else /* Negative number in fixed point notation */ if (nw->number.decimal_places >= 0) { (void)sprintf(string, fform[nw->number.decimal_places], gval); pad = 0; len = strlen(string); if (len < width) { pad = width - len; for (i = 0; i < len+1; i++) { string[(len - i) + pad] = string[len - i]; } for (i = 0; i < pad; i++) { string[i] = ' '; } } string[width] = '\0'; return (pad); } else (void)sprintf(string, fform[width], gval); } string[width] = '\0'; return 0; } /* Subroutine: MarkOverflow * Effect: Copies message of label, right justified onto string */ static void MarkOverflow( char* string, int string_width, char* label, int label_width ) #ifdef EXPLAIN char* string; int string_width; char* label; int label_width; #endif { int i, j; for( i=label_width-1, j=string_width-1; i>=0; i--, j-- ) string[j] = label[i]; for( ; j>=0; j-- ) string[j] = ' '; string[string_width] = '\0'; } #if defined(__cplusplus) || defined(c_plusplus) } #endif