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