/***************************************************************************** Major portions of this software are copyrighted by the Medical College of Wisconsin, 1994-2000, and are released under the Gnu General Public License, Version 2. See the file README.Copyright for details. ******************************************************************************/ #include "mcw_graf.h" #ifndef LABEL_ARG #define LABEL_ARG(str) \ XtVaTypedArg , XmNlabelString , XmRString , (str) , strlen(str)+1 #endif /* graph drawing functions, inspired by the GRAF code in program xv */ #define GRAF_EXTRA 6 #define GRAF_XTICK 16 #define GRAF_NTICK 8 /* 30 Nov 2002: prototypes for internal functions (at end of file) */ static char *get_popup_label( float,float,float,float , int,int ) ; static double clock_time(void) ; MCW_graf * new_MCW_graf( Widget wpar , MCW_DC * dc , char * title , gen_func * cbfunc , void * cbdata ) { MCW_graf * gp ; Widget rcbox , wf ; XmString xstr ; char * curve_label[1] = { "Crv" } ; int ii ; gp = myXtNew(MCW_graf) ; /* make the Widgets */ gp->topform = XtVaCreateWidget( "dialog" , xmFormWidgetClass , wpar , XmNinitialResourcesPersistent , False , XmNtraversalOn , True , NULL ) ; if( title == NULL || title[0] == '\0' ) title = "Graphing" ; xstr = XmStringCreateLtoR( title , XmFONTLIST_DEFAULT_TAG ); gp->toplabel = XtVaCreateManagedWidget( "dialog" , xmLabelWidgetClass , gp->topform , XmNtopAttachment , XmATTACH_FORM , XmNleftAttachment , XmATTACH_FORM , XmNlabelString , xstr , XmNrecomputeSize , False , XmNmarginWidth , 0 , XmNalignment , XmALIGNMENT_BEGINNING , XmNinitialResourcesPersistent , False , NULL ) ; XmStringFree(xstr) ; wf = XtVaCreateWidget( "dialog" , xmFrameWidgetClass , gp->topform , XmNshadowType , XmSHADOW_IN , XmNshadowThickness , 4 , XmNtopAttachment , XmATTACH_WIDGET , XmNtopWidget , gp->toplabel , XmNleftAttachment , XmATTACH_FORM , XmNtraversalOn , True , XmNinitialResourcesPersistent , False , NULL ) ; gp->drawer = XtVaCreateManagedWidget( "dialog" , xmDrawingAreaWidgetClass , wf , XmNwidth , GRAF_SIZE , XmNheight , GRAF_SIZE + GRAF_EXTRA , XmNtraversalOn , True , XmNinitialResourcesPersistent , False , NULL ) ; XtManageChild(wf) ; XtInsertEventHandler( gp->drawer , /* handle events in image */ 0 | ButtonPressMask /* button presses */ | ExposureMask /* exposures */ , FALSE , /* nonmaskable events? */ GRAF_drawing_EV , /* event handler */ (XtPointer) gp , /* client data */ XtListTail ) ; /* last in queue */ /* 30 Nov 2002: popup stuff */ #ifdef BAD_BUTTON3_POPUPS /* 21 Jul 2003 */ gp->popmenu = XmCreatePopupMenu( gp->topform, "menu" , NULL , 0 ) ; #else gp->popmenu = XmCreatePopupMenu( gp->drawer , "menu" , NULL , 0 ) ; #endif gp->poplabel = XtVaCreateManagedWidget( "help" , xmLabelWidgetClass , gp->popmenu , LABEL_ARG("I am a label") , XmNinitialResourcesPersistent , False , NULL ) ; rcbox = XtVaCreateWidget( "dialog" , xmRowColumnWidgetClass , gp->topform , XmNpacking , XmPACK_TIGHT , XmNadjustLast , False , XmNadjustMargin , False , XmNnumColumns , 1 , XmNtopAttachment , XmATTACH_WIDGET , XmNtopWidget , gp->toplabel , XmNleftAttachment , XmATTACH_WIDGET , XmNleftWidget , wf , XmNrightAttachment , XmATTACH_FORM , XmNinitialResourcesPersistent , False , NULL ) ; gp->curve_bbox = new_MCW_bbox( rcbox , 1 , curve_label , MCW_BB_check , MCW_BB_noframe , GRAF_curve_CB , (XtPointer) gp ) ; gp->handle_av = new_MCW_arrowval( rcbox , "#" , MCW_AV_downup , 2,MAX_GHANDS,4 , MCW_AV_notext , 0 , GRAF_handle_CB , (XtPointer) gp , NULL,NULL ) ; gp->reset_pb = XtVaCreateManagedWidget( "dialog" , xmPushButtonWidgetClass , rcbox , LABEL_ARG("Line") , XmNalignment , XmALIGNMENT_CENTER , XmNtraversalOn , True , XmNinitialResourcesPersistent , False , NULL ) ; XtAddCallback( gp->reset_pb, XmNactivateCallback, GRAF_reset_CB , gp ) ; XtManageChild( rcbox ) ; XtManageChild( gp->topform ) ; /* initialize data structure */ gp->dc = dc ; gp->cbfunc = cbfunc ; gp->cbdata = cbdata ; gp->fg = gp->bg = 0 ; /* will be fixed later */ gp->gwin = (Window) 0 ; gp->nhands = 4; gp->spline = 0; gp->hands[0].x = 0; gp->hands[0].y = 0; gp->hands[1].x = 64; gp->hands[1].y = 64; gp->hands[2].x = 192; gp->hands[2].y = 192; gp->hands[3].x = 255; gp->hands[3].y = 255; GenerateGrafFunc(gp,0); memcpy( gp->oldf , gp->func , sizeof(byte)*256 ) ; gp->yeqx = 1 ; gp->xbot = gp->xtop = gp->ybot = gp->ytop = 0.0 ; /* 30 Nov 2002 */ return gp ; } /*-------------------------------------------------------------------- Set the graph parameters and redraw it [14 Jul 1999]. ----------------------------------------------------------------------*/ void GRAF_put_setup( MCW_graf * gp , int nh , int * xh , int * yh , int spl ) { int ii ; if( gp == NULL || nh < 2 || nh > MAX_GHANDS || xh == NULL || yh == NULL ) return ; gp->nhands = nh ; gp->spline = (spl != 0 ) ; for( ii=0 ; ii < nh ; ii++ ){ gp->hands[ii].x = xh[ii] ; gp->hands[ii].y = yh[ii] ; } GenerateGrafFunc(gp,1) ; (void) GRAF_changed(gp) ; return ; } void GRAF_get_setup( MCW_graf * gp , int * nh , int * xh , int * yh , int * spl ) { int ii ; if( gp == NULL || nh == NULL || xh == NULL || yh == NULL || spl == NULL ) return ; *nh = gp->nhands ; *spl = gp->spline ; for( ii=0 ; ii < gp->nhands ; ii++ ){ xh[ii] = gp->hands[ii].x ; yh[ii] = gp->hands[ii].y ; } return ; } /*-------------------------------------------------------------------- Check to see if graph function has changed. As a byproduct, saves the current function in the backup place, so that if called twice in a row, the answer the second time is always no. ----------------------------------------------------------------------*/ int GRAF_changed( MCW_graf * gp ) { int ii , cc=0 , yeqx=1 ; for( ii=0 ; ii < 256 ; ii++ ){ if( gp->oldf[ii] != gp->func[ii] ){ cc = 1 ; if( !yeqx ) break ; } if( yeqx && gp->func[ii] != ii ){ yeqx = 0 ; if( cc ) break ; } } gp->yeqx = yeqx ; if( cc ) memcpy( gp->oldf , gp->func , sizeof(byte)*256 ) ; return cc ; } /*-------------------------------------------------------------------- Event handler for the drawing area ----------------------------------------------------------------------*/ void GRAF_drawing_EV( Widget w , XtPointer client_data , XEvent * ev , Boolean * continue_to_dispatch ) { MCW_graf * gp = (MCW_graf *) client_data ; if( gp == NULL ) return ; /* initialize some X11 stuff for easy access later */ if( gp->bg == 0 && gp->fg == 0 ){ XtVaGetValues( gp->drawer , XmNforeground , &(gp->fg) , XmNbackground , &(gp->bg) , NULL ) ; gp->gwin = XtWindow(gp->drawer) ; } switch( ev->type ){ /*----- redraw -----*/ case Expose:{ XExposeEvent * event = (XExposeEvent *) ev ; XSync( gp->dc->display , False ) ; /* synchronize with server */ if( event->count == 0 ){ if( w == gp->drawer ) drawGraf(gp,0) ; } } break ; /* end of Expose */ /*----- Button Press -----*/ case ButtonPress:{ XButtonEvent *event = (XButtonEvent *) ev ; Window rW,cW ; int mx,my , but , rx,ry ; int vertonly, x,y , orighx, orighy, grab,h , newx,newy ; unsigned int mask ; int use_popup=0 , opx,opy ; /* 30 Nov 2002 */ double ct1,ct2 ; #undef USE_MyCursor #ifdef USE_MyCursor static int need_MyCursor = 1 ; static Cursor MyCursor ; if( need_MyCursor ){ /* create MyCursor to be invisible */ Pixmap pix; static char bits[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; XColor cfg; cfg.red = cfg.green = cfg.blue = 0; pix = XCreateBitmapFromData(gp->dc->display, gp->gwin, bits, 8, 8); MyCursor = XCreatePixmapCursor(gp->dc->display, pix, pix, &cfg, &cfg, 0,0); XFreePixmap(gp->dc->display, pix); need_MyCursor = 0 ; } #else # define MyCursor None #endif /* USE_MyCursor */ but = event->button ; if( but == Button2 ) return ; /* bad button */ /* 30 Nov 2002: label popup if Button3 was pressed */ if( but == Button3 && gp->popmenu != NULL ){ char *str = get_popup_label( gp->xbot,gp->xtop , gp->ybot,gp->ytop , event->x , event->y ) ; if( str != NULL ){ MCW_set_widget_label( gp->poplabel , str ) ; XmMenuPosition( gp->popmenu , event ) ; XtManageChild ( gp->popmenu ) ; use_popup = 1 ; ct1 = clock_time() ; /* start timer */ opx = event->x ; opy = event->y ; /* old popup (x,y) in (opx,opy) */ } } /* see if press is within any of the handles */ mx = event->x ; my = event->y ; /* window coords */ for (h=0; hnhands; h++) { if (PTINRECT(mx*2,(127-my)*2, gp->hands[h].x-5,gp->hands[h].y-5,11,11)) break; } if (h==gp->nhands) return ; /* meaningless click - not in a "hand" */ /* grab the mouse (should always work, but you never know) */ grab = !XGrabPointer(gp->dc->display, gp->gwin, False, 0, GrabModeAsync, GrabModeAsync, gp->gwin, MyCursor , (Time) CurrentTime); orighx = gp->hands[h].x; orighy = gp->hands[h].y; /* current hand location */ vertonly = (h==0 || h==(gp->nhands-1)); /* 1st & last hands: only y moves */ /** loop while mouse button is pressed down **/ while( XQueryPointer(gp->dc->display,gp->gwin,&rW,&cW,&rx,&ry,&x,&y,&mask) ){ if( !(mask & Button1Mask) && !(mask & Button3Mask) ) break; /* button released */ /* XQueryPointer returned current mouse position in (x,y); */ /* now, convert x,y from window to handle coordinates */ newx = (vertonly) ? orighx : 2*x ; /* handle now at */ newy = (y >= 127) ? 0 : 255 - 2*y ; /* (newx,newy) */ if( !vertonly ){ /* don't let handle x stray past neighbors */ if( newx <= gp->hands[h-1].x ) newx = gp->hands[h-1].x + 1 ; if( newx >= gp->hands[h+1].x ) newx = gp->hands[h+1].x - 1 ; } RANGE(newx, 0, 255); /* ensure they are in the legal range! */ RANGE(newy, 0, 255); /* 30 Nov 2002: re-label popup if new (x,y) != (opx,opy) */ #if 1 x = newx/2 ; y = (255-newy)/2 ; /* convert back to screen coords */ if( use_popup && (opx != x || opy != y) ){ ct2 = clock_time() ; /* only update every so often */ if( ct2-ct1 > 0.100 ){ char *str = get_popup_label( gp->xbot,gp->xtop , gp->ybot,gp->ytop , x,y ) ; ct1 = ct2 ; opx = x ; opy = y ; if( str != NULL ){ MCW_set_widget_label( gp->poplabel , str ) ; XSync( XtDisplay(gp->drawer) , True ) ; XmUpdateDisplay( gp->drawer ) ; } } } #endif /* if handle moved, redraw graph */ if (newx != gp->hands[h].x || newy != gp->hands[h].y) { DC_fg_colorpix( gp->dc , gp->bg ) ; /* erase region around handle */ XFillRectangle( gp->dc->display, gp->gwin, gp->dc->myGC , (gp->hands[h].x/2)-3, ((255-gp->hands[h].y)/2)-3, 7,7); gp->hands[h].x = newx; gp->hands[h].y = newy; drawGraf(gp,1); /* erase old trace */ GenerateGrafFunc(gp,0); /* generate curve from handles */ drawGraf(gp,0); /* redraw new trace */ } /* end of redraw graph after handle move */ } /* end of loop that modifies graph, while mouse button is down */ /* release the mouse grab */ if (grab) XUngrabPointer(gp->dc->display, (Time) CurrentTime); /* if the graph WAS changed, call the callback function */ #if 0 if( GRAF_changed(gp) && gp->cbfunc != NULL ) gp->cbfunc(gp,gp->cbdata) ; #else if( GRAF_changed(gp) && gp->cbfunc != NULL ) AFNI_CALL_VOID_2ARG( gp->cbfunc , MCW_graf *,gp , void *,gp->cbdata ) ; #endif #if 1 if( use_popup ){ /* 30 Nov 2002 */ XtUnmanageChild( gp->popmenu ) ; MCW_expose_widget( gp->topform ) ; } #endif } break ; /* end of ButtonPress */ } /* end of switch on event type */ return ; } /************************************************************ draw or erase the graph *************************************************************/ void drawGraf( MCW_graf * gp , int erase ) { int i,x,y; XPoint pts[129], *pt; XSegment segs[GRAF_NTICK] , *sg ; /* decide whether to draw in foreground or background */ DC_fg_colorpix( gp->dc , (erase) ? gp->bg : gp->fg ) ; /* draw lines that make up the graph */ for (i=0, pt=pts; i<256; i+=2,pt++) { pt->x = i/2; pt->y = 127 - (gp->func[i]/2); if (i==0) i = -1; /* kludge to get sequence 0,1,3,5, ... 253,255 */ } DC_linewidth( gp->dc , 2 ) ; XDrawLines( gp->dc->display , gp->gwin , gp->dc->myGC , pts, 129, CoordModeOrigin ); DC_linewidth( gp->dc , 0 ) ; if (erase) return ; /* just erased the curve */ /* erase handles prior to drawing new ones */ DC_fg_colorpix( gp->dc , gp->bg ) ; DC_linewidth( gp->dc , 1 ) ; for (i=0; inhands; i++) { /* clear inside rectangles */ x = gp->hands[i].x/2; y = 127 - gp->hands[i].y/2; XFillRectangle(gp->dc->display, gp->gwin, gp->dc->myGC, x-2, y-2, 5,5); } /* redraw handles */ DC_fg_colorpix( gp->dc , gp->fg ) ; for (i=0; inhands; i++) { /* draw center dots */ x = gp->hands[i].x/2; y = 127 - gp->hands[i].y/2; XDrawPoint(gp->dc->display, gp->gwin, gp->dc->myGC, x, y); } for (i=0; inhands; i++) { /* draw rectangles */ x = gp->hands[i].x/2; y = 127 - gp->hands[i].y/2; XDrawRectangle(gp->dc->display, gp->gwin, gp->dc->myGC, x-3, y-3, 6,6); } DC_linewidth( gp->dc , 0 ) ; /* draw tick marks in the extra space */ for( i=0,x=GRAF_XTICK,sg=segs ; x < GRAF_SIZE ; i++,sg++,x+=GRAF_XTICK ){ sg->x1 = x ; sg->y1 = GRAF_SIZE+GRAF_EXTRA-1 ; sg->x2 = x ; sg->y2 = GRAF_SIZE ; } XDrawSegments( gp->dc->display , gp->gwin , gp->dc->myGC , segs , i ) ; return ; } /*--------------------------------------------------------------------- Callback for "Crv" button -- curves or lines? -----------------------------------------------------------------------*/ void GRAF_curve_CB( Widget w, XtPointer client_data, XtPointer call_data ) { MCW_graf * gp = (MCW_graf *) client_data ; int bval ; bval = MCW_val_bbox( gp->curve_bbox ) ; if( bval == gp->spline ) return ; /* no change */ gp->spline = bval ; GenerateGrafFunc(gp,1); #if 0 if( GRAF_changed(gp) && gp->cbfunc != NULL ) gp->cbfunc(gp,gp->cbdata) ; #else if( GRAF_changed(gp) && gp->cbfunc != NULL ) AFNI_CALL_VOID_2ARG( gp->cbfunc , MCW_graf *,gp , void *,gp->cbdata ) ; #endif return ; } /*--------------------------------------------------------------------- Callback for "Line" button -- reset the graf -----------------------------------------------------------------------*/ void GRAF_reset_CB( Widget w, XtPointer client_data, XtPointer call_data ) { MCW_graf * gp = (MCW_graf *) client_data ; int j ; for( j=0 ; j < gp->nhands ; j++ ) gp->hands[j].y = gp->hands[j].x ; GenerateGrafFunc(gp,1); #if 0 if( GRAF_changed(gp) && gp->cbfunc != NULL ) gp->cbfunc(gp,gp->cbdata) ; #else if( GRAF_changed(gp) && gp->cbfunc != NULL ) AFNI_CALL_VOID_2ARG( gp->cbfunc , MCW_graf *,gp , void *,gp->cbdata ) ; #endif return ; } /*------------------------------------------------------------------------- Set the values stored inside a passive graph ---------------------------------------------------------------------------*/ void GRAF_set_func( MCW_graf * gp , byte * func ) { int i ; if( gp == NULL ) return ; if( func != NULL ){ for( i=0 ; i < 256 ; i++ ) gp->func[i] = func[i] ; } else { for( i=0 ; i < 256 ; i++ ) gp->func[i] = i ; } for( i=0 ; i < gp->nhands ; i++ ) gp->hands[i].y = gp->func[gp->hands[i].x] ; (void) GRAF_changed(gp) ; drawGraf(gp,1); drawGraf(gp,0); return ; } /*--------------------------------------------------------------------- Callback for "#" arrowval -- how many handles today? -----------------------------------------------------------------------*/ void GRAF_handle_CB( MCW_arrowval * av , XtPointer client_data ) { MCW_graf * gp = (MCW_graf *) client_data ; int nh = av->ival , j ; if( nh < 2 || nh > MAX_GHANDS || nh == gp->nhands ){ /* error */ XBell(gp->dc->display,100) ; return ; } if( nh < gp->nhands ){ /* delete a handle */ /* find (middle) point whose x-distance to previous and next points is minimal. Delete that point */ int dist, mdist, mpos; mdist = (gp->hands[1].x - gp->hands[0].x) + (gp->hands[2].x - gp->hands[1].x); mpos = 1; for (j=2; jnhands-1; j++) { dist = (gp->hands[j ].x - gp->hands[j-1].x) + (gp->hands[j+1].x - gp->hands[j].x); if (dist < mdist) { mdist = dist; mpos = j; } } /* delete position 'mpos' in hands[] array */ xvbcopy( (char *) &gp->hands[mpos+1] , (char *) &gp->hands[mpos] , (gp->nhands-mpos-1) * sizeof(XPoint) ) ; gp->nhands--; } else if( nh > gp->nhands ){ /* add a handle */ /* find largest x-gap in handles, put new handle in mid */ int lgap, lpos, x, y; lgap = gp->hands[1].x - gp->hands[0].x; lpos = 1; for (j=1; jnhands-1; j++) if ((gp->hands[j+1].x - gp->hands[j].x) > lgap) { lgap = gp->hands[j+1].x - gp->hands[j].x; lpos = j+1; } /* open up position in hands[] array */ xvbcopy( (char *) &gp->hands[lpos] , (char *) &gp->hands[lpos+1] , (gp->nhands - lpos) * sizeof(XPoint) ) ; x = gp->hands[lpos-1].x + lgap/2; y = gp->func[x]; gp->hands[lpos].x = x; gp->hands[lpos].y = y; gp->nhands++; } GenerateGrafFunc(gp,1); #if 0 if( GRAF_changed(gp) && gp->cbfunc != NULL ) gp->cbfunc(gp,gp->cbdata) ; #else if( GRAF_changed(gp) && gp->cbfunc != NULL ) AFNI_CALL_VOID_2ARG( gp->cbfunc , MCW_graf *,gp , void *,gp->cbdata ) ; #endif return ; } /********************************************************************** Compute the graph function from the handle positions ***********************************************************************/ void GenerateGrafFunc( MCW_graf * gp , int redraw ) { int i,j,k; /* do sanity check. (x-coords must be sorted (strictly increasing)) */ for (i=0; inhands; i++) { RANGE(gp->hands[i].x, 0, 255); RANGE(gp->hands[i].y, 0, 255); } gp->hands[0].x = 0; gp->hands[gp->nhands-1].x = 255; for (i=1; inhands-1; i++) { if (gp->hands[i].x < i) gp->hands[i].x = i; if (gp->hands[i].x > 256-gp->nhands+i) gp->hands[i].x = 256-gp->nhands+i; if (gp->hands[i].x <= gp->hands[i-1].x) gp->hands[i].x = gp->hands[i-1].x + 1; } /* recompute the function */ if (!gp->spline) { /* do linear interpolation */ int y,x1,y1,x2,y2; double yd; for (i=0; inhands-1; i++) { x1 = gp->hands[ i ].x; y1 = gp->hands[ i ].y; x2 = gp->hands[i+1].x; y2 = gp->hands[i+1].y; for (j=x1,k=0; j<=x2; j++,k++) { /* x2 <= 255 */ yd = ((double) k * (y2 - y1)) / (x2 - x1); y = y1 + (int) floor(yd + 0.5); RANGE(y,0,255); gp->func[j] = y; } } } else { /* splinear interpolation */ static int x[MAX_GHANDS], y[MAX_GHANDS]; double yf[MAX_GHANDS]; double yd; for (i=0; inhands; i++) { x[i] = gp->hands[i].x; y[i] = gp->hands[i].y; } InitSpline(x, y, gp->nhands, yf); for (i=0; i<256; i++) { yd = EvalSpline(x, y, yf, gp->nhands, (double) i); j = (int) floor(yd + 0.5); RANGE(j,0,255); gp->func[i] = j; } } if (redraw) { /* redraw graph */ XClearWindow( gp->dc->display, gp->gwin ) ; drawGraf(gp,0); } } /***************************************************************************/ void InitSpline(int *x,int *y,int n,double *y2) { /* given arrays of data points x[0..n-1] and y[0..n-1], computes the values of the second derivative at each of the data points y2[0..n-1] for use in the splint function */ int i,k; double p,qn,sig,un,u[MAX_GHANDS]; y2[0] = u[0] = 0.0; for (i=1; i=0; k--) y2[k] = y2[k]*y2[k+1]+u[k]; } /*******************************************************************/ extern void FatalError(char * str); double EvalSpline(int xa[],int ya[],double y2a[],int n,double x) { int klo,khi,k; double h,b,a; klo = 0; khi = n-1; while (khi-klo > 1) { k = (khi+klo) >> 1; if (xa[k] > x) khi = k; else klo = k; } h = xa[khi] - xa[klo]; if (h==0.0) FatalError("bad xvalues in splint\n"); a = (xa[khi]-x)/h; b = (x-xa[klo])/h; return (a*ya[klo] + b*ya[khi] + ((a*a*a-a)*y2a[klo] +(b*b*b-b)*y2a[khi]) * (h*h) / 6.0); } /*******************************************************************/ void xvbcopy(char * src, char * dst, size_t len) { /* determine if the regions overlap * * 3 cases: src=dst, srcdst * * if src=dst, they overlap completely, but nothing needs to be moved * if srcdst then they overlap */ if (src==dst || len<=0) return; /* nothing to do */ if (srcdst) { /* do a backward copy */ src = src + len - 1; dst = dst + len - 1; for ( ; len>0; len--, src--, dst--) *dst = *src; } else { /* they either overlap (src>dst) or they don't overlap */ /* do a forward copy */ for ( ; len>0; len--, src++, dst++) *dst = *src; } return ; } /*=================================================================== Routines for a passive graph =====================================================================*/ MCW_pasgraf * new_MCW_pasgraf( Widget wpar , MCW_DC * dc , char * title ) { MCW_pasgraf * gp ; Widget wf ; XmString xstr ; gp = myXtNew(MCW_pasgraf) ; /* make the Widgets */ gp->topform = XtVaCreateWidget( "dialog" , xmFormWidgetClass , wpar , XmNinitialResourcesPersistent , False , XmNtraversalOn , True , NULL ) ; if( title == NULL || title[0] == '\0' ) title = "Graphing" ; xstr = XmStringCreateLtoR( title , XmFONTLIST_DEFAULT_TAG ); gp->toplabel = XtVaCreateManagedWidget( "dialog" , xmLabelWidgetClass , gp->topform , XmNtopAttachment , XmATTACH_FORM , XmNleftAttachment , XmATTACH_FORM , XmNlabelString , xstr , XmNrecomputeSize , False , XmNmarginWidth , 0 , XmNalignment , XmALIGNMENT_BEGINNING , XmNinitialResourcesPersistent , False , NULL ) ; XmStringFree(xstr) ; wf = XtVaCreateWidget( "dialog" , xmFrameWidgetClass , gp->topform , XmNshadowType , XmSHADOW_IN , XmNshadowThickness , 4 , XmNtopAttachment , XmATTACH_WIDGET , XmNtopWidget , gp->toplabel , XmNleftAttachment , XmATTACH_FORM , XmNtraversalOn , True , XmNinitialResourcesPersistent , False , NULL ) ; gp->drawer = XtVaCreateManagedWidget( "dialog" , xmDrawingAreaWidgetClass , wf , XmNwidth , GRAF_SIZE , XmNheight , GRAF_SIZE + GRAF_EXTRA , XmNtraversalOn , True , XmNinitialResourcesPersistent , False , NULL ) ; XtManageChild(wf) ; XtInsertEventHandler( gp->drawer , /* handle events in image */ 0 | ButtonPressMask /* button presses */ | ExposureMask /* exposures */ , FALSE , /* nonmaskable events? */ GRAF_pasdrawing_EV , /* event handler */ (XtPointer) gp , /* client data */ XtListTail ) ; /* last in queue */ /* 30 Nov 2002: popup stuff */ #ifdef BAD_BUTTON3_POPUPS /* 21 Jul 2003 */ gp->popmenu = XmCreatePopupMenu( gp->topform, "menu" , NULL , 0 ) ; #else gp->popmenu = XmCreatePopupMenu( gp->drawer , "menu" , NULL , 0 ) ; #endif gp->poplabel = XtVaCreateManagedWidget( "help" , xmLabelWidgetClass , gp->popmenu , LABEL_ARG("I am a label") , XmNinitialResourcesPersistent , False , NULL ) ; XtManageChild( gp->topform ) ; /* initialize data structure */ gp->dc = dc ; gp->mode = PASGRAF_LINE ; gp->fg = gp->bg = 0 ; /* will be fixed later */ gp->gwin = (Window) 0 ; gp->xbot = gp->xtop = gp->ybot = gp->ytop = 0.0 ; /* 30 Nov 2002 */ return gp ; } /*-------------------------------------------------------------------- Event handler for the drawing area ----------------------------------------------------------------------*/ void GRAF_pasdrawing_EV( Widget w , XtPointer client_data , XEvent * ev , Boolean * continue_to_dispatch ) { MCW_pasgraf * gp = (MCW_pasgraf *) client_data ; if( gp == NULL ) return ; /* initialize some X11 stuff for easy access later */ if( gp->bg == 0 && gp->fg == 0 ){ XtVaGetValues( gp->drawer , XmNforeground , &(gp->fg) , XmNbackground , &(gp->bg) , NULL ) ; gp->gwin = XtWindow(gp->drawer) ; } switch( ev->type ){ /*----- redraw -----*/ case Expose:{ XExposeEvent * event = (XExposeEvent *) ev ; XSync( gp->dc->display , False ) ; /* synchronize with server */ if( event->count == 0 ){ if( w == gp->drawer ) redraw_MCW_pasgraf(gp) ; } } break ; /* end of Expose */ /*----- Button click -----*/ case ButtonPress:{ XButtonEvent * event = (XButtonEvent *) ev ; int mx,my , but ; but = event->button ; /* 30 Nov 2002: label popup if Button3 was pressed */ if( but == Button3 && gp->popmenu != NULL ){ char *str = get_popup_label( gp->xbot,gp->xtop , gp->ybot,gp->ytop , event->x , event->y ) ; if( str != NULL ){ MCW_set_widget_label( gp->poplabel , str ) ; XmMenuPosition( gp->popmenu , event ) ; XtManageChild ( gp->popmenu ) ; } } if( but != Button1 ) return ; /* meaningless */ mx = event->x ; my = event->y ; /* window coords */ NEXT_PASGRAF_MODE(gp) ; redraw_MCW_pasgraf(gp) ; } break ; /* end of ButtonPress */ } return ; } void redraw_MCW_pasgraf( MCW_pasgraf * gp ) { XPoint pts[GRAF_SIZE], *pt ; XSegment segs[GRAF_SIZE] , *sg ; int i , x ; if( gp->gwin == (Window) 0 ) return ; /* not ready for prime time */ XClearWindow( gp->dc->display, gp->gwin ) ; DC_fg_colorpix( gp->dc , gp->fg ) ; switch( gp->mode ){ default: case PASGRAF_LINE:{ /* draw lines */ for( i=0, pt=pts ; i < GRAF_SIZE ; i++ , pt++ ){ pt->x = i ; pt->y = GRAF_SIZE - gp->func[i] ; } XDrawLines( gp->dc->display , gp->gwin , gp->dc->myGC , pts, GRAF_SIZE, CoordModeOrigin ); } break ; case PASGRAF_BAR:{ /* draw bars */ for( i=0, sg=segs ; i < GRAF_SIZE ; i++ , sg++ ){ sg->x1 = i ; sg->y1 = GRAF_SIZE - 1 ; sg->x2 = i ; sg->y2 = GRAF_SIZE - 1 - gp->func[i] ; } XDrawSegments( gp->dc->display , gp->gwin , gp->dc->myGC , segs , GRAF_SIZE ) ; } break ; } /* draw tick marks in the extra space */ for( i=0,x=GRAF_XTICK,sg=segs ; x < GRAF_SIZE ; i++,sg++,x+=GRAF_XTICK ){ sg->x1 = x ; sg->y1 = GRAF_SIZE+GRAF_EXTRA-1 ; sg->x2 = x ; sg->y2 = GRAF_SIZE ; } XDrawSegments( gp->dc->display , gp->gwin , gp->dc->myGC , segs , i ) ; return ; } /*------------------------------------------------------------------------- Set the values stored inside a passive graph ---------------------------------------------------------------------------*/ void set_MCW_pasgraf( MCW_pasgraf * gp , byte * func ) { int i ; byte b ; if( gp == NULL ) return ; if( func != NULL ){ for( i=0 ; i < GRAF_SIZE ; i++ ){ b = func[i] ; gp->func[i] = (b < GRAF_SIZE) ? b : (GRAF_SIZE-1) ; } } else { memset( gp->func , 0 , sizeof(byte)*GRAF_SIZE ) ; } return ; } void MCW_histo_bytes( int nb , byte * bar , int * har ) { int i ; if( nb <= 0 || bar == NULL || har == NULL ) return ; for( i=0 ; i < 256 ; i++ ) har[i] = 0 ; for( i=0 ; i < nb ; i++ ) har[ bar[i] ]++ ; return ; } /*****************************************************************************/ /*** 30 Nov 2002: stuff for popping up position label ***/ void PASGRAF_set_xyrange( MCW_pasgraf *gp , float xb,float xt, float yb,float yt ) { if( gp == NULL ) return ; gp->xbot = xb ; gp->xtop = xt ; gp->ybot = yb ; gp->ytop = yt ; } /*--------------------------------------------------------------------------*/ void GRAF_set_xyrange( MCW_graf *gp , float xb,float xt, float yb,float yt ) { if( gp == NULL ) return ; gp->xbot = xb ; gp->xtop = xt ; gp->ybot = yb ; gp->ytop = yt ; } /*--------------------------------------------------------------------------*/ static char *get_popup_label( float xbot,float xtop , float ybot,float ytop , int x,int y ) { static char str[128] ; char xbuf[32],ybuf[32] , *xb,*yb ; float xx , yy , ff ; if( x < 0 ) x = 0 ; else if( x >= GRAF_SIZE ) x = GRAF_SIZE-1 ; if( y < 0 ) y = 0 ; else if( y >= GRAF_SIZE ) y = GRAF_SIZE-1 ; if( xtop != xbot ){ ff = x / (float)(GRAF_SIZE-1) ; xx = ff*xtop + (1.0-ff)*xbot ; } else { xx = x ; } if( ytop != ybot ){ ff = y / (float)(GRAF_SIZE-1) ; yy = ff*ybot + (1.0-ff)*ytop ; } else { yy = GRAF_SIZE-1 - y ; } AV_fval_to_char( xx,xbuf ); xb = xbuf; if( *xb == ' ' ) xb++; AV_fval_to_char( yy,ybuf ); yb = ybuf; if( *yb == ' ' ) yb++; sprintf(str,"%s,%s",xb,yb) ; return str ; } /*-----------------------------------------------------------------*/ static double clock_time(void) /* in seconds, since first call to this */ { struct timeval new_tval ; struct timezone tzone ; static struct timeval old_tval ; /* save old time */ static int first = 1 ; gettimeofday( &new_tval , &tzone ) ; if( first ){ /* 1st time in: */ old_tval = new_tval ; /* just save current time */ first = 0 ; return 0.0 ; /* and return zero */ } if( old_tval.tv_usec > new_tval.tv_usec ){ /* adjust structs */ new_tval.tv_usec += 1000000 ; /* so new_tval.tv_usec */ new_tval.tv_sec -- ; /* >= old_tval.tv_usec */ } return (double)( (new_tval.tv_sec - old_tval.tv_sec ) +(new_tval.tv_usec - old_tval.tv_usec)*1.0e-6 ) ; }