1 /*****************************************************************************
2    Major portions of this software are copyrighted by the Medical College
3    of Wisconsin, 1994-2000, and are released under the Gnu General Public
4    License, Version 2.  See the file README.Copyright for details.
5 ******************************************************************************/
6 
7 #include "afni.h"
8 
9 #ifndef ALLOW_PLUGINS
10 #  error "Plugins not properly set up -- see machdep.h"
11 #endif
12 
13 /***********************************************************************
14   Plugin to edit/view dataset notes.
15   Makes a custom interface.
16   01 Sep 1999 - RWCox
17 ************************************************************************/
18 
19 /*---------- prototypes for internal routines ----------*/
20 
21 char * NOTES_main( PLUGIN_interface * ) ;
22 
23 static void NOTES_make_widgets(void) ;
24 
25 static void NOTES_done_CB   ( Widget , XtPointer , XtPointer ) ;
26 static void NOTES_help_CB   ( Widget , XtPointer , XtPointer ) ;
27 static void NOTES_quit_CB   ( Widget , XtPointer , XtPointer ) ;
28 static void NOTES_save_CB   ( Widget , XtPointer , XtPointer ) ;
29 static void NOTES_choose_CB ( Widget , XtPointer , XtPointer ) ;
30 static void NOTES_add_CB    ( Widget , XtPointer , XtPointer ) ;
31 static void NOTES_delete_CB ( Widget , XtPointer , XtPointer ) ;
32 static void NOTES_restore_CB( Widget , XtPointer , XtPointer ) ;
33 static void NOTES_refit_CB  ( Widget , XtPointer , XtPointer ) ;
34 
35 static void NOTES_finalize_dset_CB( Widget , XtPointer , MCW_choose_cbs * ) ;
36 
37 static PLUGIN_interface * plint = NULL ;
38 
39 #define TWIDTH 75
40 #define newstring(str) strcpy(malloc(strlen(str)+1),str)
41 static int line_count( char * ) ;
42 
43 #define THEIGHT 9
44 static int max_tlines = 0 ;
45 
46 /***********************************************************************
47    Set up the interface to the user.  Note that we bypass the
48    normal interface creation, and simply have the menu selection
49    directly call the main function, which will create a custom
50    set of interface widgets the first time in.
51 ************************************************************************/
52 
53 
54 DEFINE_PLUGIN_PROTOTYPE
55 
PLUGIN_init(int ncall)56 PLUGIN_interface * PLUGIN_init( int ncall )
57 {
58 
59    if( ncall > 0 ) return NULL ;  /* only one interface */
60 
61    plint = PLUTO_new_interface( "Dataset NOTES" , NULL , NULL ,
62                                 PLUGIN_CALL_IMMEDIATELY , NOTES_main ) ;
63 
64    PLUTO_add_hint( plint , "Edit/View Notes attached to dataset" ) ;
65 
66    PLUTO_set_sequence( plint , "A:olddset:notes" ) ;
67 
68    return plint ;
69 }
70 
71 /***************************************************************************
72   Will be called from AFNI when user selects from Plugins menu.
73 ****************************************************************************/
74 
75 /* Interface widgets */
76 
77 typedef struct {
78    Widget frame, vert_rc , horz_rc , note_label ,
79           delete_pb , restore_pb , scrollw , textw ;
80 
81    char * note_orig , * date_orig ;
82 } NOTE_wind ;
83 
84 static NOTE_wind * NOTES_make_note(void) ;
85 
86 static Widget shell=NULL , topman , info_lab , choose_pb ;
87 static Widget done_pb , help_pb , quit_pb , save_pb , add_pb ;
88 static Widget notesw , noterc , refit_pb ;
89 
90 static int text_width = 0 ;
91 static int text_height = 0 ;
92 
93 static NOTE_wind ** notar = NULL ;
94 static int          notar_num = 0 ;
95 static int          num_notes = 0 ;
96 
97 static MCW_DC * dc ;                 /* display context */
98 static Three_D_View * im3d ;         /* AFNI controller */
99 static THD_3dim_dataset * dset ;     /* The dataset!    */
100 static MCW_idcode         dset_idc ; /* 31 Mar 1999     */
101 
102 static int editor_open  = 0 ;
103 
104 #define NUM_DH 10
105 static char * default_history[NUM_DH] = {
106 
107    "The mind is its own place, and in itself\n"
108    "Can make a Heaven of Hell, a Hell of Heaven.\n"
109    "-- John Milton (Paradise Lost)\n" ,
110 
111    "Those who cannot remember the past are condemned to repeat it.\n"
112    "-- George Santayana" ,
113 
114    "Chanter of Personality, outlining what is yet to be,\n"
115    "I project the history of the future.\n"
116    "-- Walt Whitman" ,
117 
118    "I shall cheerfully bear the reproach of having descended below the\n"
119    "dignity of history.\n"
120    "-- Thomas Macaulay" ,
121 
122    "There was a time - we see it in the marvellous dawn of Hellenic life -\n"
123    "when history was distinguished neither from poetry, from mythology,\n"
124    "nor from the first dim beginnings of science.\n"
125    "-- Theodore Roosevelt" ,
126 
127    "You cannot escape the responsibility of tomorrow by evading it today.\n"
128    "-- Abraham Lincoln" ,
129 
130    "Once you get into this great stream of history, you can't get out.\n"
131    "-- Richard Nixon" ,
132 
133    "History is the version of past events that people have decided to\n"
134    "agree upon.\n"
135    "--Napoleon" ,
136 
137    "Ever returning Spring, trinity sure to me you bring:\n"
138    "Lilac blooming perennial, and drooping star in the West,\n"
139    "And thought of him I love.\n"
140    "-- Walt Whitman" ,
141 
142    "What has occurred in this case, must ever recur in similar cases.\n"
143    "Human nature will not change. In any future great national trial,\n"
144    "compared with the men of this, we shall have as weak, and as strong;\n"
145    "as silly and as wise; as bad and good. Let us, therefore, study the\n"
146    "incidents of this, as philosophy to learn wisdom from, and none of\n"
147    "them as wrongs to be revenged.\n"
148    "-- Abraham Lincoln"
149 
150 } ;
151 
152 static char * empty_note = "-- Empty Note Text --" ;
153 static char * no_date    = "no date" ;
154 
155 static void scroll_topbot( Widget , int ) ;
156 
NOTES_main(PLUGIN_interface * plint)157 char * NOTES_main( PLUGIN_interface * plint )
158 {
159    XmString xstr ;
160    int ii ;
161 
162    /*-- sanity checks --*/
163 
164    if( ! IM3D_OPEN(plint->im3d) ) return "AFNI Controller\nnot opened?!" ;
165 
166    if( editor_open ){
167       XtMapWidget(shell) ;
168       XRaiseWindow( XtDisplay(shell) , XtWindow(shell) ) ;
169       return NULL ;
170    }
171 
172    im3d = plint->im3d ;  /* save for local use */
173 
174    /*-- create widgets, first time through --*/
175 
176    if( shell == NULL ){
177       dc = im3d->dc ;        /* save this too */
178       NOTES_make_widgets() ;
179       PLUTO_set_topshell( plint , shell ) ;  /* 22 Sep 2000 */
180       RWC_visibilize_widget( shell ) ;       /* 27 Sep 2000 */
181    }
182 
183    /*-- unmanage all notes sub-windows --*/
184 
185    XtUnmanageChild( notesw ) ;
186    XtUnmanageChild( noterc ) ;
187    for( ii=0 ; ii < notar_num ; ii++ )
188       XtUnmanageChild( notar[ii]->frame ) ;
189 
190    /*-- set titlebar --*/
191 
192    { char ttl[32] ;
193      sprintf( ttl , "AFNI Notes %s" , AFNI_controller_label(im3d) ) ;
194      XtVaSetValues( shell , XmNtitle , ttl , NULL ) ;
195    }
196 
197    /*-- set the info label --*/
198 
199    xstr = XmStringCreateLtoR( "[No dataset]" ,
200                               XmFONTLIST_DEFAULT_TAG ) ;
201    XtVaSetValues( info_lab , XmNlabelString , xstr , NULL ) ;
202    XmStringFree(xstr) ;
203 
204    /*-- pop the widget up --*/
205 
206    XtMapWidget(shell) ;
207    PLUTO_cursorize(shell) ;
208 
209    /*-- misc initialization --*/
210 
211    dset         = NULL ;   /* not editing anything   */
212    editor_open  = 1 ;      /* editor is now open for business */
213    num_notes    = 0 ;      /* don't have any notes now */
214 
215    return NULL ;
216 }
217 
218 /*------------------------------------------------------------------------
219   Make the control popup window for this beast
220 --------------------------------------------------------------------------*/
221 
222 /*-- structures defining action buttons (at bottom of popup) --*/
223 
224 #define NACT 6  /* number of action buttons */
225 
226 static MCW_action_item NOTES_actor[NACT] = {
227  {"Quit",NOTES_quit_CB,NULL,
228   "Discard edits since last\nSave and close Editor" ,
229    "Discard edits and close",0} ,
230 
231  {"Help",NOTES_help_CB,NULL,
232   "Displays more help" , "Displays more help",0} ,
233 
234  {"Add",NOTES_add_CB,NULL,
235   "Add a new Note to the\nlist (at the end)", "Add a new Note" , 0 } ,
236 
237  {"Refit",NOTES_refit_CB,NULL,
238   "Resize the Notes' sub-windows\nto fit the Notes' texts." ,
239   "Resize Notes sub-windows" , 0 } ,
240 
241  {"Save",NOTES_save_CB,NULL,
242   "Save edits to disk\nand continue" , "Save to disk; continue",0} ,
243 
244  {"Done",NOTES_done_CB,NULL,
245   "Save edits to disk\nand close Editor" , "Save and Quit",1}
246 } ;
247 
NOTES_make_widgets(void)248 static void NOTES_make_widgets(void)
249 {
250    XmString xstr ;
251    Widget twid , tsep ;
252 
253    /*** get max # of text lines ***/
254 
255    if( max_tlines < 1 ){
256       char * cc = my_getenv("AFNI_NOTES_DLINES") ;
257       if( cc != NULL ) max_tlines = strtol( cc , NULL , 10 ) ;
258 
259            if( max_tlines <  1 ) max_tlines = THEIGHT ;
260       else if( max_tlines > 99 ) max_tlines = 99 ;     /* 15 Sep 1999 */
261    }
262 
263    /*** top level shell for window manager ***/
264 
265    shell =
266       XtVaAppCreateShell(
267            "AFNI" , "AFNI" , topLevelShellWidgetClass , dc->display ,
268 
269            XmNtitle             , "Notes Editor" , /* top of window */
270            XmNiconName          , "Notes"        , /* label on icon */
271            XmNdeleteResponse    , XmDO_NOTHING   , /* deletion handled below */
272            XmNallowShellResize  , True ,           /* let code resize shell? */
273            XmNmappedWhenManaged , False ,          /* must map it manually */
274       NULL ) ;
275 
276    DC_yokify( shell , dc ) ;
277 
278    if( afni48_good )             /* set icon pixmap */
279       XtVaSetValues( shell ,
280                         XmNiconPixmap , afni48_pixmap ,
281                      NULL ) ;
282 
283    XmAddWMProtocolCallback(      /* make "Close" window menu work */
284            shell ,
285            XmInternAtom( dc->display , "WM_DELETE_WINDOW" , False ) ,
286            NOTES_quit_CB , (XtPointer) plint ) ;
287 
288    /*** Form widget to hold all user interface stuff ***/
289 
290    topman = XtVaCreateWidget(
291                 "AFNI" , xmFormWidgetClass , shell ,
292                   XmNborderWidth , 0 ,
293                   XmNborderColor , 0 ,
294                   XmNtraversalOn , True  ,
295                 NULL ) ;
296 
297    /*** horizontal rowcol to hold top row of controls ***/
298 
299    twid = XtVaCreateWidget(
300              "AFNI" , xmRowColumnWidgetClass , topman ,
301                 XmNpacking     , XmPACK_TIGHT ,
302                 XmNorientation , XmHORIZONTAL ,
303                 XmNtraversalOn , True  ,
304                 XmNleftAttachment , XmATTACH_FORM ,
305                 XmNrightAttachment, XmATTACH_FORM ,
306                 XmNtopAttachment  , XmATTACH_FORM ,
307                 XmNtopOffset      , 1 ,
308              NULL ) ;
309 
310    /*** button to let user choose dataset to edit ***/
311 
312    xstr = XmStringCreateLtoR( "Choose Dataset" , XmFONTLIST_DEFAULT_TAG ) ;
313    choose_pb = XtVaCreateManagedWidget(
314                   "AFNI" , xmPushButtonWidgetClass , twid ,
315                      XmNlabelString , xstr ,
316                      XmNtraversalOn , True  ,
317                   NULL ) ;
318    XmStringFree(xstr) ;
319    XtAddCallback( choose_pb, XmNactivateCallback, NOTES_choose_CB, NULL ) ;
320    MCW_register_help( choose_pb ,
321                       "Use this to popup a\n"
322                       "'chooser' that lets\n"
323                       "you select which\n"
324                       "dataset to edit."
325                     ) ;
326    MCW_register_hint( choose_pb , "Popup a dataset chooser" ) ;
327 
328    /*** label at top to let user know who we are ***/
329 
330    xstr = XmStringCreateLtoR( "[No dataset]" , XmFONTLIST_DEFAULT_TAG ) ;
331    info_lab = XtVaCreateManagedWidget(
332                  "AFNI" , xmLabelWidgetClass , twid ,
333                     XmNlabelString , xstr ,
334                  NULL ) ;
335    XmStringFree(xstr) ;
336    MCW_register_help( info_lab , "Shows dataset being edited" ) ;
337    MCW_register_hint( info_lab , "Dataset being edited" ) ;
338 
339    XtManageChild(twid) ;  /*** end of top row ***/
340 
341    /*** separator for visual neatness ***/
342 
343    tsep   = XtVaCreateManagedWidget(
344              "AFNI" , xmSeparatorWidgetClass , topman ,
345                 XmNseparatorType , XmSINGLE_LINE ,
346                 XmNleftAttachment  , XmATTACH_FORM ,
347                 XmNrightAttachment , XmATTACH_FORM ,
348                 XmNtopAttachment   , XmATTACH_WIDGET ,
349                 XmNtopWidget       , info_lab ,
350                 XmNtopOffset       , 1 ,
351              NULL ) ;
352 
353    /*** a set of action buttons below the line ***/
354 
355    twid = MCW_action_area( topman , NOTES_actor , NACT ) ;
356 
357    XtVaSetValues( twid ,
358                      XmNleftAttachment , XmATTACH_FORM ,
359                      XmNrightAttachment, XmATTACH_FORM ,
360                      XmNtopAttachment  , XmATTACH_WIDGET ,
361                      XmNtopWidget      , tsep   ,
362                      XmNtopOffset      , 1 ,
363                   NULL ) ;
364 
365    quit_pb  = (Widget) NOTES_actor[0].data ;
366    help_pb  = (Widget) NOTES_actor[1].data ;
367    add_pb   = (Widget) NOTES_actor[2].data ;
368    refit_pb = (Widget) NOTES_actor[3].data ;
369    save_pb  = (Widget) NOTES_actor[4].data ;
370    done_pb  = (Widget) NOTES_actor[5].data ;
371 
372    /*** separator for visual neatness ***/
373 
374    tsep   = XtVaCreateManagedWidget(
375               "AFNI" , xmSeparatorWidgetClass , topman ,
376                  XmNseparatorType , XmSINGLE_LINE ,
377                  XmNleftAttachment  , XmATTACH_FORM ,
378                  XmNrightAttachment , XmATTACH_FORM ,
379                  XmNtopAttachment   , XmATTACH_WIDGET ,
380                  XmNtopWidget       , twid ,
381                  XmNtopOffset       , 1 ,
382               NULL ) ;
383 
384    /*** a scrolled window + rowcol to hold all the Notes windows ***/
385 
386    notesw = XtVaCreateWidget(
387                     "AFNI" , xmScrolledWindowWidgetClass , topman ,
388                        XmNscrollingPolicy        , XmAUTOMATIC ,
389                        XmNvisualPolicy           , XmCONSTANT ,
390                        XmNshadowThickness        , 0 ,
391                        XmNscrollBarDisplayPolicy , XmAS_NEEDED ,
392                        XmNscrollBarPlacement     , XmTOP_LEFT ,
393                        XmNleftAttachment  , XmATTACH_FORM ,
394                        XmNrightAttachment , XmATTACH_FORM ,
395                        XmNbottomAttachment, XmATTACH_FORM ,
396                        XmNtopAttachment   , XmATTACH_WIDGET ,
397                        XmNtopWidget       , tsep   ,
398                        XmNtopOffset       , 1 ,
399                     NULL ) ;
400 
401    noterc = XtVaCreateWidget(
402              "AFNI" , xmRowColumnWidgetClass , notesw ,
403                 XmNpacking     , XmPACK_TIGHT ,
404                 XmNorientation , XmVERTICAL ,
405                 XmNmarginHeight, 0 ,
406                 XmNmarginWidth , 0 ,
407                 XmNtraversalOn , True  ,
408              NULL ) ;
409 
410    /**** make an initial Note window
411          and modify it to be readonly - for the History ****/
412 
413    notar     = (NOTE_wind **) malloc( sizeof(NOTE_wind *) ) ;
414    notar[0]  = NOTES_make_note() ;
415    notar_num = 1 ;
416 
417    XtUnmanageChild( notar[0]->delete_pb  ) ;  /* these actions not   */
418    XtUnmanageChild( notar[0]->restore_pb ) ;  /* allowed for History */
419    XtVaSetValues( notar[0]->textw ,
420                     XmNautoShowCursorPosition , False ,
421                     XmNeditable               , False ,
422                     XmNcursorPositionVisible  , False ,
423                   NULL ) ;
424    xstr = XmStringCreateLtoR( "----- History -----" , XmFONTLIST_DEFAULT_TAG ) ;
425    XtVaSetValues( notar[0]->note_label , XmNlabelString , xstr , NULL ) ;
426    MCW_register_hint( notar[0]->textw , "Dataset History; or Edifying Text" ) ;
427 
428    /*** compute width of popup window ***/
429 
430    { char cbuf[TWIDTH+8] ; int ii ;
431      XmFontList xflist=(XmFontList)NULL ;
432 
433      for( ii=0; ii < TWIDTH+3; ii++ ) cbuf[ii] = 'x' ; cbuf[ii] = '\0' ;
434      xstr = XmStringCreateLtoR( cbuf , XmFONTLIST_DEFAULT_TAG ) ;
435      XtVaGetValues( notar[0]->textw , XmNfontList , &xflist , NULL ) ;
436      text_width  = XmStringWidth ( xflist , xstr ) + 14 ;
437      text_height = XmStringHeight( xflist , xstr ) ;
438      XmStringFree( xstr ) ;
439      ii = WidthOfScreen(XtScreen(shell)) - 128 ;
440      if( text_width > ii ) text_width = ii ;
441    }
442 
443    /*** done ***/
444 
445    XtManageChild(topman) ;
446    XtRealizeWidget(shell) ; NI_sleep(1) ; /* will not be mapped */
447 
448    return ;
449 }
450 
451 /*-------------------------------------------------------------------
452    make a new Note widget structure (with nothing in it)
453 ---------------------------------------------------------------------*/
454 
NOTES_make_note(void)455 static NOTE_wind * NOTES_make_note( void )
456 {
457    NOTE_wind * nw ;
458    XmString xstr ;
459 
460    nw = (NOTE_wind *) calloc( 1 , sizeof(NOTE_wind) ) ;
461 
462    nw->frame = XtVaCreateWidget(
463                   "AFNI" , xmFrameWidgetClass , noterc ,
464                   XmNshadowType , XmSHADOW_IN ,
465                   XmNshadowThickness , 1 ,
466                   XmNtraversalOn , True  ,
467                NULL ) ;
468 
469    nw->vert_rc = XtVaCreateWidget(
470                     "AFNI" , xmRowColumnWidgetClass , nw->frame ,
471                        XmNpacking     , XmPACK_TIGHT ,
472                        XmNorientation , XmVERTICAL ,
473                        XmNtraversalOn , True  ,
474                        XmNmarginHeight, 0 ,
475                        XmNmarginWidth , 0 ,
476                     NULL ) ;
477 
478    nw->horz_rc = XtVaCreateWidget(
479                     "AFNI" , xmRowColumnWidgetClass , nw->vert_rc ,
480                        XmNpacking     , XmPACK_TIGHT ,
481                        XmNorientation , XmHORIZONTAL ,
482                        XmNtraversalOn , True  ,
483                        XmNmarginHeight, 0 ,
484                        XmNmarginWidth , 0 ,
485                     NULL ) ;
486 
487    nw->note_label = XtVaCreateManagedWidget(
488                        "AFNI" , xmLabelWidgetClass , nw->horz_rc ,
489                        XmNmarginHeight, 0 ,
490                        XmNmarginWidth , 0 ,
491                        NULL ) ;
492 
493    xstr = XmStringCreateLtoR( "Delete" , XmFONTLIST_DEFAULT_TAG ) ;
494    nw->delete_pb = XtVaCreateManagedWidget(
495                       "AFNI" , xmPushButtonWidgetClass , nw->horz_rc ,
496                          XmNlabelString , xstr ,
497                          XmNtraversalOn , True  ,
498                          XmNmarginHeight, 0 ,
499                          XmNmarginWidth , 0 ,
500                       NULL ) ;
501    XmStringFree(xstr) ;
502    XtAddCallback( nw->delete_pb, XmNactivateCallback, NOTES_delete_CB, NULL ) ;
503    MCW_register_help( nw->delete_pb ,
504                       "Use this button to delete\n"
505                       "this Note from the dataset."
506                     ) ;
507    MCW_register_hint( nw->delete_pb , "Delete this Note" ) ;
508 
509    xstr = XmStringCreateLtoR( "Restore" , XmFONTLIST_DEFAULT_TAG ) ;
510    nw->restore_pb = XtVaCreateManagedWidget(
511                       "AFNI" , xmPushButtonWidgetClass , nw->horz_rc ,
512                          XmNlabelString , xstr ,
513                          XmNtraversalOn , True  ,
514                          XmNmarginHeight, 0 ,
515                          XmNmarginWidth , 0 ,
516                       NULL ) ;
517    XmStringFree(xstr) ;
518    XtAddCallback( nw->restore_pb, XmNactivateCallback, NOTES_restore_CB, NULL ) ;
519    MCW_register_help( nw->restore_pb ,
520                       "Use this button to restore\n"
521                       "this Note to its original\n"
522                       "value (i.e., as it currently\n"
523                       "stored on in the HEAD file)."
524                     ) ;
525    MCW_register_hint( nw->restore_pb , "Restore Note from disk" ) ;
526 
527    XtManageChild(nw->horz_rc) ;
528 
529    (void) XtVaCreateManagedWidget(
530              "AFNI" , xmSeparatorWidgetClass , nw->vert_rc ,
531                 XmNseparatorType , XmSINGLE_LINE ,
532              NULL ) ;
533 
534    nw->scrollw = XtVaCreateManagedWidget(
535                     "AFNI" , xmScrolledWindowWidgetClass , nw->vert_rc ,
536                        XmNscrollingPolicy        , XmAPPLICATION_DEFINED ,
537                        XmNvisualPolicy           , XmVARIABLE ,
538                        XmNshadowThickness        , 0 ,
539                     NULL ) ;
540 
541    nw->textw = XtVaCreateManagedWidget(
542                     "AFNI" , xmTextWidgetClass , nw->scrollw ,
543                        XmNeditMode               , XmMULTI_LINE_EDIT ,
544                        XmNautoShowCursorPosition , True ,
545                        XmNeditable               , True ,
546                        XmNcursorPositionVisible  , True ,
547                        XmNcolumns                , TWIDTH ,
548                        XmNrows                   , 1    ,
549                        XmNwordWrap               , True ,
550                     NULL ) ;
551 
552    XtManageChild(nw->vert_rc) ;  /* we DON'T manage the frame */
553 
554    nw->note_orig = nw->date_orig = NULL ;
555    return nw ;
556 }
557 
558 /*-------------------------------------------------------------------*/
559 
line_count(char * msg)560 static int line_count( char * msg )
561 {
562    char * cpt ; int nlin ;
563    if( msg == NULL ) return 0 ;
564    for( nlin=1,cpt=msg ; *cpt != '\0' ; cpt++ ) if( *cpt == '\n' ) nlin++ ;
565    return nlin ;
566 }
567 
568 /*-------------------------------------------------------------------
569   Callback for done button
570 ---------------------------------------------------------------------*/
571 
NOTES_done_CB(Widget w,XtPointer client_data,XtPointer call_data)572 static void NOTES_done_CB( Widget w, XtPointer client_data, XtPointer call_data )
573 {
574    if( dset != NULL && num_notes > 0 ) NOTES_save_CB(NULL,NULL,NULL) ;
575    NOTES_quit_CB(NULL,NULL,NULL) ;
576    return ;
577 }
578 
579 /*-------------------------------------------------------------------
580   Callback for quit button
581 ---------------------------------------------------------------------*/
582 
NOTES_quit_CB(Widget w,XtPointer client_data,XtPointer call_data)583 static void NOTES_quit_CB( Widget w, XtPointer client_data, XtPointer call_data )
584 {
585    int ii ;
586 
587    XtUnmapWidget( shell ) ; editor_open = 0 ; dset = NULL ;
588 
589    XtUnmanageChild( notesw ) ;
590    XtUnmanageChild( noterc ) ;
591    for( ii=0 ; ii < notar_num ; ii++ ){
592       XtUnmanageChild( notar[ii]->frame ) ;
593 
594       if( notar[ii]->note_orig != NULL ){
595           free(notar[ii]->note_orig) ; notar[ii]->note_orig = NULL ;
596       }
597 
598       if( notar[ii]->date_orig != NULL ){
599           free(notar[ii]->date_orig) ; notar[ii]->date_orig = NULL ;
600       }
601 
602       XmTextSetString( notar[ii]->textw , "\0" ) ;  /* to save some space */
603    }
604 
605    return ;
606 }
607 
608 /*-------------------------------------------------------------------
609   Callback for save button
610 ---------------------------------------------------------------------*/
611 
NOTES_save_CB(Widget w,XtPointer client_data,XtPointer call_data)612 static void NOTES_save_CB( Widget w, XtPointer client_data, XtPointer call_data )
613 {
614    int ii , nnew ;
615    char * nstr , str[256] ;
616    XmString xstr ;
617 
618    if( dset == NULL || num_notes == 0 ){ XBell(dc->display,100); return; }
619 
620    MCW_invert_widget(save_pb) ;
621 
622    /* copy notes in windows into dataset, if they are changed */
623 
624    for( nnew=0,ii=1 ; ii <= num_notes ; ii++ ){
625       nstr = XmTextGetString( notar[ii]->textw ) ;  /* get text in window */
626       if( strcmp(nstr,notar[ii]->note_orig) != 0 ){ /* compare to original */
627 
628          tross_Store_Note( dset , ii , nstr ) ; nnew++ ;  /* save it */
629          free( notar[ii]->note_orig ) ;                   /* make new copy */
630          notar[ii]->note_orig = newstring(nstr) ;
631          free( notar[ii]->date_orig ) ;                   /* re-fetch date */
632          notar[ii]->date_orig = tross_Get_Notedate( dset , ii ) ;
633 
634          sprintf(str,"----- NOTE %d [%s] -----",ii,notar[ii]->date_orig) ;
635          xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ;
636          XtVaSetValues( notar[ii]->note_label , XmNlabelString , xstr , NULL ) ;
637          XmStringFree(xstr) ;
638       }
639       XtFree(nstr) ;
640    }
641 
642    /* now save notes */
643 
644    if( nnew > 0 ){
645       putenv("AFNI_DECONFLICT=OVERWRITE") ;
646       DSET_overwrite_header(dset) ;
647    }
648    else if( w != NULL )
649       (void) MCW_popup_message( save_pb ,
650                                 " \n Nothing has changed! \n " ,
651                                 MCW_USER_KILL | MCW_TIMER_KILL ) ;
652 
653    MCW_invert_widget(save_pb) ;
654    return ;
655 }
656 
657 /*-------------------------------------------------------------------
658   Callback for help button
659 ---------------------------------------------------------------------*/
660 
NOTES_help_CB(Widget w,XtPointer client_data,XtPointer call_data)661 static void NOTES_help_CB( Widget w, XtPointer client_data, XtPointer call_data )
662 {
663    (void ) new_MCW_textwin( choose_pb ,
664 
665    " \n"
666    "This plugin is used to view and edit the Notes attached to a dataset.\n"
667    "---------------------------------------------------------------------\n"
668    "The buttons at the top perform the following functions:\n"
669    "\n"
670    "  Choose Dataset: Use this to select a dataset to deal with.\n"
671    "\n"
672    "  Quit:           Exit the plugin without saving any edits to Notes\n"
673    "                  made since the last 'Save' button press.\n"
674    "\n"
675    "  Help:           I hope you already got the idea for this widget.\n"
676    "\n"
677    "  Add:            Add a new Note at the end of the existing notes.\n"
678    "\n"
679    "  Refit:          Resize all Notes' sub-windows to fit the number of\n"
680    "                  lines of text in each Note.  The maximum line count\n"
681    "                  for each sub-window is set by the Unix environment\n"
682    "                  variable AFNI_NOTES_DLINES; if this is not defined,\n"
683    "                  the default maximum is 9.\n"
684    "\n"
685    "  Save:           Save the current Notes to the dataset .HEAD file.\n"
686    "\n"
687    "  Done:           Save then Quit.\n"
688    "-------------------------------------------------------------------------- \n"
689    "Below these buttons are the Notes sub-windows.\n"
690    "\n"
691    "The first Note sub-window shows the dataset History Note.  This Note can't\n"
692    "be edited by the user - it is created by each AFNI program as a record of\n"
693    "the actions that led to this dataset. If no History Note is present in the\n"
694    "dataset .HEAD file, some edifying text will be shown here instead.\n"
695    "\n"
696    "Each other Note sub-window shows the Note number, the date of the Note's\n"
697    "creation (or last change), and the Note itself.  These windows are\n"
698    "editable.  If you want to remove a Note from the dataset, use the\n"
699    "Delete button for that Note.  (This operation is NOT reversible - using\n"
700    "the Quit button will not get a deleted Note back!)  If you edit a note\n"
701    "badly and want to restore it to the value saved in the dataset header,\n"
702    "use the Restore button.\n"
703    "\n"
704    "Notes can also be viewed with the program 3dinfo, and with the Info\n"
705    "buttons from the Datamode/Misc menu.  The command line program 3dNotes\n"
706    "lets you create dataset Notes in a batch script file.\n"
707    "\n"
708    "--- Bob Cox - September 1999\n"
709    "    (Based on ideas and some code from Tom Ross of MCW)\n"
710 
711     , TEXT_READONLY ) ;
712 
713    return ;
714 }
715 
716 /*-------------------------------------------------------------------
717   Callback for choose button.
718     - must be in current session
719   Much of this code is adapted from PLUG_choose_dataset_CB.
720 ---------------------------------------------------------------------*/
721 
722 static int                  ndsl = 0 ;
723 static PLUGIN_dataset_link * dsl = NULL ;
724 
NOTES_choose_CB(Widget w,XtPointer client_data,XtPointer call_data)725 static void NOTES_choose_CB( Widget w, XtPointer client_data, XtPointer call_data )
726 {
727    THD_session * ss  = im3d->ss_now ;           /* current session */
728    int           vv  = im3d->vinfo->view_type ; /* view type */
729    THD_3dim_dataset * qset ;
730    int id , ltop , llen ;
731    char qnam[THD_MAX_NAME] , label[THD_MAX_NAME] ;
732    static char ** strlist = NULL ;
733 
734    /* initialize */
735 
736    ndsl = 0 ;
737 
738    /* scan datasets */
739 
740    for( id=0 ; id < ss->num_dsset ; id++ ){
741       qset = GET_SESSION_DSET(ss,id,vv);
742 /*      qset = ss->dsset_xform_table[id][vv] ;*/
743 
744       if( ! ISVALID_DSET (qset) ) continue ;  /* skip */
745 
746       ndsl++ ;
747       dsl = (PLUGIN_dataset_link *)
748               XtRealloc( (char *) dsl , sizeof(PLUGIN_dataset_link)*ndsl ) ;
749 
750       make_PLUGIN_dataset_link( qset , dsl + (ndsl-1) ) ;
751    }
752 
753    /* found nothing?  exit */
754 
755    if( ndsl < 1 ){
756       (void) MCW_popup_message( choose_pb ,
757                                    "Didn't find any\ndatasets to edit!" ,
758                                 MCW_USER_KILL | MCW_TIMER_KILL ) ;
759       XBell( dc->display , 100 ) ;
760       return ;
761    }
762 
763    /*--- 23 Nov 1996: loop over dataset links and patch their titles
764                       to include an indicator of the dataset type    ---*/
765 
766    ltop = 4 ;
767    for( id=0 ; id < ndsl ; id++ ){
768       llen = strlen(dsl[id].title) ;
769       ltop = MAX(ltop,llen) ;
770    }
771 
772    for( id=0 ; id < ndsl ; id++ ){
773       qset = PLUTO_find_dset( &(dsl[id].idcode) ) ;
774       if( ! ISVALID_DSET(qset) ) continue ;
775       if( ISANAT(qset) ){
776          if( ISANATBUCKET(qset) )         /* 30 Nov 1997 */
777             sprintf(qnam,"%-*s [%s:%d]" ,
778                     ltop,dsl[id].title ,
779                     ANAT_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
780 
781          else if( DSET_NUM_TIMES(qset) == 1 )
782             sprintf(qnam,"%-*s [%s]" ,
783                     ltop,dsl[id].title ,
784                     ANAT_prefixstr[qset->func_type] ) ;
785 
786          else
787             sprintf(qnam,"%-*s [%s:3D+t:%d]" ,
788                     ltop,dsl[id].title ,
789                     ANAT_prefixstr[qset->func_type] , DSET_NUM_TIMES(qset) ) ;
790 
791       } else {
792          if( ISFUNCBUCKET(qset) )         /* 30 Nov 1997 */
793             sprintf(qnam,"%-*s [%s:%d]" ,
794                     ltop,dsl[id].title ,
795                     FUNC_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
796 
797          else if( DSET_NUM_TIMES(qset) == 1 )
798             sprintf(qnam,"%-*s [%s]" ,
799                     ltop,dsl[id].title ,
800                     FUNC_prefixstr[qset->func_type] ) ;
801 
802          else
803             sprintf(qnam,"%-*s [%s:3D+t:%d]" ,
804                     ltop,dsl[id].title ,
805                     FUNC_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
806       }
807 
808       if( DSET_COMPRESSED(qset) ) strcat(qnam,"z") ;
809 
810       strcpy( dsl[id].title , qnam ) ;
811    }
812 
813    /*--- make a popup chooser for the user to browse ---*/
814 
815    POPDOWN_strlist_chooser ;
816 
817    strlist = (char **) XtRealloc( (char *)strlist , sizeof(char *)*ndsl ) ;
818    for( id=0 ; id < ndsl ; id++ ) strlist[id] = dsl[id].title ;
819 
820    sprintf( label , "AFNI Dataset from\nthe %s" , VIEW_typestr[vv] ) ;
821 
822    MCW_choose_strlist( w , label , ndsl , -1 , strlist ,
823                        NOTES_finalize_dset_CB , NULL     ) ;
824 
825    return ;
826 }
827 
828 /*----------------------------------------------------------------------------*/
829 
NOTES_finalize_dset_CB(Widget w,XtPointer fd,MCW_choose_cbs * cbs)830 static void NOTES_finalize_dset_CB( Widget w, XtPointer fd, MCW_choose_cbs * cbs )
831 {
832    int id = cbs->ival ;
833    THD_3dim_dataset * qset ;
834    XmString xstr ;
835    char str[256] , * his ;
836    int ii , nl , nltot , ww,hh,qh ;
837 
838    /* check for errors */
839 
840    if( ! editor_open ){ POPDOWN_strlist_chooser; XBell(dc->display,100); return; }
841 
842    if( id < 0 || id >= ndsl ){ XBell(dc->display,100) ; return ; }
843 
844    qset = PLUTO_find_dset( &(dsl[id].idcode) ) ;  /* the new dataset? */
845 
846    if( qset == NULL ){ XBell(dc->display,100) ; return ; }
847 
848    /* accept this dataset */
849 
850    dset     = qset ;
851    dset_idc = qset->idcode ;   /* 31 Mar 1999 */
852 
853    /* write the informational label */
854 
855    strcpy(str,dsl[id].title) ;
856    xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ;
857    XtVaSetValues( info_lab , XmNlabelString , xstr , NULL ) ;
858    XmStringFree(xstr) ;
859 
860    /* clear any existing notes windows */
861 
862    XtUnmanageChild( notesw ) ;
863    XtUnmanageChild( noterc ) ;
864    for( ii=0 ; ii < notar_num ; ii++ ){
865       XtUnmanageChild( notar[ii]->frame ) ;
866 
867       if( notar[ii]->note_orig != NULL ){
868           free(notar[ii]->note_orig) ; notar[ii]->note_orig = NULL ;
869       }
870 
871       if( notar[ii]->date_orig != NULL ){
872           free(notar[ii]->date_orig) ; notar[ii]->date_orig = NULL ;
873       }
874    }
875 
876    /* make new Notes windows, if necessary */
877 
878    num_notes = tross_Get_Notecount( dset ) ;
879 
880    if( notar_num < num_notes + 1 ){
881       notar = (NOTE_wind **) realloc( notar, sizeof(NOTE_wind *)*(num_notes+1) ) ;
882       for( ii=notar_num ; ii <= num_notes ; ii++ )
883          notar[ii]  = NOTES_make_note() ;
884       notar_num = num_notes + 1 ;
885    }
886 
887    /* get and set Notes text for each window */
888 
889    his = tross_Get_History( dset ) ;
890    if( his == NULL ){
891       ii = ( lrand48() >> 8) % NUM_DH ;
892       notar[0]->note_orig = newstring(default_history[ii]) ;
893       xstr = XmStringCreateLtoR( "----- EDIFYING TEXT -----" , XmFONTLIST_DEFAULT_TAG ) ;
894    } else {
895       notar[0]->note_orig = tross_breakup_string( his , 2*TWIDTH/3 , TWIDTH-1 ) ;
896       free(his) ;
897       xstr = XmStringCreateLtoR( "----- HISTORY -----" , XmFONTLIST_DEFAULT_TAG ) ;
898    }
899    notar[0]->date_orig = NULL ;
900 
901    XtVaSetValues( notar[0]->note_label , XmNlabelString , xstr , NULL ) ;
902    XmStringFree(xstr) ;
903    XmTextSetString( notar[0]->textw , notar[0]->note_orig ) ;
904 
905    nl = line_count( notar[0]->note_orig ) ;
906    if( nl > max_tlines ) nl = max_tlines ;
907    XtVaSetValues( notar[0]->textw , XmNrows , nl , NULL ) ;
908    nltot = nl ;
909 
910    XtManageChild( notar[0]->frame ) ;
911 
912    for( ii=1 ; ii <= num_notes ; ii++ ){
913       notar[ii]->note_orig = tross_Get_Note    ( dset , ii ) ;
914       notar[ii]->date_orig = tross_Get_Notedate( dset , ii ) ;
915 
916       if( notar[ii]->note_orig == NULL )
917          notar[ii]->note_orig = newstring( empty_note ) ;
918 
919       if( notar[ii]->date_orig == NULL )
920          notar[ii]->date_orig = newstring( no_date ) ;
921 
922       sprintf(str,"----- NOTE %d [%s] -----",ii,notar[ii]->date_orig) ;
923       xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ;
924       XtVaSetValues( notar[ii]->note_label , XmNlabelString , xstr , NULL ) ;
925       XmStringFree(xstr) ;
926       XmTextSetString( notar[ii]->textw , notar[ii]->note_orig ) ;
927 
928       nl = line_count( notar[ii]->note_orig ) ;
929       if( nl > max_tlines ) nl = max_tlines ;
930       XtVaSetValues( notar[ii]->textw , XmNrows , nl , NULL ) ;
931       nltot += nl ;
932 
933       XtManageChild( notar[ii]->frame ) ;
934    }
935 
936    XtVaSetValues( notar[0]->scrollw , XmNwidth , text_width , NULL ) ;
937 
938    XtManageChild( noterc ) ;
939    XtManageChild( notesw ) ;
940 
941    /* set size of scrolling area for notes */
942 
943    MCW_widget_geom( noterc , &ww,&hh , NULL,NULL ) ;
944    ww += 4 ; hh += 4 ; qh = hh ;
945    if( ww > dc->width - 128 ) ww = dc->width - 128 ;
946    if( hh > dc->height- 128 ) hh = dc->height- 128 ;
947 
948    XtVaSetValues( notesw , XmNwidth,ww , XmNheight,hh , NULL ) ;
949 
950    if( qh > hh ) scroll_topbot( notesw , 0 ) ;
951    return ;
952 }
953 
954 /*----------------------------------------------------------------------------*/
955 
NOTES_add_CB(Widget w,XtPointer client_data,XtPointer call_data)956 static void NOTES_add_CB( Widget w, XtPointer client_data, XtPointer call_data )
957 {
958    int nl , hh , ii , qh ;
959    char str[256] ;
960    XmString xstr ;
961 
962    if( dset == NULL ){ XBell(dc->display,100) ; return ; }
963 
964    if( num_notes >= MAX_DSET_NOTES ){
965       (void) MCW_popup_message( add_pb ,
966                                 " \n"
967                                 " Max number of notes\n"
968                                 " would be exceeded!\n " ,
969                                 MCW_USER_KILL | MCW_TIMER_KILL ) ;
970       XBell(dc->display,100) ; return ;
971    }
972 
973    tross_Add_Note( dset , empty_note ) ;
974 
975    num_notes ++ ;
976 
977    if( notar_num < num_notes + 1 ){
978       notar = (NOTE_wind **) realloc( notar, sizeof(NOTE_wind *)*(num_notes+1) ) ;
979       for( ii=notar_num ; ii <= num_notes ; ii++ )
980          notar[ii]  = NOTES_make_note() ;
981       notar_num = num_notes + 1 ;
982    }
983 
984    notar[num_notes]->note_orig = tross_Get_Note    ( dset , num_notes ) ;
985    notar[num_notes]->date_orig = tross_Get_Notedate( dset , num_notes ) ;
986 
987    if( notar[num_notes]->note_orig == NULL )
988       notar[num_notes]->note_orig = newstring( empty_note ) ;
989 
990    if( notar[num_notes]->date_orig == NULL )
991       notar[num_notes]->date_orig = newstring( no_date ) ;
992 
993    sprintf(str,"----- NOTE %d [%s] -----",num_notes,notar[num_notes]->date_orig) ;
994    xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ;
995    XtVaSetValues( notar[num_notes]->note_label , XmNlabelString , xstr , NULL ) ;
996    XmStringFree(xstr) ;
997    XmTextSetString( notar[num_notes]->textw , notar[num_notes]->note_orig ) ;
998 
999    nl = line_count( notar[num_notes]->note_orig ) ;
1000    if( nl > max_tlines ) nl = max_tlines ;
1001    XtVaSetValues( notar[num_notes]->textw , XmNrows , nl , NULL ) ;
1002 
1003    XtManageChild( notar[num_notes]->frame ) ;
1004 
1005    /* set size of scrolling area for notes */
1006 
1007    MCW_widget_geom( noterc , NULL,&hh , NULL,NULL ) ; hh +=4 ; qh = hh ;
1008    if( hh > dc->height- 128 ) hh = dc->height- 128 ;
1009 
1010    XtVaSetValues( notesw , XmNheight,hh , NULL ) ;
1011 
1012    if( qh > hh ) scroll_topbot( notesw , 1 ) ;
1013    return ;
1014 }
1015 
1016 /*----------------------------------------------------------------------------*/
1017 
NOTES_refit_CB(Widget w,XtPointer client_data,XtPointer call_data)1018 static void NOTES_refit_CB( Widget w, XtPointer client_data, XtPointer call_data )
1019 {
1020    int ii , hh , nl , qh ;
1021    char * ts ;
1022 
1023    if( dset == NULL ){ XBell(dc->display,100) ; return ; }
1024 
1025    for( ii=0 ; ii <= num_notes ; ii++ ){
1026       ts =  XmTextGetString( notar[ii]->textw ) ;  /* get text in window */
1027       nl = line_count( ts ) ; XtFree( ts ) ;       /* count lines */
1028       if( nl > max_tlines ) nl = max_tlines ;
1029       XtVaSetValues( notar[ii]->textw , XmNrows , nl , NULL ) ;
1030    }
1031 
1032    /* set size of scrolling area for notes */
1033 
1034    MCW_widget_geom( noterc , NULL,&hh , NULL,NULL ) ; hh += 4 ; qh = hh ;
1035    if( hh > dc->height- 128 ) hh = dc->height- 128 ;
1036 
1037    XtVaSetValues( notesw , XmNheight,hh , NULL ) ;
1038    return ;
1039 }
1040 
1041 /*----------------------------------------------------------------------------*/
1042 
NOTES_delete_CB(Widget w,XtPointer client_data,XtPointer call_data)1043 static void NOTES_delete_CB( Widget w, XtPointer client_data, XtPointer call_data )
1044 {
1045    int ii , kk , hh , nl ;
1046    char str[256] , * ts ;
1047    XmString xstr ;
1048 
1049    if( dset == NULL ) return ;    /* should never happen */
1050 
1051    for( kk=1 ; kk <= num_notes ; kk++ )
1052       if( w == notar[kk]->delete_pb ) break ;
1053 
1054    if( kk > num_notes ) return ;  /* should never happen */
1055 
1056    tross_Delete_Note( dset , kk ) ;  /* delete it in the dataset */
1057 
1058    /* erase the info in the kk-th Note window */
1059 
1060    free( notar[kk]->note_orig ) ;  /* this is history */
1061    free( notar[kk]->date_orig ) ;
1062 
1063    /* move the info in the Note windows above kk down to the previous window */
1064 
1065    for( ii=kk+1 ; ii <= num_notes ; ii++ ){
1066       notar[ii-1]->note_orig = notar[ii]->note_orig ;  /* move the orig */
1067       notar[ii-1]->date_orig = notar[ii]->date_orig ;  /* stuff down    */
1068 
1069       ts = XmTextGetString( notar[ii]->textw ) ;       /* move the text */
1070       XmTextSetString( notar[ii-1]->textw , ts ) ;     /* in the window */
1071       nl = line_count( ts ) ;
1072       if( nl > max_tlines ) nl = max_tlines ;
1073       XtVaSetValues( notar[ii-1]->textw , XmNrows , nl , NULL ) ;
1074       XtFree(ts) ;
1075 
1076       sprintf(str,"----- NOTE %d [%s] -----",ii-1,notar[ii-1]->date_orig) ;
1077       xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ;
1078       XtVaSetValues( notar[ii-1]->note_label , XmNlabelString , xstr , NULL ) ;
1079       XmStringFree(xstr) ;
1080    }
1081 
1082    notar[num_notes]->note_orig = NULL ;
1083    notar[num_notes]->date_orig = NULL ;
1084    XmTextSetString( notar[num_notes]->textw , "\0" ) ;
1085    XtUnmanageChild( notar[num_notes]->frame ) ;
1086    num_notes-- ;
1087 
1088    MCW_widget_geom( noterc , NULL,&hh , NULL,NULL ) ;
1089    if( hh > dc->height- 128 ) hh = dc->height- 128 ;
1090    XtVaSetValues( notesw , XmNheight,hh , NULL ) ;
1091 
1092    return ;
1093 }
1094 
1095 /*----------------------------------------------------------------------------*/
1096 
NOTES_restore_CB(Widget w,XtPointer client_data,XtPointer call_data)1097 static void NOTES_restore_CB( Widget w, XtPointer client_data, XtPointer call_data )
1098 {
1099    int kk ;
1100 
1101    if( dset == NULL ) return ;    /* should never happen */
1102 
1103    for( kk=1 ; kk <= num_notes ; kk++ )
1104       if( w == notar[kk]->restore_pb ) break ;
1105 
1106    if( kk > num_notes ) return ;  /* should never happen */
1107 
1108    XmTextSetString( notar[kk]->textw , notar[kk]->note_orig ) ;
1109    return ;
1110 }
1111 
1112 /*----------------------------------------------------------------------------*/
1113 
scroll_topbot(Widget sw,int where)1114 static void scroll_topbot( Widget sw , int where )
1115 {
1116    Widget sb=NULL ;
1117    int val,siz,inc,pag , smin=0,smax=0 ;
1118 
1119    if( sw == NULL ) return ;
1120 
1121    XtVaGetValues( sw ,
1122                      XmNverticalScrollBar , &sb ,
1123                      XmNmaximum           , &smax ,
1124                      XmNminimum           , &smin ,
1125                   NULL ) ;
1126    if( sb == NULL ) return ;
1127 
1128    XmScrollBarGetValues( sb , &val,&siz,&inc,&pag ) ;
1129 
1130    if( where == 0 ) val = smin ;  /* to top */
1131    else             val = smax ;  /* to bot */
1132 
1133    XmScrollBarSetValues( sb , val,siz,inc,pag , True ) ;
1134    return ;
1135 }
1136