NCBI C Toolkit Cross Reference

C/desktop/dlgutil1.c


  1 /*   dlgutil1.c
  2 * ===========================================================================
  3 *
  4 *                            PUBLIC DOMAIN NOTICE
  5 *            National Center for Biotechnology Information (NCBI)
  6 *
  7 *  This software/database is a "United States Government Work" under the
  8 *  terms of the United States Copyright Act.  It was written as part of
  9 *  the author's official duties as a United States Government employee and
 10 *  thus cannot be copyrighted.  This software/database is freely available
 11 *  to the public for use. The National Library of Medicine and the U.S.
 12 *  Government do not place any restriction on its use or reproduction.
 13 *  We would, however, appreciate having the NCBI and the author cited in
 14 *  any work or product based on this material
 15 *
 16 *  Although all reasonable efforts have been taken to ensure the accuracy
 17 *  and reliability of the software and data, the NLM and the U.S.
 18 *  Government do not and cannot warrant the performance or results that
 19 *  may be obtained by using this software or data. The NLM and the U.S.
 20 *  Government disclaim all warranties, express or implied, including
 21 *  warranties of performance, merchantability or fitness for any particular
 22 *  purpose.
 23 *
 24 * ===========================================================================
 25 *
 26 * File Name:  dlgutil1.c
 27 *
 28 * Author:  Jonathan Kans
 29 *
 30 * Version Creation Date:   1/22/95
 31 *
 32 * $Revision: 6.151 $
 33 *
 34 * File Description: 
 35 *
 36 * Modifications:  
 37 * --------------------------------------------------------------------------
 38 * Date     Name        Description of modification
 39 * -------  ----------  -----------------------------------------------------
 40 *
 41 *
 42 * ==========================================================================
 43 */
 44 
 45 #include <dlogutil.h>
 46 #include <document.h>
 47 #include <gather.h>
 48 #include <subutil.h>
 49 #include <objfdef.h>
 50 #include <gbfeat.h>
 51 #include <gbftdef.h>
 52 #include <edutil.h>
 53 #include <explore.h>
 54 #include <sqnutils.h>
 55 #include <alignmgr2.h>
 56 #include <toasn3.h>
 57 #include <vibforms.h>
 58 #include <cdrgn.h>
 59 #include <findrepl.h>
 60 
 61 #define NUMBER_OF_SUFFIXES    8
 62 
 63 static CharPtr name_suffix_labels [] = {
 64   " ", "Jr.", "Sr.", "II", "III", "IV", "V", "VI", NULL
 65 };
 66 
 67 static ENUM_ALIST(name_suffix_alist)
 68   {" ",    0},
 69   {"Jr.",  1},
 70   {"Sr.",  2},
 71   {"II",   3},
 72   {"III",  4},
 73   {"IV",   5},
 74   {"V",    6},
 75   {"VI",   7},
 76 END_ENUM_ALIST
 77 
 78 Uint2 author_types [] = {
 79   TAGLIST_TEXT, TAGLIST_TEXT, TAGLIST_TEXT, TAGLIST_POPUP
 80 };
 81 
 82 Uint2 std_author_widths [] = {
 83   8, 4, 9, 0
 84 };
 85 
 86 static EnumFieldAssocPtr author_popups [] = {
 87   NULL, NULL, NULL, name_suffix_alist
 88 };
 89 
 90 Uint2 str_author_widths [] = {
 91   24
 92 };
 93 
 94 typedef struct authordialog {
 95   DIALOG_MESSAGE_BLOCK
 96   DialoG             stdAuthor;
 97   DialoG             strAuthor;
 98   GrouP              stdGrp;
 99   GrouP              strGrp;
100   Uint1              type;
101 } AuthorDialog, PNTR AuthorDialogPtr;
102 
103 StdPrintOptionsPtr  spop = NULL;
104 
105 extern Boolean SetupPrintOptions (void)
106 
107 {
108   if (spop == NULL) {
109     spop = StdPrintOptionsNew (NULL);
110     if (spop != NULL) {
111       spop->newline = "\r";
112       spop->indent = "";
113     } else {
114       Message (MSG_FATAL, "StdPrintOptionsNew failed");
115     }
116   }
117   return (Boolean) (spop != NULL);
118 }
119 
120 extern void FreePrintOptions (void)
121 
122 {
123   spop = StdPrintOptionsFree (spop);
124 }
125 
126 ENUM_ALIST(months_alist)
127   {" ",     0},
128   {"Jan",   1},
129   {"Feb",   2},
130   {"Mar",   3},
131   {"Apr",   4},
132   {"May",   5},
133   {"Jun",   6},
134   {"Jul",   7},
135   {"Aug",   8},
136   {"Sep",   9},
137   {"Oct",  10},
138   {"Nov",  11},
139   {"Dec",  12},
140 END_ENUM_ALIST
141 
142 extern void SetDescriptorPropagate (BioseqSetPtr bssp)
143 {
144   BioseqPtr         bsp;
145   SeqEntryPtr       seqentry;
146   ValNodePtr        sourcedescr;
147 
148   if (bssp != NULL) {
149     sourcedescr = bssp->descr;
150     if (sourcedescr != NULL) {
151       bssp->descr = NULL;
152       seqentry = bssp->seq_set;
153       while (seqentry != NULL) {
154         if (seqentry->data.ptrvalue != NULL) {
155           if (seqentry->choice == 1) {
156             bsp = (BioseqPtr) seqentry->data.ptrvalue;
157             ValNodeLink (&(bsp->descr),
158                          AsnIoMemCopy ((Pointer) sourcedescr,
159                                        (AsnReadFunc) SeqDescrAsnRead,
160                                        (AsnWriteFunc) SeqDescrAsnWrite));
161           } else if (seqentry->choice == 2) {
162             bssp = (BioseqSetPtr) seqentry->data.ptrvalue;
163             ValNodeLink (&(bssp->descr),
164                          AsnIoMemCopy ((Pointer) sourcedescr,
165                                        (AsnReadFunc) SeqDescrAsnRead,
166                                        (AsnWriteFunc) SeqDescrAsnWrite));
167           }
168         }
169         seqentry = seqentry->next;
170       }
171       SeqDescrFree (sourcedescr);
172     }
173   }
174 }
175 
176 
177 static void CopyOneDescriptorToSeqEntry (SeqDescrPtr sdp, SeqEntryPtr sep)
178 {
179   BioseqPtr bsp;
180   BioseqSetPtr bssp;
181 
182   if (sdp == NULL || sep == NULL) {
183     return;
184   }
185   
186   /* NOTE - we are using SeqDescAsnRead and SeqDescAsnWrite
187    * instead of SeqDescrAsnWrite and SeqDescrAsnWrite
188    * so that only THIS descriptor is propagated, rather than than chain
189    */
190   if (sep->choice == 1) {
191     bsp = (BioseqPtr) sep->data.ptrvalue;
192     ValNodeLink (&(bsp->descr),
193                   AsnIoMemCopy ((Pointer) sdp,
194                                 (AsnReadFunc) SeqDescAsnRead,
195                                 (AsnWriteFunc) SeqDescAsnWrite));
196   } else if (sep->choice == 2) {
197     bssp = (BioseqSetPtr) sep->data.ptrvalue;
198     ValNodeLink (&(bssp->descr),
199                   AsnIoMemCopy ((Pointer) sdp,
200                                 (AsnReadFunc) SeqDescAsnRead,
201                                 (AsnWriteFunc) SeqDescAsnWrite));
202   }
203 }
204 
205 
206 extern Int2 LIBCALLBACK DescriptorPropagate (Pointer data)
207 
208 {
209   BioseqSetPtr      bssp = NULL;
210   OMProcControlPtr  ompcp;
211   SeqDescrPtr sdp;
212   ObjValNodePtr ovp;
213   SeqEntryPtr   sep;
214 
215   ompcp = (OMProcControlPtr) data;
216   if (ompcp == NULL || ompcp->input_entityID == 0) {
217     Message (MSG_ERROR, "Please select a BioseqSet");
218     return OM_MSG_RET_ERROR;
219   }
220   switch (ompcp->input_itemtype) {
221     case OBJ_BIOSEQSET :
222       bssp = (BioseqSetPtr) ompcp->input_data;
223       break;
224     case OBJ_SEQDESC:
225       /* special case - propagate just this descriptor */
226       sdp = (SeqDescrPtr) ompcp->input_data;
227       if (sdp != NULL && sdp->extended > 0) {
228         ovp = (ObjValNodePtr) sdp;
229         if (ovp->idx.parenttype == OBJ_BIOSEQSET && ovp->idx.parentptr != NULL) {
230           bssp = ovp->idx.parentptr;
231           for (sep = bssp->seq_set; sep != NULL; sep = sep->next) {
232             CopyOneDescriptorToSeqEntry (sdp, sep);
233           }
234           ovp->idx.deleteme = TRUE;
235           DeleteMarkedObjects (ompcp->input_entityID, 0, NULL);
236           ObjMgrSetDirtyFlag (ompcp->input_entityID, TRUE);
237           ObjMgrSendMsg (OM_MSG_UPDATE, ompcp->input_entityID, 0, 0);
238           return OM_MSG_RET_DONE;
239         }
240       }
241       return OM_MSG_RET_ERROR;
242       break;
243     case 0 :
244       Message (MSG_ERROR, "Please select a BioseqSet");
245       return OM_MSG_RET_ERROR;
246     default :
247       Message (MSG_ERROR, "Please select a BioseqSet");
248       return OM_MSG_RET_ERROR;
249   }
250 
251   SetDescriptorPropagate (bssp);
252 
253   ObjMgrSetDirtyFlag (ompcp->input_entityID, TRUE);
254   ObjMgrSendMsg (OM_MSG_UPDATE, ompcp->input_entityID, 0, 0);
255   return OM_MSG_RET_DONE;
256 }
257 
258 extern Boolean DescFormReplaceWithoutUpdateProc (ForM f)
259 
260 {
261   MsgAnswer          ans;
262   DescriptorFormPtr  dfp;
263   Int4Ptr            intptr;
264   OMProcControl      ompc;
265   Boolean            rsult;
266   ValNodePtr         sdp;
267   SeqEntryPtr        sep;
268   BioseqSetPtr       bssp;
269 
270   rsult = FALSE;
271   dfp = (DescriptorFormPtr) GetObjectExtra (f);
272   if (dfp != NULL) {
273     MemSet ((Pointer) &ompc, 0, sizeof (OMProcControl));
274     ompc.input_entityID = dfp->input_entityID;
275     ompc.input_itemID = dfp->input_itemID;
276     ompc.input_itemtype = dfp->input_itemtype;
277     ompc.output_itemtype = dfp->input_itemtype;
278     sdp = SeqDescrNew (NULL);
279     if (sdp != NULL) {
280       sdp->choice = (Uint1)dfp->this_subtype;
281       switch (sdp->choice) {
282         case Seq_descr_mol_type :
283         case Seq_descr_method :
284           intptr = (Int4Ptr) DialogToPointer (dfp->data);
285           if (intptr != NULL) {
286             sdp->data.intvalue = *intptr;
287           }
288           break;
289         default :
290           sdp->data.ptrvalue = DialogToPointer (dfp->data);
291           break;
292       }
293       FixSpecialCharactersForObject (OBJ_SEQDESC, sdp, "You may not include special characters in the text.\nIf you do not choose replacement characters, these special characters will be replaced with '#'.", TRUE, NULL);
294 
295       ompc.output_data = (Pointer) sdp;
296       if (ompc.input_entityID == 0) {
297         if (! ObjMgrRegister (OBJ_SEQDESC, (Pointer) sdp)) {
298           Message (MSG_ERROR, "ObjMgrRegister failed");
299         }
300       } else if (ompc.input_itemtype != OBJ_SEQDESC) {
301         ompc.output_itemtype = OBJ_SEQDESC;
302         if (! AttachDataForProc (&ompc, FALSE)) {
303           Message (MSG_ERROR, "AttachDataForProc failed");
304         }
305         rsult = TRUE;
306       } else {
307         if (! ReplaceDataForProc (&ompc, FALSE)) {
308           Message (MSG_ERROR, "ReplaceDataForProc failed");
309         }
310         rsult = TRUE;
311       }
312     }
313 
314     /* If the descriptor was added to a GenBank set then*/
315     /* optionally propagate it to the set's Bioseqs.    */
316 
317     if (ompc.input_itemtype == OBJ_BIOSEQSET) {
318       sep = (SeqEntryPtr) ompc.input_choice;
319       bssp = (BioseqSetPtr) sep->data.ptrvalue;
320       if (bssp->_class == BioseqseqSet_class_genbank) {
321     ans = Message (MSG_YN, "Do you wish to propagate the descriptor to "
322                "the set's Bioseqs?");
323     if (ANS_YES == ans)
324       DescriptorPropagate (&ompc);
325       }
326     }
327     
328   }
329   return rsult;
330 }
331 
332 extern void StdDescFormActnProc (ForM f)
333 
334 {
335   DescriptorFormPtr  dfp;
336 
337   if (DescFormReplaceWithoutUpdateProc (f)) {
338     dfp = (DescriptorFormPtr) GetObjectExtra (f);
339     if (dfp != NULL) {
340       GetRidOfEmptyFeatsDescStrings (dfp->input_entityID, NULL);
341       if (GetAppProperty ("InternalNcbiSequin") != NULL) {
342         ExtendGeneFeatIfOnMRNA (dfp->input_entityID, NULL);
343       }
344       ObjMgrSetDirtyFlag (dfp->input_entityID, TRUE);
345       ObjMgrSendMsg (OM_MSG_UPDATE, dfp->input_entityID,
346                      dfp->input_itemID, dfp->input_itemtype);
347     }
348   }
349 }
350 
351 extern void StdDescFormCleanupProc (GraphiC g, VoidPtr data)
352 
353 {
354   DescriptorFormPtr  dfp;
355   Uint2              userkey;
356 
357   dfp = (DescriptorFormPtr) data;
358   if (dfp != NULL) {
359     if (dfp->input_entityID > 0 && dfp->userkey > 0) {
360       userkey = dfp->userkey;
361       dfp->userkey = 0;
362       ObjMgrFreeUserData (dfp->input_entityID, dfp->procid, dfp->proctype, userkey);
363     }
364   }
365   StdCleanupExtraProc (g, data);
366 }
367 
368 extern OMUserDataPtr ItemAlreadyHasEditor (Uint2 entityID, Uint4 itemID, Uint2 itemtype, Uint2 procid)
369 
370 {
371   BaseFormPtr    bfp;
372   Uint4          j;
373   Uint4          num;
374   ObjMgrPtr      omp;
375   ObjMgrDataPtr  PNTR omdpp;
376   OMUserDataPtr  omudp;
377   ObjMgrDataPtr  tmp;
378 
379   if (entityID == 0 || itemID == 0 || itemtype == 0 || procid == 0) return NULL;
380   omp = ObjMgrGet ();
381   if (omp == NULL) return NULL;
382   num = omp->currobj;
383   for (j = 0, omdpp = omp->datalist; j < num && omdpp != NULL; j++, omdpp++) {
384     tmp = *omdpp;
385     if (tmp->parentptr == NULL && tmp->EntityID == entityID) {
386 
387       for (omudp = tmp->userdata; omudp != NULL; omudp = omudp->next) {
388         if (omudp->proctype == OMPROC_EDIT && omudp->procid == procid) {
389           bfp = (BaseFormPtr) omudp->userdata.ptrvalue;
390           if (bfp != NULL) {
391             if (bfp->input_itemID == itemID && bfp->input_itemtype == itemtype) {
392               return omudp;
393             }
394           }
395         }
396       }
397     }
398   }
399   return NULL;
400 }
401 
402 
403 extern Uint2 GetProcIdForItemEditor (Uint2 entityID, Uint2 itemID, Uint1 itemtype, Uint2 subinputtype)
404 {
405   ObjMgrPtr     omp;
406     ObjMgrProcPtr ompp=NULL;
407   Uint2         best_procid = 0;
408 
409   omp = ObjMgrGet ();
410 
411     while ((ompp = ObjMgrProcFindNext(omp, OMPROC_EDIT, itemtype, itemtype, ompp)) != NULL)
412     {
413         if (ompp->subinputtype == subinputtype)
414         {
415       return ompp->procid;
416         }
417         else if (! ompp->subinputtype)  /* general proc found */
418     {
419             best_procid = ompp->procid;
420     }
421     }
422   return best_procid;
423 }
424 
425 
426 /* Note - if a viewer displays features from a far sequence mapped to
427  * the current sequence, it will register an interest in the entityID
428  * for the far sequence.
429  * To find the "main" viewer for just the original sequence, we want
430  * to find a view for which only one entityID is registered.
431  */
432 extern OMUserDataPtr EntityAlreadyHasViewer (Uint2 entityID)
433 
434 {
435   BaseFormPtr    bfp;
436   Uint4          j;
437   Uint4          num;
438   ObjMgrPtr      omp;
439   ObjMgrDataPtr  PNTR omdpp;
440   OMUserDataPtr  omudp;
441   ObjMgrDataPtr  tmp;
442   ValNodePtr     found_views = NULL, vnp_view;
443   ValNodePtr     view_forms = NULL, vnp_form;
444   Boolean        not_this_one;
445 
446   if (entityID == 0) return NULL;
447   omp = ObjMgrGet ();
448   if (omp == NULL) return NULL;
449   num = omp->currobj;
450   for (j = 0, omdpp = omp->datalist; j < num && omdpp != NULL; j++, omdpp++) {
451     tmp = *omdpp;
452     if (tmp->parentptr == NULL && tmp->EntityID == entityID) {
453 
454       for (omudp = tmp->userdata; omudp != NULL; omudp = omudp->next) {
455         if (omudp->proctype == OMPROC_VIEW) {
456           bfp = (BaseFormPtr) omudp->userdata.ptrvalue;
457           if (bfp != NULL) {
458             ValNodeAddPointer (&found_views, 0, omudp);
459             ValNodeAddPointer (&view_forms, 0, bfp);
460           }
461         }
462       }
463     }
464   }
465   
466   /* now look to see if the form is also a view for another entityID */
467   for (vnp_view = found_views, vnp_form = view_forms;
468        vnp_view != NULL && vnp_form != NULL;
469        vnp_view = vnp_view->next, vnp_form = vnp_form->next) {
470     not_this_one = FALSE;
471     for (j = 0, omdpp = omp->datalist; j < num && omdpp != NULL && !not_this_one; j++, omdpp++) {
472       tmp = *omdpp;
473       if (tmp->parentptr == NULL && tmp->EntityID != entityID) {
474 
475         for (omudp = tmp->userdata; omudp != NULL && !not_this_one; omudp = omudp->next) {
476           if (omudp->proctype == OMPROC_VIEW) {
477             bfp = (BaseFormPtr) omudp->userdata.ptrvalue;
478             if (bfp != NULL && bfp == (BaseFormPtr) vnp_form->data.ptrvalue) {
479               not_this_one = TRUE;
480             }
481           }
482         }
483       }
484     }
485     if (!not_this_one) {
486       omudp = vnp_view->data.ptrvalue;
487       found_views = ValNodeFree (found_views);
488       view_forms = ValNodeFree (view_forms);
489       return omudp;
490     }
491   }
492   found_views = ValNodeFree (found_views);
493   view_forms = ValNodeFree (view_forms);
494   
495   return NULL;
496 }
497 
498 
499 extern Boolean MakeViewerIndependent (Uint2 entityID, OMUserDataPtr omudp)
500 {
501   Uint4          j;
502   Uint4          num;
503   ObjMgrPtr      omp;
504   ObjMgrDataPtr  PNTR omdpp;
505   OMUserDataPtr  omudp_tmp;
506   ObjMgrDataPtr  tmp;
507 
508   if (entityID == 0 || omudp == NULL) return FALSE;
509   omp = ObjMgrGet ();
510   if (omp == NULL) return FALSE;
511   num = omp->currobj;
512   for (j = 0, omdpp = omp->datalist; j < num && omdpp != NULL; j++, omdpp++) {
513     tmp = *omdpp;
514     if (tmp->parentptr == NULL && tmp->EntityID == entityID) {
515       omudp_tmp = tmp->userdata;
516       while (omudp_tmp != NULL && omudp_tmp != omudp) {
517         omudp_tmp = omudp_tmp->next;
518       }
519       if (omudp_tmp != NULL) {
520         tmp->tempload = TL_NOT_TEMP;
521         return TRUE;
522       }
523     }
524   }
525   return FALSE;
526 }
527 
528 
529 typedef struct genegatherlist {
530   FeatureFormPtr  ffp;
531   ObjMgrPtr       omp;
532   SeqLocPtr       slp;
533   GeneRefPtr      genexref;
534   Boolean         xrefmatch;
535   Int2            idx;
536   Int2            val;
537   Int4            min;
538   Uint2           geneEntityID;
539   Uint4           geneItemID;
540   Uint2           geneItemtype;
541   Boolean         geneFound;
542   SeqLocPtr       old_feature_location;
543   ValNodePtr      lastgene;
544 } GeneGatherList, PNTR GeneGatherPtr;
545 
546 static Boolean GeneFindFunc (GatherContextPtr gcp)
547 
548 {
549   GeneGatherPtr  ggp;
550   ObjMgrTypePtr  omtp;
551   SeqFeatPtr     sfp;
552   Char           thislabel [41];
553 
554   if (gcp == NULL) return TRUE;
555 
556   ggp = (GeneGatherPtr) gcp->userdata;
557   if (ggp == NULL ) return TRUE;
558 
559   thislabel [0] = '\0';
560 
561   if (gcp->thistype == OBJ_SEQFEAT) {
562     sfp = (SeqFeatPtr) gcp->thisitem;
563     if (sfp != NULL && sfp->data.choice == SEQFEAT_GENE) {
564       omtp = ObjMgrTypeFind (ggp->omp, gcp->thistype, NULL, NULL);
565       if (omtp == NULL) {
566         return TRUE;
567       }
568       if (omtp->labelfunc != NULL) {
569         (*(omtp->labelfunc)) (gcp->thisitem, thislabel, 40, OM_LABEL_CONTENT);
570       }
571       if (thislabel [0] != '\0') {
572         ggp->idx++;
573         if (ggp->idx == ggp->val) {
574           ggp->geneEntityID = gcp->entityID;
575           ggp->geneItemID = gcp->itemID;
576           ggp->geneItemtype = gcp->thistype;
577           ggp->geneFound = TRUE;
578           return FALSE;
579         }
580       }
581     }
582   }
583   return TRUE;
584 }
585 
586 extern void Nlm_LaunchGeneFeatEd (ButtoN b);
587 extern void Nlm_LaunchGeneFeatEd (ButtoN b)
588 
589 {
590   FeatureFormPtr  ffp;
591   GeneGatherList  ggl;
592   GatherScope     gs;
593   Int2            handled;
594   Int2            val;
595 
596   ffp = (FeatureFormPtr) GetObjectExtra (b);
597   if (ffp != NULL && ffp->gene != NULL && GetValue (ffp->useGeneXref) == 1) {
598     val = GetValue (ffp->gene);
599     if (val > 2) {
600       ggl.ffp = ffp;
601       ggl.omp = ObjMgrGet ();
602       ggl.idx = 2;
603       ggl.val = val;
604       ggl.min = INT4_MAX;
605       ggl.geneFound = FALSE;
606       ggl.geneEntityID = 0;
607       ggl.geneItemID = 0;
608       ggl.geneItemtype = 0;
609       MemSet ((Pointer) (&gs), 0, sizeof (GatherScope));
610       gs.seglevels = 1;
611       gs.get_feats_location = TRUE;
612       MemSet((Pointer)(gs.ignore), (int)(TRUE), (size_t)(OBJ_MAX * sizeof(Boolean)));
613       gs.ignore[OBJ_BIOSEQ] = FALSE;
614       gs.ignore[OBJ_BIOSEQ_SEG] = FALSE;
615       gs.ignore[OBJ_SEQFEAT] = FALSE;
616       gs.ignore[OBJ_SEQANNOT] = FALSE;
617       gs.scope = GetBestTopParentForItemID (ffp->input_entityID,
618                                             ffp->input_itemID,
619                                             ffp->input_itemtype);
620       GatherEntity (ffp->input_entityID, (Pointer) &ggl, GeneFindFunc, &gs);
621       if (ggl.geneFound) {
622         WatchCursor ();
623         Update ();
624         handled = GatherProcLaunch (OMPROC_EDIT, FALSE, ggl.geneEntityID, ggl.geneItemID,
625                                     ggl.geneItemtype, 0, 0, ggl.geneItemtype, 0);
626         ArrowCursor ();
627         Update ();
628         if (handled != OM_MSG_RET_DONE || handled == OM_MSG_RET_NOPROC) {
629           Message (MSG_ERROR, "Unable to launch editor on gene feature.");
630         }
631       }
632     }
633   }
634 }
635 
636 extern void UpdateGeneLocation 
637 (SeqFeatPtr gene,
638  SeqLocPtr  old_feat_loc,
639  SeqLocPtr  new_feat_loc,
640  Uint2      entityID)
641 {
642   Uint1          strandfeat, strandgene, strandold;
643   BioseqPtr      bsp;
644   SeqLocPtr      tmpslp, slp;
645   Boolean        hasNulls;
646   Boolean        noLeft;
647   Boolean        noRight;
648   Boolean        noLeftFeat;
649   Boolean        noLeftGene;
650   Boolean        noRightFeat;
651   Boolean        noRightGene;
652 
653   if (gene == NULL || new_feat_loc == NULL)
654   {
655     return;
656   }
657   
658   strandfeat = SeqLocStrand (new_feat_loc);
659   strandgene = SeqLocStrand (gene->location); 
660   if (old_feat_loc == NULL)
661   {
662     strandold = strandfeat;
663   }
664   else
665   {
666     strandold = SeqLocStrand (old_feat_loc);
667   }
668           
669   /* only correct gene location if gene is on same strand as old feature
670    * location and contained in new feature location (on either strand).
671    */
672   if (SeqLocAinB (new_feat_loc, gene->location) <= 0
673       && ((strandold == Seq_strand_minus && strandgene == Seq_strand_minus)
674           || (strandold != Seq_strand_minus && strandgene != Seq_strand_minus))) 
675   {
676     bsp = GetBioseqGivenSeqLoc (gene->location, entityID);
677     if (bsp != NULL) {
678       hasNulls = LocationHasNullsBetween (gene->location);
679       if ((strandfeat == Seq_strand_minus && strandgene != Seq_strand_minus)
680           || (strandfeat != Seq_strand_minus && strandgene == Seq_strand_minus))
681       {
682         tmpslp = SeqLocCopy (gene->location);
683         SeqLocRevCmp (tmpslp);
684         slp = SeqLocMerge (bsp, tmpslp, new_feat_loc, TRUE, FALSE, hasNulls);
685         tmpslp = SeqLocFree (tmpslp);
686       }
687       else
688       {
689         slp = SeqLocMergeExEx (bsp, gene->location, new_feat_loc, TRUE, FALSE, TRUE, hasNulls, TRUE, TRUE);
690       }
691 
692       if (slp != NULL) {
693         CheckSeqLocForPartial (gene->location, &noLeftGene, &noRightGene);
694         gene->location = SeqLocFree (gene->location);
695         gene->location = slp;
696         CheckSeqLocForPartial (new_feat_loc, &noLeftFeat, &noRightFeat);
697         if (bsp->repr == Seq_repr_seg) {
698           slp = SegLocToPartsEx (bsp, gene->location, TRUE);
699           gene->location = SeqLocFree (gene->location);
700           gene->location = slp;
701           hasNulls = LocationHasNullsBetween (gene->location);
702           gene->partial = (gene->partial || hasNulls);
703         }
704         FreeAllFuzz (gene->location);
705         noLeft = (noLeftFeat || noLeftGene);
706         noRight = (noRightFeat || noRightGene);
707         SetSeqLocPartial (gene->location, noLeft, noRight);
708         gene->partial = (gene->partial || noLeft || noRight);
709       }
710     }
711   }
712 }
713 
714 static Boolean DlgStrandsMatch (Uint1 featstrand, Uint1 locstrand)
715 
716 {
717   if (featstrand == locstrand) return TRUE;
718   if (locstrand == Seq_strand_unknown && featstrand != Seq_strand_minus) return TRUE;
719   if (featstrand == Seq_strand_unknown && locstrand != Seq_strand_minus) return TRUE;
720   if (featstrand == Seq_strand_both && locstrand != Seq_strand_minus) return TRUE;
721   if (locstrand == Seq_strand_both) return TRUE;
722   return FALSE;
723 }
724 
725 static Boolean GeneUpdateFunc (GatherContextPtr gcp)
726 
727 {
728   GeneGatherPtr  ggp;
729   ObjMgrTypePtr  omtp;
730   SeqFeatPtr     sfp;
731   Uint1          strand1, strand2;
732   Char           thislabel [41];
733 
734   if (gcp == NULL) return TRUE;
735 
736   ggp = (GeneGatherPtr) gcp->userdata;
737   if (ggp == NULL ) return TRUE;
738 
739   thislabel [0] = '\0';
740 
741   if (gcp->thistype == OBJ_SEQFEAT) {
742     sfp = (SeqFeatPtr) gcp->thisitem;
743     if (sfp != NULL && sfp->data.choice == SEQFEAT_GENE) {
744       omtp = ObjMgrTypeFind (ggp->omp, gcp->thistype, NULL, NULL);
745       if (omtp == NULL) {
746         return TRUE;
747       }
748       if (omtp->labelfunc != NULL) {
749         (*(omtp->labelfunc)) (gcp->thisitem, thislabel, 40, OM_LABEL_CONTENT);
750       }
751       if (thislabel [0] != '\0') {
752         ggp->idx++;
753         if (ggp->idx == ggp->val) {
754           strand1 = SeqLocStrand (sfp->location);
755           strand2 = SeqLocStrand (ggp->slp);
756           if (DlgStrandsMatch (strand1, strand2)) {
757             UpdateGeneLocation (sfp, ggp->old_feature_location, ggp->slp, gcp->entityID);
758             return FALSE;
759           }
760         }
761       }
762     }
763   }
764   return TRUE;
765 }
766 
767 static Boolean GeneGatherFunc (GatherContextPtr gcp)
768 
769 {
770   FeatureFormPtr  ffp;
771   GeneGatherPtr   ggp;
772   GeneRefPtr      grp;
773   ObjMgrTypePtr   omtp;
774   SeqFeatPtr      sfp;
775   Char            thislabel [41];
776   ValNodePtr      vnp;
777 
778   if (gcp == NULL) return TRUE;
779 
780   ggp = (GeneGatherPtr) gcp->userdata;
781   if (ggp == NULL || ggp->ffp == NULL) return TRUE;
782 
783   thislabel [0] = '\0';
784 
785   if (gcp->thistype == OBJ_SEQFEAT) {
786     sfp = (SeqFeatPtr) gcp->thisitem;
787     if (sfp != NULL && sfp->data.choice == SEQFEAT_GENE) {
788       omtp = ObjMgrTypeFind (ggp->omp, gcp->thistype, NULL, NULL);
789       if (omtp == NULL) {
790         return TRUE;
791       }
792       if (omtp->labelfunc != NULL) {
793         (*(omtp->labelfunc)) (gcp->thisitem, thislabel, 40, OM_LABEL_CONTENT);
794       }
795       if (thislabel [0] != '\0') {
796         ffp = (FeatureFormPtr) ggp->ffp;
797         grp = (GeneRefPtr) sfp->data.value.ptrvalue;
798         if (grp != NULL 
799             && (grp->locus != NULL || grp->locus_tag != NULL || grp->desc != NULL))
800         {
801           vnp = ValNodeNew (ggp->lastgene);
802           if (ffp->geneNames == NULL) {
803             ffp->geneNames = vnp;
804           }
805           ggp->lastgene = vnp;
806           if (vnp != NULL) {
807             vnp->data.ptrvalue = StringSave (thislabel);
808             if (grp->locus != NULL) {
809               vnp->choice = 1;
810             } else if (grp->desc != NULL) {
811               vnp->choice = 2;
812             } else if (grp->locus_tag != NULL) {
813               vnp->choice = 3;
814             }
815           }
816         }
817       }
818     }
819   }
820 
821   return TRUE;
822 }
823 
824 extern void PopulateGenePopup (FeatureFormPtr ffp)
825 
826 {
827   Int2            count;
828   GeneGatherList  ggl;
829   GatherScope     gs;
830   CharPtr         str;
831   Boolean         usePopupForGene;
832   ValNodePtr      vnp;
833 
834   if (ffp != NULL && ffp->genePopup != NULL && ffp->geneList != NULL) {
835     ggl.ffp = ffp;
836     ggl.omp = ObjMgrGet ();
837     ggl.slp = NULL;
838     ggl.genexref = NULL;
839     ggl.xrefmatch = FALSE;
840     ggl.idx = 0;
841     ggl.val = 0;
842     ggl.min = 0;
843     ggl.lastgene = NULL;
844     MemSet ((Pointer) (&gs), 0, sizeof (GatherScope));
845     gs.seglevels = 1;
846     gs.get_feats_location = TRUE;
847     MemSet((Pointer)(gs.ignore), (int)(TRUE), (size_t)(OBJ_MAX * sizeof(Boolean)));
848     gs.ignore[OBJ_BIOSEQ] = FALSE;
849     gs.ignore[OBJ_BIOSEQ_SEG] = FALSE;
850     gs.ignore[OBJ_SEQFEAT] = FALSE;
851     gs.ignore[OBJ_SEQANNOT] = FALSE;
852     gs.scope = GetBestTopParentForItemID (ffp->input_entityID,
853                                           ffp->input_itemID,
854                                           ffp->input_itemtype);
855     GatherEntity (ffp->input_entityID, (Pointer) &ggl, GeneGatherFunc, &gs);
856     count = 0;
857     for (vnp = ffp->geneNames; vnp != NULL; vnp = vnp->next) {
858       count++;
859     }
860     if (count < 32) {
861       usePopupForGene = TRUE;
862       ffp->gene = ffp->genePopup;
863     } else {
864       usePopupForGene = FALSE;
865       ffp->gene = ffp->geneList;
866     }
867     for (vnp = ffp->geneNames; vnp != NULL; vnp = vnp->next) {
868       str = (CharPtr) vnp->data.ptrvalue;
869       if (StringHasNoText (str)) {
870         str = "??";
871       }
872       if (usePopupForGene) {
873         PopupItem (ffp->gene, str);
874       } else {
875         ListItem (ffp->gene, str);
876       }
877     }
878     Show (ffp->gene);
879   }
880 }
881 
882 static Boolean GeneMatchFunc (GatherContextPtr gcp)
883 
884 {
885   Int4            diff;
886   FeatureFormPtr  ffp;
887   GeneRefPtr      genexref;
888   GeneGatherPtr   ggp;
889   GeneRefPtr      grp;
890   ObjMgrTypePtr   omtp;
891   SeqFeatPtr      sfp;
892   Uint1           strand1, strand2;
893   Char            thislabel [41];
894 
895   if (gcp == NULL) return TRUE;
896 
897   ggp = (GeneGatherPtr) gcp->userdata;
898   if (ggp == NULL || ggp->ffp == NULL) return TRUE;
899 
900   thislabel [0] = '\0';
901 
902   if (gcp->thistype == OBJ_SEQFEAT) {
903     sfp = (SeqFeatPtr) gcp->thisitem;
904     if (sfp != NULL && sfp->data.choice == SEQFEAT_GENE && sfp->data.value.ptrvalue != NULL) {
905       omtp = ObjMgrTypeFind (ggp->omp, gcp->thistype, NULL, NULL);
906       if (omtp == NULL) {
907         return TRUE;
908       }
909       if (omtp->labelfunc != NULL) {
910         (*(omtp->labelfunc)) (gcp->thisitem, thislabel, 40, OM_LABEL_CONTENT);
911       }
912       if (thislabel [0] != '\0') {
913         ffp = (FeatureFormPtr) ggp->ffp;
914         ggp->idx++;
915         genexref = ggp->genexref;
916         if (genexref != NULL) {
917           grp = (GeneRefPtr) sfp->data.value.ptrvalue;
918           if (! StringHasNoText (genexref->locus)) {
919             if (StringICmp (genexref->locus, grp->locus) == 0) {
920               ggp->val = ggp->idx;
921               ggp->xrefmatch = TRUE;
922               if (ffp != NULL) {
923                 SetValue (ffp->useGeneXref, 2);
924               }
925             }
926           } else if (! StringHasNoText (genexref->locus_tag)) {
927             if (StringICmp (genexref->locus_tag, grp->locus_tag) == 0) {
928               ggp->val = ggp->idx;
929               ggp->xrefmatch = TRUE;
930               if (ffp != NULL) {
931                 SetValue (ffp->useGeneXref, 2);
932               }
933             }
934           } else if (! StringHasNoText (genexref->desc)) {
935             if (StringICmp (genexref->desc, grp->desc) == 0) {
936               ggp->val = ggp->idx;
937               ggp->xrefmatch = TRUE;
938               if (ffp != NULL) {
939                 SetValue (ffp->useGeneXref, 2);
940               }
941             }
942           }
943         }
944         diff = SeqLocAinB (ggp->slp, sfp->location);
945         if (diff >= 0) {
946           if (diff < ggp->min) {
947             strand1 = SeqLocStrand (ggp->slp);
948             strand2 = SeqLocStrand (sfp->location);
949             if (DlgStrandsMatch (strand1, strand2)) {
950               ggp->min = diff;
951               if (! ggp->xrefmatch) {
952                 ggp->val = ggp->idx;
953               }
954             }
955           }
956         }
957       }
958     }
959   }
960 
961   return TRUE;
962 }
963 
964 static void SaveGoTermsInSfp (UserObjectPtr uop, Pointer userdata)
965 
966 {
967   FeatureFormPtr  ffp;
968   ObjectIdPtr     oip;
969 
970   if (uop == NULL || userdata == NULL) return;
971   oip = uop->type;
972   if (oip == NULL) return;
973   if (StringCmp (oip->str, "GeneOntology") == 0) {
974     ffp = (FeatureFormPtr) userdata;
975     ffp->goTermUserObj = AsnIoMemCopy (uop, (AsnReadFunc) UserObjectAsnRead,
976                                        (AsnWriteFunc) UserObjectAsnWrite);
977   }
978 }
979 
980 static void FeatIDtoText (TexT t, ChoicePtr cp)
981 
982 {
983   Char         buf [32];
984   ObjectIdPtr  oip;
985 
986   if (t == NULL) return;
987   if (cp == NULL) {
988     SetTitle (t, "");
989     return;
990   }
991 
992   if (cp->choice == 3) {
993     oip = (ObjectIdPtr) cp->value.ptrvalue;
994     if (oip != NULL) {
995       if (StringDoesHaveText (oip->str)) {
996         SetTitle (t, oip->str);
997         return;
998       } else {
999         sprintf (buf, "%ld", (long) oip->id);
1000         SetTitle (t, buf);
1001         return;
1002       }
1003     }
1004   }
1005 
1006   SetTitle (t, "");
1007 }
1008 
1009 static void TextToFeatID (TexT t, ChoicePtr cp)
1010 
1011 {
1012   Boolean      all_digits = TRUE;
1013   Char         buf [128];
1014   Char         ch;
1015   ObjectIdPtr  oip;
1016   CharPtr      str;
1017   long int     val;
1018 
1019   if (t == NULL || cp == NULL) return;
1020 
1021   GetTitle (t, buf, sizeof (buf) - 1);
1022   if (StringHasNoText (buf)) {
1023     SeqFeatIdFree (cp);
1024     cp->choice = 0;
1025     return;
1026   }
1027 
1028   oip = ObjectIdNew ();
1029   if (oip == NULL) return;
1030 
1031   str = buf;
1032   ch = *str;
1033   while (ch != '\0') {
1034     if (! IS_DIGIT (ch)) {
1035       all_digits = FALSE;
1036     }
1037     str++;
1038     ch = *str;
1039   }
1040 
1041   if (all_digits && sscanf (buf, "%ld", &val) == 1) {
1042     oip->id = (Int4) val;
1043   } else {
1044     oip->str = StringSave (buf);
1045   }
1046   SeqFeatIdFree (cp);
1047   cp->choice = 3;
1048   cp->value.ptrvalue = (Pointer) oip;
1049 }
1050 
1051 static void FeatXreftoText (TexT t, SeqFeatPtr sfp)
1052 
1053 {
1054   Char            buf [32];
1055   ObjectIdPtr     oip;
1056   SeqFeatXrefPtr  xref;
1057 
1058   if (t == NULL) return;
1059   if (sfp == NULL) {
1060     SetTitle (t, "");
1061     return;
1062   }
1063 
1064   for (xref = sfp->xref; xref != NULL; xref = xref->next) {
1065     if (xref->id.choice != 3) continue;
1066     oip = (ObjectIdPtr) xref->id.value.ptrvalue;
1067     if (oip != NULL) {
1068       if (StringDoesHaveText (oip->str)) {
1069         SetTitle (t, oip->str);
1070         return;
1071       } else {
1072         sprintf (buf, "%ld", (long) oip->id);
1073         SetTitle (t, buf);
1074         return;
1075       }
1076     }
1077   }
1078 
1079   SetTitle (t, "");
1080 }
1081 
1082 static void TextToFeatXref (TexT t, SeqFeatPtr sfp)
1083 
1084 {
1085   Boolean         all_digits = TRUE;
1086   Char            buf [128];
1087   Char            ch;
1088   ObjectIdPtr     oip;
1089   CharPtr         str;
1090   long int        val;
1091   SeqFeatXrefPtr  xref;
1092 
1093   if (t == NULL || sfp == NULL) return;
1094 
1095   GetTitle (t, buf, sizeof (buf) - 1);
1096   if (StringHasNoText (buf)) {
1097     ClearFeatIDXrefs (sfp);
1098     return;
1099   }
1100 
1101   ClearFeatIDXrefs (sfp);
1102 
1103   oip = ObjectIdNew ();
1104   if (oip == NULL) return;
1105 
1106   str = buf;
1107   ch = *str;
1108   while (ch != '\0') {
1109     if (! IS_DIGIT (ch)) {
1110       all_digits = FALSE;
1111     }
1112     str++;
1113     ch = *str;
1114   }
1115 
1116   if (all_digits && sscanf (buf, "%ld", &val) == 1) {
1117     oip->id = (Int4) val;
1118   } else {
1119     oip->str = StringSave (buf);
1120   }
1121 
1122   xref = SeqFeatXrefNew ();
1123   if (xref != NULL) {
1124     xref->id.choice = 3;
1125     xref->id.value.ptrvalue = (Pointer) oip;
1126     xref->next = sfp->xref;
1127     sfp->xref = xref;
1128   }
1129 }
1130 
1131 static void GbqualsToVisStringDialog (SeqFeatPtr sfp, DialoG d, CharPtr qual)
1132 
1133 {
1134   GBQualPtr   gbq;
1135   ValNodePtr  head = NULL;
1136 
1137   if (sfp == NULL || StringHasNoText (qual)) {
1138     PointerToDialog (d, NULL);
1139     return;
1140   }
1141   for (gbq = sfp->qual; gbq != NULL; gbq = gbq->next) {
1142     if (StringICmp (gbq->qual, qual) != 0) continue;
1143     ValNodeCopyStr (&head, 0, gbq->val);
1144   }
1145   PointerToDialog (d, head);
1146   ValNodeFreeData (head);
1147 }
1148 
1149 extern void VisStringDialogToGbquals (SeqFeatPtr sfp, DialoG d, CharPtr qual);
1150 extern void VisStringDialogToGbquals (SeqFeatPtr sfp, DialoG d, CharPtr qual)
1151 
1152 {
1153   GBQualPtr   gbq, gbqlast = NULL;
1154   ValNodePtr  head = NULL, vnp;
1155   CharPtr     str;
1156 
1157   if (sfp == NULL || StringHasNoText (qual)) return;
1158   head = DialogToPointer (d);
1159   for (gbq = sfp->qual; gbq != NULL; gbq = gbq->next) {
1160     gbqlast = gbq;
1161   }
1162   for (vnp = head; vnp != NULL; vnp = vnp->next) {
1163     str = (CharPtr) vnp->data.ptrvalue;
1164     if (StringHasNoText (str)) continue;
1165     gbq = GBQualNew ();
1166     if (gbq == NULL) continue;
1167     gbq->qual = StringSave (qual);
1168     gbq->val = StringSave (str);
1169     if (gbqlast == NULL) {
1170       sfp->qual = gbq;
1171     } else {
1172       gbqlast->next = gbq;
1173     }
1174     gbqlast = gbq;
1175   }
1176   ValNodeFreeData (head);
1177 }
1178 
1179 extern void SeqFeatPtrToFieldPage (DialoG d, SeqFeatPtr sfp);
1180 
1181 extern void SeqFeatPtrToCommon (FeatureFormPtr ffp, SeqFeatPtr sfp)
1182 
1183 {
1184   GeneGatherList  ggl;
1185   GeneRefPtr      grp;
1186   GatherScope     gs;
1187   ProtRefPtr      prp;
1188   CharPtr         str;
1189   ValNodePtr      vnp;
1190   SeqFeatXrefPtr  xref;
1191   /*
1192   Char            ch;
1193   CharPtr         ptr;
1194   */
1195 
1196   if (ffp != NULL) {
1197     if (sfp != NULL) {
1198       str = StringSave (sfp->comment);
1199       /*
1200       ptr = str;
1201       if (ptr != NULL) {
1202         ch = *ptr;
1203         while (ch != '\0') {
1204           if (ch == '~') {
1205 #ifdef WIN_MAC
1206             *ptr = '\015';
1207 #else
1208             *ptr = '\n';
1209 #endif
1210           }
1211           ptr++;
1212           ch = *ptr;
1213         }
1214       }
1215       */
1216       SetTitle (ffp->comment, str);
1217       SetTitle (ffp->title, sfp->title);
1218       SetValue (ffp->evidence, sfp->exp_ev + 1);
1219       if (GetAppProperty ("InternalNcbiSequin") == NULL) {
1220         if (sfp->exp_ev == 0) {
1221           SafeDisable (ffp->evidence);
1222         }
1223       }
1224       SetStatus (ffp->partial, sfp->partial);
1225       SetStatus (ffp->exception, sfp->excpt);
1226       SetStatus (ffp->pseudo, sfp->pseudo);
1227       SetTitle (ffp->exceptText, sfp->except_text);
1228       SetValue (ffp->useGeneXref, 1);
1229       SetTitle (ffp->geneSymbol, "");
1230       SetTitle (ffp->geneAllele, "");
1231       SetTitle (ffp->geneDesc, "");
1232       SetTitle (ffp->locusTag, "");
1233       SetTitle (ffp->geneSynonym, "");
1234       ggl.ffp = ffp;
1235       ggl.omp = ObjMgrGet ();
1236       ggl.slp = sfp->location;
1237       ggl.genexref = NULL;
1238       grp = NULL;
1239       xref = sfp->xref;
1240       while (xref != NULL && xref->data.choice != SEQFEAT_PROT) {
1241         xref = xref->next;
1242       }
1243       if (xref != NULL) {
1244         prp = (ProtRefPtr) xref->data.value.ptrvalue;
1245         if (prp != NULL && ffp->protXrefName != NULL) {
1246           vnp = prp->name;
1247           if (vnp != NULL) {
1248             SetTitle (ffp->protXrefName, (CharPtr) vnp->data.ptrvalue);
1249           }
1250         }
1251         if (prp != NULL && StringDoesHaveText (prp->desc)) {
1252           SetTitle (ffp->protXrefDesc, prp->desc);
1253         }
1254       }
1255       xref = sfp->xref;
1256       while (xref != NULL && xref->data.choice != SEQFEAT_GENE) {
1257         xref = xref->next;
1258       }
1259       if (xref != NULL) {
1260         grp = (GeneRefPtr) xref->data.value.ptrvalue;
1261         ggl.genexref = grp;
1262       }
1263       ggl.xrefmatch = FALSE;
1264       ggl.idx = 2;
1265       ggl.val = 1;
1266       ggl.min = INT4_MAX;
1267       MemSet ((Pointer) (&gs), 0, sizeof (GatherScope));
1268       gs.seglevels = 1;
1269       gs.get_feats_location = TRUE;
1270       MemSet((Pointer)(gs.ignore), (int)(TRUE), (size_t)(OBJ_MAX * sizeof(Boolean)));
1271       gs.ignore[OBJ_BIOSEQ] = FALSE;
1272       gs.ignore[OBJ_BIOSEQ_SEG] = FALSE;
1273       gs.ignore[OBJ_SEQFEAT] = FALSE;
1274       gs.ignore[OBJ_SEQANNOT] = FALSE;
1275       gs.scope = GetBestTopParentForItemID (ffp->input_entityID,
1276                                             ffp->input_itemID,
1277                                             ffp->input_itemtype);
1278       GatherEntity (ffp->input_entityID, (Pointer) &ggl, GeneMatchFunc, &gs);
1279       if (grp != NULL && StringHasNoText (grp->locus) && StringHasNoText (grp->allele) &&
1280             StringHasNoText (grp->desc) && StringHasNoText (grp->maploc) &&
1281             grp->db == NULL && grp->syn == NULL && grp->locus_tag == NULL) {
1282         SetValue (ffp->gene, 1);
1283         SetValue (ffp->useGeneXref, 3);
1284         SetTitle (ffp->geneSymbol, grp->locus);
1285         SetTitle (ffp->geneAllele, grp->allele);
1286         SetTitle (ffp->geneDesc, grp->desc);
1287         SetTitle (ffp->locusTag, grp->locus_tag);
1288         if (grp->syn == NULL) {
1289           SetTitle (ffp->geneSynonym, "");
1290         } else {
1291           SetTitle (ffp->geneSynonym, grp->syn->data.ptrvalue);
1292         }
1293         SafeHide (ffp->editGeneBtn);
1294         SafeHide (ffp->newGeneGrp);
1295       } else if (ggl.val == 1 && grp != NULL && (! ggl.xrefmatch)) {
1296         SetValue (ffp->gene, 2);
1297         SetValue (ffp->useGeneXref, 2);
1298         SetTitle (ffp->geneSymbol, grp->locus);
1299         SetTitle (ffp->geneAllele, grp->allele);
1300         SetTitle (ffp->geneDesc, grp->desc);
1301         SetTitle (ffp->locusTag, grp->locus_tag);
1302         if (grp->syn == NULL) {
1303           SetTitle (ffp->geneSynonym, "");
1304         } else {
1305           SetTitle (ffp->geneSynonym, grp->syn->data.ptrvalue);
1306         }
1307         SafeHide (ffp->editGeneBtn);
1308         SafeShow (ffp->newGeneGrp);
1309       } else if (grp != NULL && (! ggl.xrefmatch)) {
1310         SetValue (ffp->gene, 2);
1311         SetValue (ffp->useGeneXref, 2);
1312         SetTitle (ffp->geneSymbol, grp->locus);
1313         SetTitle (ffp->geneAllele, grp->allele);
1314         SetTitle (ffp->geneDesc, grp->desc);
1315         SetTitle (ffp->locusTag, grp->locus_tag);
1316         if (grp->syn == NULL) {
1317           SetTitle (ffp->geneSynonym, "");
1318         } else {
1319           SetTitle (ffp->geneSynonym, grp->syn->data.ptrvalue);
1320         }
1321         SafeHide (ffp->editGeneBtn);
1322         SafeShow (ffp->newGeneGrp);
1323       } else if (ggl.val > 2) {
1324         SetValue (ffp->gene, ggl.val);
1325         SafeShow (ffp->editGeneBtn);
1326         if (ffp->gene == ffp->geneList) {
1327           SetOffset (ffp->gene, 0, (Int2) (ggl.val - 1));
1328         }
1329         if (grp != NULL && (! ggl.xrefmatch)) {
1330           SetValue (ffp->useGeneXref, 2);
1331         }
1332       } else {
1333         SetValue (ffp->gene, ggl.val);
1334       }
1335       PointerToDialog (ffp->featcits, sfp->cit);
1336       PointerToDialog (ffp->dbxrefs, sfp->dbxref);
1337       PointerToDialog (ffp->gbquals, sfp->qual);
1338       SeqFeatPtrToFieldPage (ffp->gbquals, sfp);
1339       GbqualsToVisStringDialog (sfp, ffp->experiment, "experiment");
1340       GBQualsToInferenceDialog (ffp->inference, sfp);
1341       PointerToDialog (ffp->usrobjext, sfp->ext);
1342       VisitUserObjectsInUop (sfp->ext, (Pointer) ffp, SaveGoTermsInSfp);
1343       FeatIDtoText (ffp->featid, &(sfp->id));
1344       FeatXreftoText (ffp->fidxref, sfp);
1345     } else {
1346       SetTitle (ffp->comment, "");
1347       SetValue (ffp->evidence, 1);
1348       if (GetAppProperty ("InternalNcbiSequin") == NULL) {
1349         SafeDisable (ffp->evidence);
1350       }
1351       SetStatus (ffp->partial, FALSE);
1352       SetStatus (ffp->exception, FALSE);
1353       SetStatus (ffp->pseudo, FALSE);
1354       SetTitle (ffp->exceptText, "");
1355       SetValue (ffp->gene, 1);
1356       SafeHide (ffp->newGeneGrp);
1357       SafeHide (ffp->editGeneBtn);      
1358       SetValue (ffp->useGeneXref, 1);
1359       SetTitle (ffp->geneSymbol, "");
1360       SetTitle (ffp->geneAllele, "");
1361       SetTitle (ffp->geneDesc, "");
1362       SetTitle (ffp->locusTag, "");
1363       SetTitle (ffp->geneSynonym, "");
1364       PointerToDialog (ffp->featcits, NULL);
1365       PointerToDialog (ffp->dbxrefs, NULL);
1366       PointerToDialog (ffp->gbquals, NULL);
1367       PointerToDialog (ffp->experiment, NULL);
1368       GBQualsToInferenceDialog (ffp->inference, NULL);
1369       PointerToDialog (ffp->usrobjext, NULL);
1370       ffp->goTermUserObj = NULL;
1371       FeatIDtoText (ffp->featid, NULL);
1372       FeatXreftoText (ffp->fidxref, NULL);
1373     }
1374   }
1375 }
1376 
1377 extern void CleanupEvidenceGBQuals (GBQualPtr PNTR prevgbq)
1378 
1379 {
1380   GBQualPtr  gbq;
1381   GBQualPtr  next;
1382   Boolean    unlink;
1383 
1384   if (prevgbq == NULL) return;
1385   gbq = *prevgbq;
1386   while (gbq != NULL) {
1387     next = gbq->next;
1388     unlink = FALSE;
1389     if (StringICmp (gbq->qual, "experiment") == 0 || StringICmp (gbq->qual, "inference") == 0) {
1390       unlink = TRUE;
1391     }
1392     if (unlink) {
1393       *prevgbq = gbq->next;
1394       gbq->next = NULL;
1395       GBQualFree (gbq);
1396     } else {
1397       prevgbq = (GBQualPtr PNTR) &(gbq->next);
1398     }
1399     gbq = next;
1400   }
1401 }
1402 
1403 static UserObjectPtr CombineGOTermUserObjects (UserObjectPtr origuop, UserObjectPtr newuop)
1404 
1405 {
1406   ObjectIdPtr  oip;
1407 
1408   if (newuop == NULL) return origuop;
1409   if (origuop == NULL) return newuop;
1410 
1411   oip = origuop->type;
1412   if (oip != NULL && StringCmp (oip->str, "GeneOntology") == 0) return origuop;
1413 
1414   return CombineUserObjects (origuop, newuop);
1415 }
1416 
1417 typedef struct replacesdata {
1418   FeatureFormPtr  ffp;
1419   SeqFeatPtr      sfp;
1420 } ReplaceData, PNTR ReplaceDataPtr;
1421 
1422 static Boolean ReplaceFeatureExtras (GatherContextPtr gcp)
1423 
1424 {
1425   SeqFeatXrefPtr  curr;
1426   FeatureFormPtr  ffp;
1427   SeqFeatXrefPtr  PNTR last;
1428   SeqFeatXrefPtr  next;
1429   SeqFeatPtr      old;
1430   ReplaceDataPtr  rdp;
1431   SeqFeatPtr      sfp;
1432 
1433   rdp = (ReplaceDataPtr) gcp->userdata;
1434   if (rdp != NULL && rdp->sfp != NULL && rdp->ffp != NULL) {
1435     sfp = rdp->sfp;
1436     ffp = rdp->ffp;
1437     old = gcp->thisitem;
1438     if (old != NULL) {
1439       if (ffp->gbquals != NULL) {
1440         sfp->qual = DialogToPointer (ffp->gbquals);
1441       } else if (sfp->qual == NULL) {
1442         sfp->qual = old->qual;
1443         old->qual = NULL;
1444       }
1445       CleanupEvidenceGBQuals (&(sfp->qual));
1446       VisStringDialogToGbquals (sfp, ffp->experiment, "experiment");
1447       InferenceDialogToGBQuals (ffp->inference, sfp, TRUE);
1448       if (ffp->usrobjext != NULL) {
1449         sfp->ext = DialogToPointer (ffp->usrobjext);
1450       } else if (sfp->ext == NULL) {
1451         sfp->ext = old->ext;
1452         old->ext = NULL;
1453       }
1454       if (ffp->goTermUserObj != NULL) {
1455         sfp->ext = CombineGOTermUserObjects (sfp->ext, ffp->goTermUserObj);
1456       }
1457       if (ffp->featid != NULL) {
1458         TextToFeatID (ffp->featid, &(sfp->id));
1459       }
1460       /*
1461       if (sfp->cit == NULL) {
1462         sfp->cit = old->cit;
1463         old->cit = NULL;
1464       }
1465       */
1466       if (old->xref != NULL) {
1467         last = (SeqFeatXrefPtr PNTR) &(old->xref);
1468         curr = old->xref;
1469         while (curr != NULL) {
1470           next = curr->next;
1471           if (curr->data.choice == SEQFEAT_GENE) {
1472             *last = next;
1473             curr->next = NULL;
1474             SeqFeatXrefFree (curr);
1475           } else {
1476             last = &(curr->next);
1477           }
1478           curr = next;
1479         }
1480       }
1481       if (sfp->xref == NULL) {
1482         sfp->xref = old->xref;
1483       } else {
1484         curr = sfp->xref;
1485         while (curr->next != NULL) {
1486           curr = curr->next;
1487         }
1488         if (curr != NULL) {
1489           curr->next = old->xref;
1490         }
1491       }
1492       old->xref = NULL;
1493     }
1494   }
1495   return TRUE;
1496 }
1497 
1498 static Boolean GetOldFeatureLocation (GatherContextPtr gcp)
1499 {
1500   SeqLocPtr PNTR  old_loc;
1501   SeqFeatPtr      old_feat;
1502 
1503   if (gcp == NULL || gcp->userdata == NULL)
1504   {
1505     return FALSE;
1506   }
1507   old_loc = (SeqLocPtr PNTR) gcp->userdata;
1508   if (*old_loc != NULL)
1509   {
1510     return TRUE;
1511   }
1512   old_feat = gcp->thisitem;
1513   if (old_feat != NULL)
1514   {
1515     *old_loc = (SeqLocPtr) AsnIoMemCopy (old_feat->location,
1516                                          (AsnReadFunc) SeqLocAsnRead,
1517                                          (AsnWriteFunc) SeqLocAsnWrite);
1518   }
1519   return TRUE;
1520 }
1521 
1522 static Boolean HasExceptionGBQual (SeqFeatPtr sfp)
1523 
1524 {
1525   GBQualPtr  qual;
1526   Boolean    rsult;
1527 
1528   rsult = FALSE;
1529   if (sfp != NULL) {
1530     qual = sfp->qual;
1531     while (qual != NULL) {
1532       if (StringICmp (qual->qual, "exception") == 0) {
1533         if (! StringHasNoText (qual->val)) {
1534           rsult = TRUE;
1535         }
1536       }
1537       qual = qual->next;
1538     }
1539   }
1540   return rsult;
1541 }
1542 
1543 static void AddProtRefXref (SeqFeatPtr sfp, TexT protXrefName, TexT protXrefDesc)
1544 
1545 {
1546   Char            desc [256];
1547   Char            name [256];
1548   ProtRefPtr      prp;
1549   SeqFeatXrefPtr  xref;
1550 
1551   if (sfp == NULL) return;
1552   if (protXrefName == NULL && protXrefDesc == NULL) return;
1553   GetTitle (protXrefName, name, sizeof (name) - 1);
1554   GetTitle (protXrefDesc, desc, sizeof (desc) - 1);
1555   if (StringHasNoText (name) && StringHasNoText (desc)) return;
1556   for (xref = sfp->xref; xref != NULL; xref = xref->next) {
1557     if (xref->data.choice == SEQFEAT_PROT) break;
1558   }
1559   if (xref == NULL) {
1560     xref = SeqFeatXrefNew ();
1561     if (xref != NULL) {
1562       prp = ProtRefNew ();
1563       xref->data.choice = SEQFEAT_PROT;
1564       xref->data.value.ptrvalue = (Pointer) prp;
1565       xref->next = sfp->xref;
1566       sfp->xref = xref;
1567     }
1568   }
1569   if (xref != NULL && xref->data.choice == SEQFEAT_PROT) {
1570     prp = (ProtRefPtr) xref->data.value.ptrvalue;
1571     xref->data.value.ptrvalue = ProtRefFree (prp);
1572     prp = CreateNewProtRef (name, desc, NULL, NULL);
1573     xref->data.value.ptrvalue = (Pointer) prp;
1574   }
1575 }
1576 
1577 static Boolean FeatIsPseudo (SeqFeatPtr sfp)
1578 {
1579   Boolean   pseudo = FALSE;
1580   GBQualPtr qual;
1581   
1582   if (sfp != NULL)
1583   {
1584     if (sfp->pseudo)
1585     {
1586       pseudo = TRUE;
1587     }
1588     else
1589     {
1590       qual = sfp->qual;
1591       while (qual != NULL)
1592       {
1593         if (StringICmp (qual->qual, "pseudo") == 0) 
1594         {
1595           pseudo = TRUE;
1596         }
1597         qual = qual->next;
1598       }
1599     }
1600   }
1601   return pseudo;
1602 }
1603 
1604 static void RemoveProtXrefs (SeqFeatPtr sfp)
1605 {
1606   SeqFeatXrefPtr  xref_prev = NULL, xref_next, xref;
1607 
1608   if (sfp == NULL || sfp->xref == NULL) return;
1609 
1610   for (xref = sfp->xref; xref != NULL; xref = xref_next)
1611   {
1612     xref_next = xref->next;
1613     if (xref->data.choice == SEQFEAT_PROT)
1614     {
1615       if (xref_prev == NULL)
1616       {
1617         sfp->xref = xref->next;
1618       }
1619       else
1620       {
1621         xref_prev->next = xref->next;
1622       }
1623       xref->next = NULL;
1624       xref->data.value.ptrvalue = ProtRefFree (xref->data.value.ptrvalue);
1625       SeqFeatXrefFree (xref);
1626     }
1627     else
1628     {
1629       xref_prev = xref;
1630     }
1631   }  
1632 }
1633 
1634 static CharPtr infDetails [] = {
1635   "unknown error",
1636   "empty inference string",
1637   "bad inference prefix",
1638   "bad inference body",
1639   "single inference field",
1640   "spaces in inference",
1641   "same species misused",
1642   "bad inference accession",
1643   "accession is missing version",
1644   "accession.version not public",
1645   NULL
1646 };
1647 
1648 extern Boolean TestInference (FeatureFormPtr ffp, CharPtr badInfQual, size_t len, CharPtr badInfMssg)
1649 
1650 {
1651   GBQualPtr   gbq;
1652   Int2        inferenceCode;
1653   Boolean     rsult = TRUE;
1654   CharPtr     str;
1655   SeqFeatPtr  tmp;
1656 
1657   if (ffp == NULL) return FALSE;
1658   tmp = SeqFeatNew ();
1659   if (tmp == NULL) return FALSE;
1660   if (badInfQual != NULL) {
1661     *badInfQual = '\0';
1662   }
1663   if (badInfMssg != NULL) {
1664     *badInfMssg = '\0';
1665   }
1666   InferenceDialogToGBQuals (ffp->inference, tmp, FALSE);
1667   for (gbq = tmp->qual; gbq != NULL; gbq = gbq->next) {
1668     str = gbq->val;
1669     if (StringICmp (gbq->qual, "inference") != 0) continue;
1670     inferenceCode = ValidateInferenceQualifier (str, FALSE);
1671     if (inferenceCode != VALID_INFERENCE) {
1672         if (inferenceCode < VALID_INFERENCE || inferenceCode > ACC_VERSION_NOT_PUBLIC) {
1673           inferenceCode = VALID_INFERENCE;
1674         }
1675         if (badInfQual != NULL) {
1676         StringNCpy_0 (badInfQual, str, len);
1677         }
1678         if (badInfMssg != NULL) {
1679         StringCpy (badInfMssg, infDetails [(int) inferenceCode]);
1680         }
1681       rsult = FALSE;
1682       break;
1683     }
1684   }
1685   SeqFeatFree (tmp);
1686   return rsult;
1687 }
1688 
1689 static CharPtr infWarning1 =
1690 "Bad inference qualifier!  You must conform to international nucleotide sequence database\nconventions for inference qualifiers!";
1691 
1692 static CharPtr infWarning2 =
1693 "Still has bad inference!  You must conform to international nucleotide sequence database\nconventions for inference qualifiers!";
1694 
1695 static CharPtr infAccept =
1696 "Do you want to accept changes with bad inference data?  The bad qualifier will be converted to a note!";
1697 
1698 extern Boolean FeatFormReplaceWithoutUpdateProc (ForM f)
1699 
1700 {
1701   Char            allele [128];
1702   MsgAnswer       ans;
1703   Int2            attempts = 3;
1704   Char            badInfMssg [32];
1705   Char            badInfQual [256];
1706   BioseqPtr       bsp;
1707   Char            desc [128];
1708   Int2            expev;
1709   SeqMgrFeatContext  fcontext;
1710   FeatureFormPtr  ffp;
1711   SeqFeatPtr      gene;
1712   GeneGatherList  ggl;
1713   GeneRefPtr      grp;
1714   GeneRefPtr      grpfeat;
1715   GatherScope     gs;
1716   SeqLocPtr       gslp;
1717   Boolean         hasNulls;
1718   Int2            i;
1719   Int4Ptr         intptr;
1720   Uint4           itemID;
1721   Char            locustag [128];
1722   Boolean         noLeft;
1723   Boolean         noRight;
1724   SeqEntryPtr     oldscope;
1725   OMProcControl   ompc;
1726   ReplaceData     rd;
1727   Boolean         rsult;
1728   SeqAnnotPtr     sap;
1729   SeqEntryPtr     sep;
1730   SeqFeatPtr      sfp;
1731   SeqLocPtr       slp;
1732   CharPtr         str;
1733   Char            symbol [128];
1734   Int2            usexref;
1735   Int2            val;
1736   ValNodePtr      vnp, err_list;
1737   SeqFeatXrefPtr  xref;
1738   SeqLocPtr       old_location = NULL; /* we need the old location of the feature 
1739                                         * if we're going to do a gene update    
1740                                         */
1741   Boolean         fix_interval_order;
1742 
1743   rsult = FALSE;
1744   ffp = (FeatureFormPtr) GetObjectExtra (f);
1745   if (ffp != NULL) {
1746     MemSet ((Pointer) &ompc, 0, sizeof (OMProcControl));
1747     ompc.input_entityID = ffp->input_entityID;
1748     ompc.input_itemID = ffp->input_itemID;
1749     ompc.input_itemtype = ffp->input_itemtype;
1750     ompc.output_itemtype = ffp->input_itemtype;
1751     sfp = SeqFeatNew ();
1752     if (sfp != NULL) {
1753       sep = GetTopSeqEntryForEntityID (ffp->input_entityID);
1754       oldscope = SeqEntrySetScope (sep);
1755       sfp->data.choice = FindFeatFromFeatDefType (ffp->this_subtype);
1756       switch (sfp->data.choice) {
1757         case SEQFEAT_BOND :
1758         case SEQFEAT_SITE :
1759         case SEQFEAT_PSEC_STR :
1760           intptr = (Int4Ptr) DialogToPointer (ffp->data);
1761           if (intptr != NULL) {
1762             sfp->data.value.intvalue = *intptr;
1763           }
1764           break;
1765         case SEQFEAT_COMMENT:
1766           sfp->data.value.ptrvalue = NULL;
1767           break;
1768         default :
1769           sfp->data.value.ptrvalue = DialogToPointer (ffp->data);
1770           break;
1771       }
1772       sfp->comment = SaveStringFromText (ffp->comment);
1773       NewlinesToTildes (sfp->comment);
1774       expev = GetValue (ffp->evidence);
1775       if (expev > 0 && expev <= 3) {
1776         sfp->exp_ev = expev - 1;
1777       } else {
1778         sfp->exp_ev = 0;
1779       }
1780       sfp->partial = GetStatus (ffp->partial);
1781       sfp->excpt = GetStatus (ffp->exception);
1782       sfp->pseudo = GetStatus (ffp->pseudo);
1783       sfp->except_text = SaveStringFromText (ffp->exceptText);
1784       sfp->title = NULL;
1785       sfp->product = DialogToPointer (ffp->product);
1786       sfp->location = DialogToPointer (ffp->location);
1787       if (sfp->location == NULL) {
1788         SeqEntrySetScope (oldscope);
1789         ErrPostEx (SEV_ERROR, 0, 0, "Feature must have a location!");
1790         err_list = TestDialog (ffp->location);
1791         DisplayErrorMessages ("Location Errors", err_list);
1792         err_list = ValNodeFree (err_list);    
1793         return FALSE;
1794       }
1795       if ((! ffp->acceptBadInf) && (! TestInference (ffp, badInfQual, sizeof (badInfQual), badInfMssg))) {
1796         (ffp->badInfAttempts)++;
1797         if (GetAppProperty ("InternalNcbiSequin") != NULL) {
1798           attempts = 2;
1799         }
1800         if (ffp->badInfAttempts < attempts) {
1801           if (ffp->badInfAttempts == 2) {
1802             Message (MSG_OK, "%s - Please fix the %s error in\n%s", infWarning2, badInfMssg, badInfQual);
1803           } else {
1804             Message (MSG_OK, "%s - Please fix the %s error in\n%s", infWarning1, badInfMssg, badInfQual);
1805           }
1806           return FALSE;
1807         } else {
1808           if (Message (MSG_YN, "%s", infAccept) == ANS_NO) return FALSE;
1809           ffp->acceptBadInf = TRUE;
1810         }
1811       }
1812       bsp = GetBioseqGivenSeqLoc (sfp->location, ffp->input_entityID);
1813       if (bsp != NULL) {
1814         if (SeqLocBadSortOrder (bsp, sfp->location)) {
1815           fix_interval_order = FALSE;
1816           if (StringICmp (sfp->except_text, "trans-splicing") == 0)
1817           {
1818             ans = Message (MSG_YN, "Your feature intervals are out of order, but this coding region has a trans-splicing exception. Continue without correcting interval order?");
1819             if (ans == ANS_NO)
1820             {
1821               fix_interval_order = TRUE;
1822             }
1823           }
1824           else
1825           {
1826             ans = Message (MSG_YN,
1827             "Feature location intervals are out of order.  Do you want them repaired?");
1828             if (ans == ANS_YES)
1829             {
1830               fix_interval_order = TRUE;
1831             }
1832           }
1833           if (fix_interval_order) {
1834             hasNulls = LocationHasNullsBetween (sfp->location);
1835             gslp = SeqLocMerge (bsp, sfp->location, NULL, FALSE, FALSE, hasNulls);
1836             if (gslp != NULL) {
1837               CheckSeqLocForPartial (sfp->location, &noLeft, &noRight);
1838               sfp->location = SeqLocFree (sfp->location);
1839               sfp->location = gslp;
1840               if (bsp->repr == Seq_repr_seg) {
1841                 gslp = SegLocToParts (bsp, sfp->location);
1842                 sfp->location = SeqLocFree (sfp->location);
1843                 sfp->location = gslp;
1844               }
1845               FreeAllFuzz (sfp->location);
1846               SetSeqLocPartial (sfp->location, noLeft, noRight);
1847             }
1848           }
1849         }
1850       }
1851       if (CheckSeqLocForPartial (sfp->location, NULL, NULL)) {
1852         sfp->partial = TRUE;
1853       }
1854       sfp->cit = DialogToPointer (ffp->featcits);
1855       sfp->dbxref = DialogToPointer (ffp->dbxrefs);
1856       slp = AsnIoMemCopy (sfp->location, (AsnReadFunc) SeqLocAsnRead,
1857                           (AsnWriteFunc) SeqLocAsnWrite);
1858       usexref = GetValue (ffp->useGeneXref);
1859       if (ffp->gene != NULL) {
1860         val = GetValue (ffp->gene);
1861         if (usexref == 3 || (val > 1 && usexref == 2)) {
1862           grp = NULL;
1863           if (usexref == 3) {
1864             grp = GeneRefNew ();
1865           } else if (val == 2) {
1866             GetTitle (ffp->geneSymbol, symbol, sizeof (symbol));
1867             GetTitle (ffp->geneAllele, allele, sizeof (allele));
1868             GetTitle (ffp->geneDesc, desc, sizeof (desc));
1869             GetTitle (ffp->locusTag, locustag, sizeof (locustag));
1870             grp = CreateNewGeneRef (symbol, allele, desc, FALSE);
1871             if (! StringHasNoText (locustag)) {
1872               if (grp == NULL) {
1873                 grp = GeneRefNew ();
1874               }
1875               grp->locus_tag = StringSave (locustag);
1876             }
1877             if (!TextHasNoText (ffp->geneSynonym)) {
1878               if (grp == NULL) {
1879                 grp = GeneRefNew ();
1880               }
1881               ValNodeAddPointer (&(grp->syn), 0, SaveStringFromText (ffp->geneSynonym));
1882             }
1883           } else {
1884             vnp = ffp->geneNames;
1885             i = val - 3;
1886             while (i > 0 && vnp != NULL) {
1887               vnp = vnp->next;
1888               i--;
1889             }
1890             if (vnp != NULL) {
1891               if (vnp->choice == 1) {
1892                 str = (CharPtr) vnp->data.ptrvalue;
1893                 if (StringDoesHaveText (str)) {
1894                   grp = CreateNewGeneRef (str, NULL, NULL, FALSE);
1895                   gene = SeqMgrGetFeatureByLabel (bsp, str, SEQFEAT_GENE, 0, &fcontext);
1896                   if (gene != NULL && gene->data.choice == SEQFEAT_GENE) {
1897                     grpfeat = (GeneRefPtr) gene->data.value.ptrvalue;
1898                     if (grpfeat != NULL) {
1899                       grp->locus_tag = StringSaveNoNull (grpfeat->locus_tag);
1900                     }
1901                   }
1902                 }
1903               } else if (vnp->choice == 2) {
1904                  grp = GeneRefNew ();
1905                 if (grp != NULL) {
1906                   grp->desc = StringSave ((CharPtr) vnp->data.ptrvalue);
1907                 }
1908              } else if (vnp->choice == 3) {
1909                 grp = GeneRefNew ();
1910                 if (grp != NULL) {
1911                   grp->locus_tag = StringSave ((CharPtr) vnp->data.ptrvalue);
1912                 }
1913               }
1914             }
1915           }
1916           if (grp != NULL) {
1917             xref = SeqFeatXrefNew ();
1918             sfp->xref = xref;
1919             if (xref != NULL) {
1920               xref->data.choice = SEQFEAT_GENE;
1921               xref->data.value.ptrvalue = (Pointer) grp;
1922             }
1923           }
1924         }
1925       } else if (usexref == 3) {
1926         /* protein feature can now suppress gene on GenBank view */
1927         grp = GeneRefNew ();
1928         if (grp != NULL) {
1929           xref = SeqFeatXrefNew ();
1930           sfp->xref = xref;
1931           if (xref != NULL) {
1932             xref->data.choice = SEQFEAT_GENE;
1933             xref->data.value.ptrvalue = (Pointer) grp;
1934           }
1935         }
1936       }
1937 
1938       ompc.output_data = (Pointer) sfp;
1939       if (ompc.input_entityID == 0) {
1940         sfp->qual = DialogToPointer (ffp->gbquals);
1941         VisStringDialogToGbquals (sfp, ffp->experiment, "experiment");
1942         InferenceDialogToGBQuals (ffp->inference, sfp, TRUE);
1943 
1944         sfp->ext = DialogToPointer (ffp->usrobjext);
1945         if (ffp->goTermUserObj != NULL) {
1946           sfp->ext = CombineGOTermUserObjects (sfp->ext, ffp->goTermUserObj);
1947         }
1948         if (ffp->featid != NULL) {
1949           TextToFeatID (ffp->featid, &(sfp->id));
1950         }
1951         if (HasExceptionGBQual (sfp)) {
1952           sfp->excpt = TRUE;
1953         }
1954         if (ffp->fidxref != NULL) {
1955           TextToFeatXref (ffp->fidxref, sfp);
1956         }
1957         AddProtRefXref (sfp, ffp->protXrefName, ffp->protXrefDesc);
1958         FixSpecialCharactersForObject (OBJ_SEQFEAT, sfp, "You may not include special characters in the text.\nIf you do not choose replacement characters, these special characters will be replaced with '#'.", TRUE, NULL);
1959         if (! ObjMgrRegister (OBJ_SEQFEAT, (Pointer) sfp)) {
1960           Message (MSG_ERROR, "ObjMgrRegister failed");
1961         }
1962         SeqLocFree (slp);
1963         SeqEntrySetScope (oldscope);
1964         return TRUE;
1965       } else if (ompc.input_itemtype != OBJ_SEQFEAT) {
1966         sfp->qual = DialogToPointer (ffp->gbquals);
1967         VisStringDialogToGbquals (sfp, ffp->experiment, "experiment");
1968         InferenceDialogToGBQuals (ffp->inference, sfp, TRUE);
1969 
1970         sfp->ext = DialogToPointer (ffp->usrobjext);
1971         if (ffp->goTermUserObj != NULL) {
1972           sfp->ext = CombineGOTermUserObjects (sfp->ext, ffp->goTermUserObj);
1973         }
1974         if (ffp->featid != NULL) {
1975           TextToFeatID (ffp->featid, &(sfp->id));
1976         }
1977         if (HasExceptionGBQual (sfp)) {
1978           sfp->excpt = TRUE;
1979         }
1980         if (ffp->fidxref != NULL) {
1981           TextToFeatXref (ffp->fidxref, sfp);
1982         }
1983         AddProtRefXref (sfp, ffp->protXrefName, ffp->protXrefDesc);
1984         FixSpecialCharactersForObject (OBJ_SEQFEAT, sfp, "You may not include special characters in the text.\nIf you do not choose replacement characters, these special characters will be replaced with '#'.", TRUE, NULL);
1985         ompc.output_itemtype = OBJ_SEQFEAT;
1986         if (ompc.input_itemtype == OBJ_BIOSEQ) {
1987           bsp = GetBioseqGivenIDs (ompc.input_entityID, ompc.input_itemID, ompc.input_itemtype);
1988           if (bsp != NULL) {
1989             sap = bsp->annot;
1990             while (sap != NULL && (sap->name != NULL || sap->desc != NULL || sap->type != 1)) {
1991               sap = sap->next;
1992             }
1993             if (sap == NULL) {
1994               sap = SeqAnnotNew ();
1995               if (sap != NULL) {
1996                 sap->type = 1;
1997                 sap->next = bsp->annot;
1998                 bsp->annot = sap;
1999               }
2000             }
2001             if (sap != NULL) {
2002               itemID = GetItemIDGivenPointer (ompc.input_entityID, OBJ_SEQANNOT, (Pointer) sap);
2003               if (itemID > 0) {
2004                 ompc.input_itemID = itemID;
2005                 ompc.input_itemtype = OBJ_SEQANNOT;
2006               }
2007             }
2008           }
2009         }
2010         if (! AttachDataForProc (&ompc, FALSE)) {
2011           Message (MSG_ERROR, "AttachDataForProc failed");
2012         }
2013         rsult = TRUE;
2014       } else {
2015         GatherItem (ompc.input_entityID, ompc.input_itemID, ompc.input_itemtype,
2016                     (Pointer) &old_location, GetOldFeatureLocation);
2017       
2018         rd.ffp = ffp;
2019         rd.sfp = sfp;
2020         GatherItem (ompc.input_entityID, ompc.input_itemID, ompc.input_itemtype,
2021                     (Pointer) &rd, ReplaceFeatureExtras);
2022         if (HasExceptionGBQual (sfp)) {
2023           sfp->excpt = TRUE;
2024         }
2025         if (ffp->fidxref != NULL) {
2026           TextToFeatXref (ffp->fidxref, sfp);
2027         }
2028         if (FeatIsPseudo (sfp))
2029         {
2030           RemoveProtXrefs (sfp);
2031         }
2032         else
2033         {
2034           AddProtRefXref (sfp, ffp->protXrefName, ffp->protXrefDesc);
2035         }
2036         FixSpecialCharactersForObject (OBJ_SEQFEAT, sfp, "You may not include special characters in the text.\nIf you do not choose replacement characters, these special characters will be replaced with '#'.", TRUE, NULL);
2037         if (! ReplaceDataForProc (&ompc, FALSE)) {
2038           Message (MSG_ERROR, "ReplaceDataForProc failed");
2039         }
2040         rsult = TRUE;
2041       }
2042       if (ffp->gene != NULL && usexref == 1) {
2043         val = GetValue (ffp->gene);
2044         if (val == 2) {
2045           sep = GetBestTopParentForItemID (ffp->input_entityID,
2046                                            ffp->input_itemID,
2047                                            ffp->input_itemtype);
2048           /*
2049           sep = GetTopSeqEntryForEntityID (ffp->input_entityID);
2050           */
2051           if (sep != NULL) {
2052             sep = FindNucSeqEntry (sep);
2053           }
2054           if (sep != NULL && sep->data.ptrvalue != NULL) {
2055             GetTitle (ffp->geneSymbol, symbol, sizeof (symbol));
2056             GetTitle (ffp->geneAllele, allele, sizeof (allele));
2057             GetTitle (ffp->geneDesc, desc, sizeof (desc));
2058             GetTitle (ffp->locusTag, locustag, sizeof (locustag));
2059             grp = CreateNewGeneRef (symbol, allele, desc, FALSE);
2060             if (! StringHasNoText (locustag)) {
2061               if (grp == NULL) {
2062                 grp = GeneRefNew ();
2063               }
2064               grp->locus_tag = StringSave (locustag);
2065             }
2066             if (!TextHasNoText (ffp->geneSynonym)) {
2067               if (grp == NULL) {
2068                 grp = GeneRefNew ();
2069               }
2070               ValNodeAddPointer (&(grp->syn), 0, SaveStringFromText (ffp->geneSynonym));
2071             }
2072             if (grp != NULL) {
2073               sfp = CreateNewFeature (sep, NULL, SEQFEAT_GENE, NULL);
2074               if (sfp != NULL) {
2075                 sfp->data.value.ptrvalue = (Pointer) grp;
2076                 FixSpecialCharactersForObject (OBJ_SEQFEAT, sfp, "You may not include special characters in the text.\nIf you do not choose replacement characters, these special characters will be replaced with '#'.", TRUE, NULL);
2077                 sfp->location = SeqLocFree (sfp->location);
2078                 sfp->location = DialogToPointer (ffp->location);
2079                 bsp = GetBioseqGivenSeqLoc (sfp->location, ffp->input_entityID);
2080                 if (bsp != NULL) {
2081                   hasNulls = LocationHasNullsBetween (sfp->location);
2082                   gslp = SeqLocMerge (bsp, sfp->location, NULL, TRUE, FALSE, FALSE);
2083                   if (gslp != NULL) {
2084                     CheckSeqLocForPartial (sfp->location, &noLeft, &noRight);
2085                     sfp->location = SeqLocFree (sfp->location);
2086                     sfp->location = gslp;
2087                     if (bsp->repr == Seq_repr_seg) {
2088                       gslp = SegLocToPartsEx (bsp, sfp->location, TRUE);
2089                       sfp->location = SeqLocFree (sfp->location);
2090                       sfp->location = gslp;
2091                       hasNulls = LocationHasNullsBetween (sfp->location);
2092                       sfp->partial = (sfp->partial || hasNulls);
2093                     }
2094                     FreeAllFuzz (sfp->location);
2095                     SetSeqLocPartial (sfp->location, noLeft, noRight);
2096                     sfp->partial = (sfp->partial || noLeft || noRight);
2097                   }
2098                 }
2099               }
2100             }
2101           }
2102         } else if (val > 2) {
2103           ggl.ffp = ffp;
2104           ggl.omp = ObjMgrGet ();
2105           ggl.slp = slp;
2106           ggl.genexref = NULL;
2107           ggl.xrefmatch = FALSE;
2108           ggl.idx = 2;
2109           ggl.val = val;
2110           ggl.min = INT4_MAX;
2111           ggl.old_feature_location = old_location;
2112           MemSet ((Pointer) (&gs), 0, sizeof (GatherScope));
2113           gs.seglevels = 1;
2114           gs.get_feats_location = TRUE;
2115             MemSet((Pointer)(gs.ignore), (int)(TRUE), (size_t)(OBJ_MAX * sizeof(Boolean)));
2116             gs.ignore[OBJ_BIOSEQ] = FALSE;
2117             gs.ignore[OBJ_BIOSEQ_SEG] = FALSE;
2118             gs.ignore[OBJ_SEQFEAT] = FALSE;
2119             gs.ignore[OBJ_SEQANNOT] = FALSE;
2120           gs.scope = GetBestTopParentForItemID (ffp->input_entityID,
2121                                                 ffp->input_itemID,
2122                                                 ffp->input_itemtype);
2123           GatherEntity (ffp->input_entityID, (Pointer) &ggl, GeneUpdateFunc, &gs);
2124         }
2125       }
2126       SeqLocFree (slp);
2127       SeqEntrySetScope (oldscope);
2128     }
2129   }
2130   old_location = SeqLocFree (old_location);
2131   return rsult;
2132 }
2133 
2134 extern void StdFeatFormActnProc (ForM f)
2135 
2136 {
2137   FeatureFormPtr  ffp;
2138 
2139   if (FeatFormReplaceWithoutUpdateProc (f)) {
2140     ffp = (FeatureFormPtr) GetObjectExtra (f);
2141     if (ffp != NULL) {
2142       GetRidOfEmptyFeatsDescStrings (ffp->input_entityID, NULL);
2143       if (GetAppProperty ("InternalNcbiSequin") != NULL) {
2144         ExtendGeneFeatIfOnMRNA (ffp->input_entityID, NULL);
2145       }
2146       ObjMgrSetDirtyFlag (ffp->input_entityID, TRUE);
2147       ObjMgrSendMsg (OM_MSG_UPDATE, ffp->input_entityID,
2148                      ffp->input_itemID, ffp->input_itemtype);
2149     }
2150   }
2151 }
2152 
2153 extern void StdSeqFeatPtrToFeatFormProc (ForM f, Pointer data)
2154 
2155 {
2156   FeatureFormPtr  ffp;
2157   SeqEntryPtr     oldsep;
2158   SeqEntryPtr     sep;
2159   SeqFeatPtr      sfp;
2160   Int4            val;
2161 
2162   ffp = (FeatureFormPtr) GetObjectExtra (f);
2163   if (ffp != NULL) {
2164     sep = GetTopSeqEntryForEntityID (ffp->input_entityID);
2165     oldsep = SeqEntrySetScope (sep);
2166     sfp = (SeqFeatPtr) data;
2167     if (sfp != NULL) {
2168       switch (sfp->data.choice) {
2169         case SEQFEAT_BOND :
2170         case SEQFEAT_SITE :
2171         case SEQFEAT_PSEC_STR :
2172           val = (Int4) sfp->data.value.intvalue;
2173           PointerToDialog (ffp->data, (Pointer) &(val));
2174           break;
2175         case SEQFEAT_COMMENT:
2176           break;
2177         default :
2178           PointerToDialog (ffp->data, sfp->data.value.ptrvalue);
2179           break;
2180       }
2181       SeqFeatPtrToCommon (ffp, sfp);
2182       PointerToDialog (ffp->location, sfp->location);
2183     }
2184     SeqEntrySetScope (oldsep);
2185   }
2186 }
2187 
2188 extern void StdInitFeatFormProc (ForM f)
2189 
2190 {
2191   FeatureFormPtr  ffp;
2192 
2193   ffp = (FeatureFormPtr) GetObjectExtra (f);
2194   if (ffp != NULL) {
2195     PopulateGenePopup (ffp);
2196   }
2197 }
2198 
2199 extern void StdFeatFormAcceptButtonProc (ButtoN b)
2200 
2201 {
2202   Int2            attempts = 3;
2203   Char            badInfMssg [32];
2204   Char            badInfQual [256];
2205   FeatureFormPtr  ffp;
2206   SeqLocPtr       slp;
2207   WindoW          w;
2208   ValNodePtr      err_list;
2209   SeqEntryPtr     oldscope, sep;
2210 
2211   if (b != NULL) {
2212     w = ParentWindow (b);
2213     ffp = (FeatureFormPtr) GetObjectExtra (b);
2214     if (ffp != NULL && ffp->form != NULL && ffp->actproc != NULL) {
2215       sep = GetTopSeqEntryForEntityID (ffp->input_entityID);
2216       oldscope = SeqEntrySetScope (sep);
2217       slp = DialogToPointer (ffp->location);
2218       if (slp == NULL) {
2219         ErrPostEx (SEV_ERROR, 0, 0, "Feature must have a location!");
2220         err_list = TestDialog (ffp->location);
2221         DisplayErrorMessages ("Location Errors", err_list);
2222         err_list = ValNodeFree (err_list);    
2223         SeqEntrySetScope (oldscope);
2224         return;
2225       }
2226       SeqLocFree (slp);
2227       if ((! ffp->acceptBadInf) && (! TestInference (ffp, badInfQual, sizeof (badInfQual), badInfMssg))) {
2228         (ffp->badInfAttempts)++;
2229         if (GetAppProperty ("InternalNcbiSequin") != NULL) {
2230           attempts = 2;
2231         }
2232         if (ffp->badInfAttempts < attempts) {
2233           if (ffp->badInfAttempts == 2) {
2234             Message (MSG_OK, "%s - Please fix the %s error in\n%s", infWarning2, badInfMssg, badInfQual);
2235           } else {
2236             Message (MSG_OK, "%s - Please fix the %s error in\n%s", infWarning1, badInfMssg, badInfQual);
2237           }
2238           return;
2239         } else {
2240           if (Message (MSG_YN, "%s", infAccept) == ANS_NO) return;
2241           ffp->acceptBadInf = TRUE;
2242         }
2243       }
2244       Hide (w);
2245       (ffp->actproc) (ffp->form);
2246       SeqEntrySetScope (oldscope);
2247     }
2248     Update ();
2249     if (ffp != NULL && (ffp->leave_dlg_up == NULL || ! GetStatus (ffp->leave_dlg_up)))
2250     {
2251       Remove (w);
2252     }
2253     else
2254     {
2255       /* set strand and sequence ID, but clear other location information */
2256       SendMessageToDialog (ffp->location, NUM_VIB_MSG + 1);
2257       SendMessageToDialog (ffp->location, NUM_VIB_MSG + 2);
2258       Show (w);
2259     }
2260   }
2261 }
2262 
2263 extern void StdFeatFormCleanupProc (GraphiC g, VoidPtr data)
2264 
2265 {
2266   FeatureFormPtr  ffp;
2267   Uint2           userkey;
2268 
2269   ffp = (FeatureFormPtr) data;
2270   if (ffp != NULL) {
2271     ValNodeFreeData (ffp->geneNames);
2272     if (ffp->input_entityID > 0 && ffp->userkey > 0) {
2273       userkey = ffp->userkey;
2274       ffp->userkey = 0;
2275       ObjMgrFreeUserData (ffp->input_entityID, ffp->procid, ffp->proctype, userkey);
2276     }
2277   }
2278   StdCleanupFormProc (g, data);
2279 }
2280 
2281 extern ValNodePtr AddStringToValNodeChain (ValNodePtr head, CharPtr str, Uint1 choice)
2282 
2283 {
2284   ValNodePtr  vnp;
2285 
2286   vnp = ValNodeNew (head);
2287   if (head == NULL) {
2288     head = vnp;
2289   }
2290   if (vnp != NULL) {
2291     vnp->choice = choice;
2292     vnp->data.ptrvalue = StringSave (str);
2293   }
2294   return head;
2295 }
2296 
2297 static void StripPeriods (CharPtr str)
2298 
2299 {
2300   Char     ch;
2301   CharPtr  dst;
2302 
2303   if (str != NULL) {
2304     dst = str;
2305     ch = *str;
2306     while (ch != '\0') {
2307       if (ch != '.') {
2308         *dst = ch;
2309         dst++;
2310       }
2311       str++;
2312       ch = *str;
2313     }
2314     *dst = '\0';
2315   }
2316 }
2317 
2318 static void TrimLeadingSpaces (CharPtr str)
2319 
2320 {
2321   Char     ch;
2322   CharPtr  dst;
2323 
2324   if (str != NULL && str [0] != '\0') {
2325     dst = str;
2326     ch = *str;
2327     while (ch != '\0' && ch <= ' ') {
2328       str++;
2329       ch = *str;
2330     }
2331     while (ch != '\0') {
2332       *dst = ch;
2333       dst++;
2334       str++;
2335       ch = *str;
2336     }
2337     *dst = '\0';
2338   }
2339 }
2340 
2341 extern CharPtr NameStdPtrToAuthorSpreadsheetString (NameStdPtr nsp);
2342 extern CharPtr NameStdPtrToAuthorSpreadsheetString (NameStdPtr nsp)
2343 
2344 {
2345   Char   first [256];
2346   Char   frstinits [64];
2347   Char   initials [64];
2348   Int2   j;
2349   Char   last [256];
2350   Char   middle [128];
2351   Char   str [512];
2352   Char   suffix [64];
2353   Char   suffixPosition[64];
2354   Int2   i;
2355 
2356   if (nsp == NULL) return NULL;
2357   str [0] = '\0';
2358   StringNCpy_0 (first, nsp->names [1], sizeof (first));
2359   TrimSpacesAroundString (first);
2360   StringNCpy_0 (initials, nsp->names [4], sizeof (initials));
2361   StripPeriods (initials);
2362   TrimLeadingSpaces (initials);
2363   StringNCpy_0 (last, nsp->names [0], sizeof (last));
2364   TrimLeadingSpaces (last);
2365   StringNCpy_0 (middle, nsp->names [2], sizeof (middle));
2366   TrimLeadingSpaces (middle);
2367   if (StringCmp (initials, "al") == 0 &&
2368       StringCmp (last, "et") == 0 &&
2369       first [0] == '\0') {
2370     initials [0] = '\0';
2371     StringCpy (last, "et al.");
2372   }
2373   if (first [0] == '\0') {
2374     StringNCpy_0 (first, initials, sizeof (first));
2375     if (IS_ALPHA (first [0])) {
2376       if (first [1] == '-') {
2377         first [3] = '\0';
2378       } else {
2379         first [1] = '\0';
2380       }
2381     } else {
2382       first [0] = '\0';
2383     }
2384   }
2385   FirstNameToInitials (first, frstinits, sizeof (frstinits) - 1);
2386   StripPeriods (first);
2387   TrimLeadingSpaces (first);
2388   if (first [0] != '\0') {
2389     StringCat (str, first);
2390   } else {
2391   }
2392   StringCat (str, "\t");
2393   j = 0;
2394   while (initials [j] != '\0' && TO_UPPER (initials [j]) == TO_UPPER (frstinits [j])) {
2395     j++;
2396   }
2397   if (initials [j] != '\0') {
2398     StringCat (str, initials + j);
2399   } else {
2400   }
2401   StringCat (str, "\t");
2402   StringCat (str, last);
2403   StringNCpy_0 (suffix, nsp->names [5], sizeof (suffix));
2404   for (i = 0; i <= NUMBER_OF_SUFFIXES; i++)
2405     if (StringICmp (suffix, name_suffix_labels [i]) == 0) {
2406       sprintf (suffixPosition, "%d", i);
2407       break;
2408     }
2409   if (i == NUMBER_OF_SUFFIXES)
2410     sprintf (suffixPosition, "%d", 0);
2411   StringCat (str, "\t");
2412   if (suffix [0] != '\0') {
2413     StringCat (str, suffixPosition);
2414   } else {
2415   }
2416   StringCat (str, "\t");
2417   StringCat (str, middle);
2418   StringCat (str, "\n");
2419   return StringSave (str);
2420 }
2421 
2422 static void StdAuthListPtrToAuthorDialog (DialoG d, Pointer data)
2423 
2424 {
2425   AuthListPtr  alp;
2426   AuthorPtr    ap;
2427   ValNodePtr   head;
2428   Int2         j;
2429   ValNodePtr   names;
2430   NameStdPtr   nsp;
2431   PersonIdPtr  pid;
2432   TagListPtr   tlp;
2433   ValNodePtr   vnp;
2434 
2435   tlp = (TagListPtr) GetObjectExtra (d);
2436   alp = (AuthListPtr) data;
2437   if (tlp != NULL) {
2438     head = NULL;
2439     if (alp != NULL) {
2440       if (alp->choice == 1) {
2441         names = alp->names;
2442         while (names != NULL) {
2443           ap = names->data.ptrvalue;
2444           if (ap != NULL) {
2445             pid = ap->name;
2446             if (pid != NULL) {
2447               if (pid->choice == 2) {
2448                 nsp = pid->data;
2449                 if (nsp != NULL) {
2450                   vnp = ValNodeNew (head);
2451                   if (head == NULL) {
2452                     head = vnp;
2453                   }
2454                   if (vnp != NULL) {
2455                     vnp->data.ptrvalue = NameStdPtrToAuthorSpreadsheetString (nsp);
2456                   }
2457                 }
2458               }
2459             }
2460           }
2461           names = names->next;
2462         }
2463       } else {
2464         Message (MSG_ERROR, "Unable to handle author type %d", (int) alp->choice);
2465       }
2466     }
2467     SendMessageToDialog (tlp->dialog, VIB_MSG_RESET);
2468     tlp->vnp = head;
2469     SendMessageToDialog (tlp->dialog, VIB_MSG_REDRAW);
2470     for (j = 0, vnp = tlp->vnp; vnp != NULL; j++, vnp = vnp->next) {
2471     }
2472     tlp->max = MAX ((Int2) 0, (Int2) (j - tlp->rows + 1));
2473     CorrectBarMax (tlp->bar, tlp->max);
2474     CorrectBarPage (tlp->bar, tlp->rows - 1, tlp->rows - 1);
2475   }
2476 }
2477 
2478 extern NameStdPtr AuthorSpreadsheetStringToNameStdPtr (CharPtr txt);
2479 extern NameStdPtr AuthorSpreadsheetStringToNameStdPtr (CharPtr txt)
2480 
2481 {
2482   Char        ch;
2483   CharPtr     first;
2484   Char        initials [64];
2485   Int2        j;
2486   Int2        k;
2487   Char        last;
2488   NameStdPtr  nsp;
2489   Char        periods [128];
2490   CharPtr     str;
2491   Char        str1 [64];
2492   CharPtr     suffix;
2493   Char        suffixVal [80];
2494 
2495   if (txt == NULL) return NULL;
2496   nsp = NameStdNew ();
2497   if (nsp == NULL) return NULL;
2498   nsp->names [0] = ExtractTagListColumn (txt, 2);
2499   TrimLeadingSpaces (nsp->names [0]);
2500   first = ExtractTagListColumn (txt, 0);
2501   StripPeriods (first);
2502   nsp->names [1] = StringSave (first);
2503   TrimLeadingSpaces (nsp->names [1]);
2504   FirstNameToInitials (first, str1, sizeof (str1) - 1);
2505   str = ExtractTagListColumn (txt, 1);
2506   StringNCat (str1, str, sizeof (str1) - 1);
2507   MemFree (str);
2508   j = 0;
2509   k = 0;
2510   ch = str1 [j];
2511   while (ch != '\0') {
2512     if (ch != ' ') {
2513       initials [k] = ch;
2514       k++;
2515     }
2516     j++;
2517     ch = str1 [j];
2518   }
2519   initials [k] = '\0';
2520   periods [0] = '\0';
2521           j = 0;
2522           ch = initials [j];
2523           while (ch != '\0') {
2524             if (ch == ',') {
2525               initials [j] = '.';
2526             }
2527             j++;
2528             ch = initials [j];
2529           }
2530           str = StringStr (initials, ".ST.");
2531           if (str != NULL) {
2532             *(str + 2) = 't';
2533           }
2534   j = 0;
2535   k = 0;
2536   ch = initials [j];
2537   while (ch != '\0') {
2538     if (ch == '-') {
2539       periods [k] = ch;
2540       k++;
2541       j++;
2542       ch = initials [j];
2543     } else if (ch == '.') {
2544       j++;
2545       ch = initials [j];
2546             } else if (ch == ' ') {
2547               j++;
2548               ch = initials [j];
2549     } else {
2550       periods [k] = ch;
2551               last = ch;
2552       k++;
2553       j++;
2554       ch = initials [j];
2555               if (ch == '\0') {
2556                 if (! (IS_LOWER (last))) {
2557                   periods [k] = '.';
2558                   k++;
2559                 }
2560               /* } else if (ch == '.' && initials [j + 1] == '\0') { */
2561               } else if (! (IS_LOWER (ch))) {
2562                 periods [k] = '.';
2563                 k++;
2564               }
2565     }
2566   }
2567   periods [k] = '\0';
2568   nsp->names [4] = StringSave (periods);
2569   TrimLeadingSpaces (nsp->names [4]);
2570   str = ExtractTagListColumn (txt, 3);
2571   StringNCpy_0 (str1, str, sizeof (str1));
2572   MemFree (str);
2573   j = 0;
2574   k = 0;
2575   ch = str1 [j];
2576   while (ch != '\0') {
2577     if (ch != ' ') {
2578       suffixVal [k] = ch;
2579       k++;
2580     }
2581     j++;
2582     ch = str1 [j];
2583   }
2584   suffixVal [k] = '\0';
2585   if (suffixVal [0] != '\0') {
2586     suffix = GetEnumName (atoi(suffixVal), name_suffix_alist);
2587     nsp->names [5] = StringSave (suffix);
2588     TrimLeadingSpaces (nsp->names [5]);
2589     if (StringHasNoText (nsp->names [5])) {
2590       nsp->names [5] = MemFree (nsp->names [5]);
2591     }
2592   }
2593   if (StringCmp (nsp->names [0], "et al") == 0) {
2594     nsp->names [0] = MemFree (nsp->names [0]);
2595     nsp->names [0] = StringSave ("et al.");
2596   }
2597   nsp->names [2] = ExtractTagListColumn (txt, 4);
2598   TrimLeadingSpaces (nsp->names [2]);
2599   if (StringHasNoText (nsp->names [2])) {
2600     nsp->names [2] = MemFree (nsp->names [2]);
2601   }
2602   return nsp;
2603 }
2604 
2605 static Pointer AuthorDialogToStdAuthListPtr (DialoG d)
2606 
2607 {
2608   AuthListPtr  alp;
2609   AuthorPtr    ap;
2610   Char         ch;
2611   Int2         j;
2612   Int2         len;
2613   ValNodePtr   names;
2614   NameStdPtr   nsp;
2615   Boolean      okay;
2616   PersonIdPtr  pid;
2617   CharPtr      str;
2618   TagListPtr   tlp;
2619   ValNodePtr   vnp;
2620 
2621   alp = NULL;
2622   tlp = (TagListPtr) GetObjectExtra (d);
2623   if (tlp != NULL && tlp->vnp != NULL) {
2624     alp = AuthListNew ();
2625     if (alp != NULL) {
2626       alp->choice = 1;
2627       names = NULL;
2628       for (vnp = tlp->vnp; vnp != NULL; vnp = vnp->next) {
2629         str = ExtractTagListColumn ((CharPtr) vnp->data.ptrvalue, 2);
2630         okay = FALSE;
2631         len = StringLen (str);
2632         for (j = 0; j < len; j++) {
2633           ch = str [j];
2634           if (ch != ' ' && ch != '\t' && ch != '\n') {
2635             okay = TRUE;
2636           }
2637         }
2638         MemFree (str);
2639         if (okay) {
2640           names = ValNodeNew (names);
2641           if (alp->names == NULL) {
2642             alp->names = names;
2643           }
2644           if (names != NULL) {
2645             ap = AuthorNew ();
2646             names->choice = 1;
2647             names->data.ptrvalue = ap;
2648             if (ap != NULL) {
2649               pid = PersonIdNew ();
2650               ap->name = pid;
2651               if (pid != NULL) {
2652                 pid->choice = 2;
2653                 nsp = AuthorSpreadsheetStringToNameStdPtr ((CharPtr) vnp->data.ptrvalue);
2654                 pid->data = nsp;
2655               }
2656             }
2657           }
2658         }
2659       }
2660       if (alp->names == NULL) {
2661         alp = AuthListFree (alp);
2662       }
2663     }
2664   }
2665   return (Pointer) alp;
2666 }
2667 
2668 static void StrAuthListPtrToAuthorDialog (DialoG d, Pointer data)
2669 
2670 {
2671   AuthListPtr  alp;
2672   ValNodePtr   head;
2673   Int2         j;
2674   ValNodePtr   names;
2675   Char         str [128];
2676   TagListPtr   tlp;
2677   ValNodePtr   vnp;
2678 
2679   tlp = (TagListPtr) GetObjectExtra (d);
2680   alp = (AuthListPtr) data;
2681   if (tlp != NULL) {
2682     head = NULL;
2683     if (alp != NULL) {
2684       if (alp->choice == 2 || alp->choice == 3) {
2685         names = alp->names;
2686         while (names != NULL) {
2687           StringNCpy_0 (str, names->data.ptrvalue, sizeof (str) - 2);
2688           StringCat (str, "\n");
2689           vnp = ValNodeNew (head);
2690           if (head == NULL) {
2691             head = vnp;
2692           }
2693           if (vnp != NULL) {
2694             vnp->data.ptrvalue = StringSave (str);
2695           }
2696           names = names->next;
2697         }
2698       } else {
2699         Message (MSG_ERROR, "Unable to handle author type %d", (int) alp->choice);
2700       }
2701     }
2702     SendMessageToDialog (tlp->dialog, VIB_MSG_RESET);
2703     tlp->vnp = head;
2704     SendMessageToDialog (tlp->dialog, VIB_MSG_REDRAW);
2705     for (j = 0, vnp = tlp->vnp; vnp != NULL; j++, vnp = vnp->next) {
2706     }
2707     tlp->max = MAX ((Int2) 0, (Int2) (j - tlp->rows + 1));
2708     CorrectBarMax (tlp->bar, tlp->max);
2709     CorrectBarPage (tlp->bar, tlp->rows - 1, tlp->rows - 1);
2710   }
2711 }
2712 
2713 static Pointer AuthorDialogToStrAuthListPtr (DialoG d)
2714 
2715 {
2716   AuthListPtr  alp;
2717   Char         ch;
2718   Int2         j;
2719   Int2         len;
2720   ValNodePtr   names;
2721   Boolean      okay;
2722   CharPtr      str;
2723   TagListPtr   tlp;
2724   ValNodePtr   vnp;
2725 
2726   alp = NULL;
2727   tlp = (TagListPtr) GetObjectExtra (d);
2728   if (tlp != NULL && tlp->vnp != NULL) {
2729     alp = AuthListNew ();
2730     if (alp != NULL) {
2731       alp->choice = 2;
2732       names = NULL;
2733       for (vnp = tlp->vnp; vnp != NULL; vnp = vnp->next) {
2734         str = (CharPtr) vnp->data.ptrvalue;
2735         okay = FALSE;
2736         len = StringLen (str);
2737         for (j = 0; j < len; j++) {
2738           ch = str [j];
2739           if (ch != ' ' && ch != '\t' && ch != '\n') {
2740             okay = TRUE;
2741           }
2742         }
2743         if (okay) {
2744           names = ValNodeNew (names);
2745           if (alp->names == NULL) {
2746             alp->names = names;
2747           }
2748           if (names != NULL) {
2749             names->choice = 2;
2750             names->data.ptrvalue = ExtractTagListColumn ((CharPtr) vnp->data.ptrvalue, 0);
2751 
2752           }
2753         }
2754       }
2755       if (alp->names == NULL) {
2756         alp = AuthListFree (alp);
2757       }
2758     }
2759   }
2760   return (Pointer) alp;
2761 }
2762 
2763 static void AuthListPtrToAuthorDialog (DialoG d, Pointer data)
2764 
2765 {
2766   AuthorDialogPtr  adp;
2767   AuthListPtr      alp;
2768 
2769   adp = (AuthorDialogPtr) GetObjectExtra (d);
2770   if (adp != NULL) {
2771     alp = (AuthListPtr) data;
2772     if (alp != NULL) {
2773       adp->type = alp->choice;
2774     }
2775     if (adp->type == 1) {
2776       Hide (adp->strGrp);
2777       Show (adp->stdGrp);
2778       PointerToDialog (adp->stdAuthor, data);
2779     } else if (adp->type == 2 || adp->type == 3) {
2780       Hide (adp->stdGrp);
2781       Show (adp->strGrp);
2782       PointerToDialog (adp->strAuthor, data);
2783     }
2784   }
2785 }
2786 
2787 static Pointer AuthorDialogToAuthListPtr (DialoG d)
2788 
2789 {
2790   AuthorDialogPtr  adp;
2791   AuthListPtr      alp;
2792 
2793   adp = (AuthorDialogPtr) GetObjectExtra (d);
2794   alp = NULL;
2795   if (adp != NULL) {
2796     if (adp->type == 1) {
2797       alp = (AuthListPtr) DialogToPointer (adp->stdAuthor);
2798     } else if (adp->type == 2 || adp->type == 3) {
2799       if ((alp = (AuthListPtr) DialogToPointer (adp->strAuthor)) != NULL)
2800         alp->choice = adp->type;
2801     }
2802   }
2803   return (Pointer) alp;
2804 }
2805 
2806 static void AuthorDialogMessage (DialoG d, Int2 mssg)
2807 
2808 {
2809   AuthorDialogPtr  adp;
2810 
2811   adp = (AuthorDialogPtr) GetObjectExtra (d);
2812   if (adp != NULL) {
2813     switch (mssg) {
2814       case VIB_MSG_ENTER :
2815         if (adp->type == 1) {
2816           SendMessageToDialog (adp->stdAuthor, VIB_MSG_ENTER);
2817         } else if (adp->type == 2 || adp->type == 3) {
2818           SendMessageToDialog (adp->strAuthor, VIB_MSG_ENTER);
2819         }
2820         break;
2821       default :
2822         break;
2823     }
2824   }
2825 }
2826 
2827 
2828 static Boolean IsAuthList (CharPtr path)
2829 {
2830   FILE *fp;
2831   Char buffer[10];
2832   Int4 len;
2833   Boolean rval = FALSE;
2834 
2835   fp = FileOpen (path, "r");
2836   if (fp == NULL) return FALSE;
2837   
2838   len = fread (buffer, 1, 9, fp);
2839   buffer[9] = 0;
2840   if (StringCmp (buffer, "Auth-list") == 0)
2841   {
2842     rval = TRUE;
2843   }
2844   FileClose (fp);
2845   return rval;
2846 }
2847 
2848 
2849 static NameStdPtr ReadNameFromString (CharPtr str, CharPtr PNTR next_name, BoolPtr found_special)
2850 {
2851   CharPtr cp_end, cp_space;
2852   NameStdPtr n;
2853 
2854   if (StringHasNoText (str)) 
2855   {
2856     if (next_name != NULL)
2857     {
2858       *next_name = NULL;
2859     }
2860     return NULL;
2861   }
2862 
2863   /* skip over any leading spaces */
2864   str += StringSpn (str, " \t");
2865 
2866   /* skip over "and" if found */
2867   if (StringNCmp (str, "and ", 4) == 0)
2868   {
2869     str += 4;
2870   }
2871   if (StringHasNoText (str)) return NULL;
2872 
2873   cp_end = StringChr (str, ',');
2874   if (cp_end != NULL)
2875   {
2876     *cp_end = 0;
2877     if (next_name != NULL)
2878     {
2879       if (StringHasNoText (cp_end + 1))
2880       {
2881         *next_name = NULL;
2882       }
2883       else
2884       {
2885         *next_name = cp_end + 1;
2886       }
2887     }
2888   }
2889   else if (next_name != NULL)
2890   {
2891     *next_name = NULL;
2892   }
2893 
2894   n = NameStdNew ();  
2895   /* look for elements in name */
2896   cp_space = StringRChr (str, ' ');
2897   if (cp_space == NULL)
2898   {
2899     n->names[0] = StringSave (str);
2900   }
2901   else
2902   {
2903     n->names[0] = StringSave (cp_space + 1);
2904     while (isspace (*cp_space))
2905     {
2906       cp_space--;
2907     }
2908     *(cp_space + 1) = 0;
2909     cp_space = StringChr (str, ' ');
2910     if (cp_space == NULL)
2911     {
2912        n->names[1] = StringSave (str);
2913        n->names[4] = (CharPtr) MemNew (sizeof (Char) * 3);
2914        sprintf (n->names[4], "%c.", *(n->names[1]));
2915     }
2916     else
2917     {
2918       *(cp_space) = 0;
2919       n->names[1] = StringSave (str);
2920 
2921       cp_space++;
2922       while (isspace (*cp_space))
2923       {
2924         cp_space++;
2925       }
2926       
2927       n->names[4] = (CharPtr) MemNew (sizeof (Char) * (4 + StringLen (cp_space)));
2928       sprintf (n->names[4], "%c.%s.", *(n->names[1]), cp_space);
2929     }
2930     SpecialCharReplace (&(n->names[1]), NULL, found_special, NULL);
2931     SpecialCharReplace (&(n->names[4]), NULL, found_special, NULL);
2932   }
2933   SpecialCharReplace (&(n->names[0]), NULL, found_special, NULL);
2934 
2935   return n;
2936 }
2937 
2938 
2939 static Boolean EndsWithComma (CharPtr str)
2940 {
2941   CharPtr cp;
2942 
2943   if (StringHasNoText (str)) return FALSE;
2944   cp = str + StringLen (str) - 1;
2945   while (isspace (*cp) && cp > str)
2946   {
2947     cp--;
2948   }
2949   if (*cp == ',')
2950   {
2951     return TRUE;
2952   }
2953   else
2954   {
2955     return FALSE;
2956   }
2957 }
2958 
2959 
2960 static CharPtr ExtendToComma (CharPtr line, ReadBufferPtr rbp)
2961 {
2962   CharPtr next_line = NULL, tmp;
2963   Boolean end_of_file = FALSE;
2964 
2965   while (!EndsWithComma (line) && !end_of_file)
2966   {
2967     next_line = AbstractReadFunction (rbp);
2968     if (next_line == NULL)
2969     {
2970       end_of_file = TRUE;
2971     }
2972     else if (StringHasNoText (next_line))
2973     {
2974       next_line = MemFree (next_line);
2975     }
2976     else if (StringHasNoText (line))
2977     {
2978       line = MemFree (line);
2979       line = next_line;
2980     }
2981     else
2982     {
2983       tmp = (CharPtr) MemNew (sizeof (Char) * (StringLen (line) + StringLen (next_line) + 2));
2984       sprintf (tmp, "%s %s", line, next_line);
2985       next_line = MemFree (next_line);
2986       line = MemFree (line);
2987       line = tmp;
2988     }
2989   }
2990   return line;
2991 }
2992 
2993 
2994 static AuthListPtr ReadAuthorListFromTextFile (CharPtr path)
2995 {
2996   ReadBufferData rbd;
2997   CharPtr        line;
2998   AuthListPtr    alp = NULL;
2999   AuthorPtr      ap;
3000   CharPtr        cp, next_cp;
3001   NameStdPtr     n;
3002   ValNodePtr     names = NULL;
3003   Boolean        found_special = FALSE;
3004 
3005   rbd.fp = FileOpen (path, "r");
3006   if (rbd.fp == NULL) return FALSE;
3007   rbd.current_data = NULL;
3008 
3009   line = ExtendToComma (NULL, &rbd);
3010   while (line != NULL) 
3011   {
3012     cp = line;
3013     next_cp = NULL;
3014     while (cp != NULL)
3015     {
3016       n = ReadNameFromString (cp, &next_cp, &found_special);
3017       if (n != NULL)
3018       {
3019         ap = AuthorNew ();
3020         ap->name = PersonIdNew ();
3021         ap->name->choice = 2;
3022         ap->name->data = n;
3023         ValNodeAddPointer (&names, 0, ap);
3024       }
3025       cp = next_cp;
3026     }
3027     line = MemFree (line);
3028     line = ExtendToComma (NULL, &rbd);
3029   }
3030   if (names != NULL)
3031   {
3032     alp = AuthListNew ();
3033     alp->choice = 1;
3034     alp->names = names;
3035   }
3036   if (found_special)
3037   {
3038     Message (MSG_OK, "Special characters in names were converted");
3039   }
3040   
3041   FileClose (rbd.fp);
3042   return alp;
3043 }
3044 
3045 
3046 static Boolean ReadAuthorDialog (DialoG d, CharPtr filename)
3047 
3048 {
3049   AuthorDialogPtr  adp;
3050   AsnIoPtr         aip;
3051   AuthListPtr      alp = NULL;
3052   Char             path [PATH_MAX];
3053 
3054   path [0] = '\0';
3055   StringNCpy_0 (path, filename, sizeof (path));
3056   adp = (AuthorDialogPtr) GetObjectExtra (d);
3057   if (adp != NULL) {
3058     if (path [0] != '\0' || GetInputFileName (path, sizeof (path), "", "TEXT")) {
3059       if (IsAuthList (path)) {
3060         aip = AsnIoOpen (path, "r");
3061         if (aip != NULL) {
3062           alp = AuthListAsnRead (aip, NULL);
3063           AsnIoClose (aip);
3064         }
3065       } else {
3066         alp = ReadAuthorListFromTextFile (path);
3067       }
3068       if (alp != NULL) {
3069         PointerToDialog (adp->dialog, (Pointer) alp);
3070         alp = AuthListFree (alp);
3071         Update ();
3072         return TRUE;
3073       }
3074     }
3075   }
3076   return FALSE;
3077 }
3078 
3079 static Boolean WriteAuthorDialog (DialoG d, CharPtr filename)
3080 
3081 {
3082   AuthorDialogPtr  adp;
3083   AsnIoPtr         aip;
3084   AuthListPtr      alp;
3085   Char             path [PATH_MAX];
3086 #ifdef WIN_MAC
3087   FILE            *f;
3088 #endif
3089 
3090   path [0] = '\0';
3091   StringNCpy_0 (path, filename, sizeof (path));
3092   adp = (AuthorDialogPtr) GetObjectExtra (d);
3093   if (adp != NULL) {
3094     if (path [0] != '\0' || GetOutputFileName (path, sizeof (path), NULL)) {
3095 #ifdef WIN_MAC
3096       f = FileOpen (path, "r");
3097       if (f != NULL) {
3098         FileClose (f);
3099       } else {
3100         FileCreate (path, "TEXT", "ttxt");
3101       }
3102 #endif
3103       aip = AsnIoOpen (path, "w");
3104       if (aip != NULL) {
3105         alp = DialogToPointer (adp->dialog);
3106         AuthListAsnWrite (alp, aip, NULL);
3107         AsnIoClose (aip);
3108         alp = AuthListFree (alp);
3109         return TRUE;
3110       }
3111     }
3112   }
3113   return FALSE;
3114 }
3115 
3116 
3117 static void InsertFirstAuthor (ButtoN b)
3118 {
3119   AuthorDialogPtr  adp;
3120   AuthListPtr      alp;
3121   AuthorPtr        ap;
3122   NameStdPtr       nsp;
3123   ValNodePtr       vnp;
3124 
3125   adp = (AuthorDialogPtr) GetObjectExtra (b);
3126   if (adp == NULL) {
3127     return;
3128   }
3129 
3130   alp = DialogToPointer (adp->dialog);
3131   if (alp != NULL && alp->names != NULL) {
3132     vnp = ValNodeNew (NULL);
3133     vnp->next = alp->names;
3134     alp->names = vnp;
3135     if (alp->choice == 1) {
3136       ap = AuthorNew ();
3137       ap->name = PersonIdNew ();
3138       ap->name->choice = 2;
3139       nsp = NameStdNew ();
3140       ap->name->data = nsp;
3141       vnp->data.ptrvalue = ap;
3142     } else {
3143       vnp->data.ptrvalue = StringSave ("");
3144     }
3145     PointerToDialog (adp->dialog, alp);
3146     SendMessageToDialog (adp->dialog, VIB_MSG_ENTER);
3147   }
3148   alp = AuthListFree (alp);
3149 }
3150 
3151 
3152 extern DialoG CreateAuthorDialog (GrouP prnt, Uint2 rows, Int2 spacing)
3153 
3154 {
3155   AuthorDialogPtr  adp;
3156   GrouP            k;
3157   GrouP            p, list_grp;
3158   PrompT           p1, p2, p3, p4, p5;
3159   TagListPtr       tlp;
3160   ButtoN           b;
3161 
3162   p = HiddenGroup (prnt, -1, 0, NULL);
3163 
3164   adp = (AuthorDialogPtr) MemNew (sizeof (AuthorDialog));
3165   if (adp != NULL) {
3166 
3167     SetObjectExtra (p, adp, StdCleanupExtraProc);
3168     adp->dialog = (DialoG) p;
3169     adp->todialog = AuthListPtrToAuthorDialog;
3170     adp->fromdialog = AuthorDialogToAuthListPtr;
3171     adp->testdialog = NULL;
3172     adp->importdialog = ReadAuthorDialog;
3173     adp->exportdialog = WriteAuthorDialog;
3174     adp->dialogmessage = AuthorDialogMessage;
3175 
3176     list_grp = HiddenGroup (p, 0, 0, NULL);
3177 
3178     adp->strGrp = HiddenGroup (list_grp, -1, 0, NULL);
3179     SetGroupSpacing (adp->strGrp, 3, 2);
3180 
3181     k = HiddenGroup (adp->strGrp, -4, 0, NULL);
3182     SetGroupSpacing (k, spacing, spacing);
3183     p1 = StaticPrompt (k, "Name", 0, 0, programFont, 'c');
3184 
3185     adp->strAuthor = CreateTagListDialog (adp->strGrp, rows, 1, spacing,
3186                                           author_types, str_author_widths, NULL,
3187                                           StrAuthListPtrToAuthorDialog,
3188                                           AuthorDialogToStrAuthListPtr);
3189     Hide (adp->strGrp);
3190 
3191     adp->stdGrp = HiddenGroup (list_grp, -1, 0, NULL);
3192     SetGroupSpacing (adp->stdGrp, 3, 2);
3193 
3194     k = HiddenGroup (adp->stdGrp, -4, 0, NULL);
3195     SetGroupSpacing (k, spacing, spacing);
3196     p2 = StaticPrompt (k, "First Name", 0, 0, programFont, 'c');
3197     p3 = StaticPrompt (k, "M.I.", 0, 0, programFont, 'c');
3198     p4 = StaticPrompt (k, "Last Name", 0, 0, programFont, 'c');
3199     p5 = StaticPrompt (k, "Sfx", 0, 0, programFont, 'c');
3200 
3201     adp->stdAuthor = CreateTagListDialog (adp->stdGrp, rows, 4, spacing,
3202                                           author_types, std_author_widths,
3203                                           author_popups,
3204                                           StdAuthListPtrToAuthorDialog,
3205                                           AuthorDialogToStdAuthListPtr);
3206     adp->type = 1;
3207 
3208     tlp = (TagListPtr) GetObjectExtra (adp->strAuthor);
3209     if (tlp != NULL) {
3210       AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [0], (HANDLE) p1, NULL);
3211     }
3212     tlp = (TagListPtr) GetObjectExtra (adp->stdAuthor);
3213     if (tlp != NULL) {
3214       AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [0], (HANDLE) p2, NULL);
3215       AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [1], (HANDLE) p3, NULL);
3216       AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [2], (HANDLE) p4, NULL);
3217       AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [3], (HANDLE) p5, NULL);
3218     }
3219 
3220     b = PushButton (p, "Insert First Author", InsertFirstAuthor);
3221     SetObjectExtra (b, adp, NULL);
3222     AlignObjects (ALIGN_CENTER, (HANDLE) list_grp, (HANDLE) b, NULL);
3223   }
3224 
3225   return (DialoG) p;
3226 }
3227 
3228 typedef struct affildialog {
3229   DIALOG_MESSAGE_BLOCK
3230   TexT            affil;
3231   TexT            div;
3232   TexT            address;
3233   TexT            city;
3234   TexT            state;
3235   TexT            zip;
3236   TexT            country;
3237   TexT            phone;
3238   TexT            fax;
3239   TexT            email;
3240 } AffilDialog, PNTR AffilDialogPtr;
3241 
3242 static void AffilPtrToAffilDialog (DialoG d, Pointer data)
3243 
3244 {
3245   AffilDialogPtr  adp;
3246   AffilPtr        ap;
3247 
3248   adp = (AffilDialogPtr) GetObjectExtra (d);
3249   ap = (AffilPtr) data;
3250   if (adp != NULL) {
3251     if (ap != NULL) {
3252       SafeSetTitle (adp->affil, ap->affil);
3253       SafeSetTitle (adp->div, ap->div);
3254       SafeSetTitle (adp->address, ap->street);
3255       SafeSetTitle (adp->city, ap->city);
3256       SafeSetTitle (adp->state, ap->sub);
3257       SafeSetTitle (adp->zip, ap->postal_code);
3258       SafeSetTitle (adp->country, ap->country);
3259       SafeSetTitle (adp->phone, ap->phone);
3260       SafeSetTitle (adp->fax, ap->fax);
3261       SafeSetTitle (adp->email, ap->email);
3262     } else {
3263       SafeSetTitle (adp->affil, "");
3264       SafeSetTitle (adp->div, "");
3265       SafeSetTitle (adp->address, "");
3266       SafeSetTitle (adp->city, "");
3267       SafeSetTitle (adp->state, "");
3268       SafeSetTitle (adp->zip, "");
3269       SafeSetTitle (adp->country, "");
3270       SafeSetTitle (adp->phone, "");
3271       SafeSetTitle (adp->fax, "");
3272       SafeSetTitle (adp->email, "");
3273     }
3274   }
3275 }
3276 
3277 static Pointer AffilDialogToAffilPtr (DialoG d)
3278 
3279 {
3280   AffilDialogPtr  adp;
3281   AffilPtr        ap;
3282 
3283   ap = NULL;
3284   adp = (AffilDialogPtr) GetObjectExtra (d);
3285   if (adp != NULL) {
3286     ap = AffilNew ();
3287     if (ap != NULL) {
3288       ap->affil = SaveStringFromText (adp->affil);
3289       ap->div = SaveStringFromText (adp->div);
3290       ap->street = SaveStringFromText (adp->address);
3291       ap->city = SaveStringFromText (adp->city);
3292       ap->sub = SaveStringFromText (adp->state);
3293       ap->postal_code = SaveStringFromText (adp->zip);
3294       ap->country = SaveStringFromText (adp->country);
3295       ap->phone = SaveStringFromText (adp->phone);
3296       ap->fax = SaveStringFromText (adp->fax);
3297       ap->email = SaveStringFromText (adp->email);
3298       if (ap->div == NULL && ap->street == NULL && ap->city == NULL &&
3299            ap->sub == NULL && ap->postal_code == NULL && ap->country == NULL &&
3300            ap->phone == NULL && ap->fax == NULL && ap->email == NULL) {
3301         ap->choice = 1;
3302         if (ap->affil == NULL) {
3303           ap = AffilFree (ap);
3304         }
3305       } else {
3306         ap->choice = 2;
3307       }
3308     }
3309   }
3310   return (Pointer) ap;
3311 }
3312 
3313 static void AffilDialogMessage (DialoG d, Int2 mssg)
3314 
3315 {
3316   AffilDialogPtr  adp;
3317 
3318   adp = (AffilDialogPtr) GetObjectExtra (d);
3319   if (adp != NULL) {
3320     switch (mssg) {
3321       case VIB_MSG_ENTER :
3322         Select (adp->affil);
3323         break;
3324       default :
3325         break;
3326     }
3327   }
3328 }
3329 
3330 static DialoG CreateAnAffilDialog (GrouP prnt, CharPtr title,
3331                                    Boolean publisher,
3332                                    Boolean split,
3333                                    Boolean proceedings,
3334                                    GrouP PNTR grp1, GrouP PNTR grp2)
3335 
3336 {
3337   AffilDialogPtr  adp;
3338   GrouP           g;
3339   GrouP           g1, g2;
3340   GrouP           j;
3341   GrouP           m;
3342   GrouP           p;
3343   GrouP           q;
3344   GrouP           s;
3345 #ifdef WIN_MAC
3346   Int2            wid = 20;
3347   Int2            zipw = 6;
3348   Int2            ewid = 8;
3349 #else
3350   Int2            wid = 30;
3351   Int2            zipw = 10;
3352   Int2            ewid = 20;
3353 #endif
3354 
3355   p = HiddenGroup (prnt, 0, 0, NULL);
3356 
3357   adp = (AffilDialogPtr) MemNew (sizeof (AffilDialog));
3358   if (adp != NULL) {
3359 
3360     SetObjectExtra (p, adp, StdCleanupExtraProc);
3361     adp->dialog = (DialoG) p;
3362     adp->todialog = AffilPtrToAffilDialog;
3363     adp->fromdialog = AffilDialogToAffilPtr;
3364     adp->testdialog = NULL;
3365     adp->dialogmessage = AffilDialogMessage;
3366 
3367     if (title != NULL && title [0] != '\0') {
3368       s = NormalGroup (p, 0, -2, title, systemFont, NULL);
3369     } else {
3370       s = HiddenGroup (p, 0, -2, NULL);
3371     }
3372     m = HiddenGroup (s, 0, 0, NULL);
3373 
3374     q = HiddenGroup (m, 2, 0, NULL);
3375     g1 = q;
3376     g2 = NULL;
3377     if (grp1 != NULL) {
3378       *grp1 = q;
3379     }
3380     if (grp2 != NULL) {
3381       *grp2 = NULL;
3382     }
3383     g = HiddenGroup (q, 0, 20, NULL);
3384     if (publisher) {
3385       StaticPrompt (g, "Publisher", 0, dialogTextHeight, programFont, 'l');
3386     } else if (proceedings) {
3387       StaticPrompt (g, "Location", 0, dialogTextHeight, programFont, 'l');
3388     } else {
3389       StaticPrompt (g, "Institution", 0, dialogTextHeight, programFont, 'l');
3390       StaticPrompt (g, "Department", 0, dialogTextHeight, programFont, 'l');
3391     }
3392     StaticPrompt (g, "Address", 0, dialogTextHeight, programFont, 'l');
3393     StaticPrompt (g, "City", 0, dialogTextHeight, programFont, 'l');
3394     StaticPrompt (g, "State/Province", 0, dialogTextHeight, programFont, 'l');
3395     StaticPrompt (g, "Country", 0, dialogTextHeight, programFont, 'l');
3396     if (! split) {
3397       if (! proceedings) {
3398         StaticPrompt (g, "", 0, stdLineHeight, programFont, 'l');
3399         StaticPrompt (g, "Phone", 0, dialogTextHeight, programFont, 'l');
3400         if (publisher) {
3401           StaticPrompt (g, "Internet Access", 0, dialogTextHeight, programFont, 'l');
3402         } else {
3403           StaticPrompt (g, "Email", 0, dialogTextHeight, programFont, 'l');
3404         }
3405       }
3406     }
3407     j = HiddenGroup (q, 0, 20, NULL);
3408     g = HiddenGroup (j, 0, 20, NULL);
3409     adp->affil = DialogText (g, "", wid, NULL);
3410     adp->div = NULL;
3411     if (! publisher && ! proceedings) {
3412       adp->div = DialogText (g, "", wid, NULL);
3413     }
3414     adp->address = DialogText (g, "", wid, NULL);
3415     adp->city = DialogText (g, "", wid, NULL);
3416     g = HiddenGroup (j, 3, 0, NULL);
3417     SetGroupSpacing (g, 20, 2);
3418     adp->state = DialogText (g, "", 10, NULL);
3419     if (! proceedings) {
3420       /* StaticPrompt (g, "Zip/Postal Code", 7 * stdCharWidth,
3421                     dialogTextHeight, programFont, 'l'); */
3422       StaticPrompt (g, "Zip/Postal Code", 0, dialogTextHeight, programFont, 'l');
3423       adp->zip = DialogText (g, "", zipw, NULL);
3424     }
3425     g = HiddenGroup (j, 0, 20, NULL);
3426     adp->country = DialogText (g, "", wid, NULL);
3427     if (split) {
3428       if (! proceedings) {
3429         q = HiddenGroup (m, 2, 0, NULL);
3430         g2 = q;
3431         if (grp2 != NULL) {
3432           *grp2 = q;
3433         }
3434         g = HiddenGroup (q, 0, 20, NULL);
3435         StaticPrompt (g, "", 0, stdLineHeight, programFont, 'l');
3436         StaticPrompt (g, "Phone", 0, dialogTextHeight, programFont, 'l');
3437         if (publisher) {
3438           StaticPrompt (g, "Internet Access", 0, dialogTextHeight, programFont, 'l');
3439         } else {
3440           StaticPrompt (g, "Email", 0, dialogTextHeight, programFont, 'l');
3441         }
3442         j = HiddenGroup (q, 0, 20, NULL);
3443       }
3444     }
3445     if (! proceedings) {
3446       StaticPrompt (j, "Please include country code for non-U.S. phone numbers.",
3447                     0, stdLineHeight, programFont, 'l');
3448       g = HiddenGroup (j, 3, 0, NULL);
3449       SetGroupSpacing (g, 20, 2);
3450       adp->phone = DialogText (g, "", 10, NULL);
3451       if (split) {
3452         StaticPrompt (g, "Fax", 0,
3453                       dialogTextHeight, programFont, 'l');
3454       } else {
3455         StaticPrompt (g, "Fax", /* 7 * stdCharWidth */ 0,
3456                       dialogTextHeight, programFont, 'l');
3457       }
3458       adp->fax = DialogText (g, "", 10, NULL);
3459       g = HiddenGroup (j, 0, 20, NULL);
3460       if (split) {
3461         adp->email = DialogText (g, "", ewid, NULL);
3462       } else {
3463         adp->email = DialogText (g, "", ewid, NULL);
3464       }
3465     }
3466 
3467     if (split) {
3468       AlignObjects (ALIGN_RIGHT, (HANDLE) adp->affil,
3469                     (HANDLE) adp->address, (HANDLE) adp->city,
3470                     (HANDLE) adp->zip, (HANDLE) adp->country,
3471                     (HANDLE) adp->div, NULL);
3472       AlignObjects (ALIGN_RIGHT, (HANDLE) adp->fax, (HANDLE) adp->email, NULL);
3473       AlignObjects (ALIGN_CENTER, (HANDLE) g1, (HANDLE) g2, NULL);
3474       Hide (g1);
3475       Hide (g2);
3476     } else {
3477       AlignObjects (ALIGN_RIGHT, (HANDLE) adp->affil,
3478                     (HANDLE) adp->address, (HANDLE) adp->city,
3479                     (HANDLE) adp->zip, (HANDLE) adp->country,
3480                     (HANDLE) adp->fax, (HANDLE) adp->email,
3481                     (HANDLE) adp->div, NULL);
3482     }
3483   }
3484 
3485   return (DialoG) p;
3486 }
3487 
3488 extern DialoG CreateAffilDialog (GrouP prnt, CharPtr title)
3489 
3490 {
3491   return CreateAnAffilDialog (prnt, title, FALSE, FALSE, FALSE, NULL, NULL);
3492 }
3493 
3494 extern DialoG CreatePublisherAffilDialog (GrouP prnt, CharPtr title)
3495 
3496 {
3497   return CreateAnAffilDialog (prnt, title, TRUE, FALSE, FALSE, NULL, NULL);
3498 }
3499 
3500 extern DialoG CreateProceedingsDialog (GrouP prnt, CharPtr title)
3501 
3502 {
3503   return CreateAnAffilDialog (prnt, title, FALSE, FALSE, TRUE, NULL, NULL);
3504 }
3505 
3506 extern DialoG CreateExtAffilDialog (GrouP prnt, CharPtr title, GrouP PNTR grp1, GrouP PNTR grp2)
3507 
3508 {
3509   return CreateAnAffilDialog (prnt, title, FALSE, TRUE, FALSE, grp1, grp2);
3510 }
3511 
3512 extern DialoG CreateExtPublisherAffilDialog (GrouP prnt, CharPtr title, GrouP PNTR grp1, GrouP PNTR grp2)
3513 
3514 {
3515   return CreateAnAffilDialog (prnt, title, TRUE, TRUE, FALSE, grp1, grp2);
3516 }
3517 
3518 extern DialoG CreateExtProceedingsDialog (GrouP prnt, CharPtr title, GrouP PNTR grp1, GrouP PNTR grp2)
3519 
3520 {
3521   return CreateAnAffilDialog (prnt, title, FALSE, TRUE, TRUE, grp1, grp2);
3522 }
3523 
3524 extern void DatePtrToVibrant (DatePtr dp, PopuP dateMonth, TexT dateDay, TexT dateYear)
3525 
3526 {
3527   Int2  day;
3528   Char  str [32];
3529   Int2  year;
3530 
3531   if (dp != NULL) {
3532     if (dp->data [0] == 0) {
3533       DatePrint (dp, str);
3534     } else if (dp->data [0] == 1) {
3535       SetEnumPopup (dateMonth, months_alist, (UIEnum) dp->data [2]);
3536       day = (Int2) dp->data [3];
3537       if (day > 0 && day <= 31) {
3538         sprintf (str, "%d", (int) day);
3539         SafeSetTitle (dateDay, str);
3540       } else {
3541         SafeSetTitle (dateDay, "");
3542       }
3543       year = (Int2) dp->data [1];
3544       if (year > 0) {
3545         sprintf (str, "%d", (int) (year + 1900));
3546         SafeSetTitle (dateYear, str);
3547       } else {
3548         SafeSetTitle (dateYear, "");
3549       }
3550     } else {
3551       Message (MSG_ERROR, "Unknown date type");
3552     }
3553   } else {
3554     SafeSetValue (dateMonth, 1);
3555     SafeSetTitle (dateDay, "");
3556     SafeSetTitle (dateYear, "");
3557   }
3558 }
3559 
3560 extern DatePtr VibrantToDatePtr (PopuP dateMonth, TexT dateDay, TexT dateYear)
3561 
3562 {
3563   Int2     day;
3564   Int2     dateType;
3565   DatePtr  dp;
3566   UIEnum   month;
3567   Char     str [32];
3568   Int2     year;
3569 
3570   dp = DateNew ();
3571   if (dp != NULL) {
3572     dateType = 1;
3573     dp->data [0] = (Uint1) dateType;
3574     if (dateType == 0) {
3575     } else if (dateType == 1) {
3576       GetTitle (dateYear, str, sizeof (str));
3577       if (! StringHasNoText (str)) {
3578         StrToInt (str, &year);
3579         if (year >= 1900) {
3580           dp->data [1] = (Uint1) (year - 1900);
3581         } else {
3582           /* dp->data [1] = 0; */
3583           dp = DateFree (dp);
3584           return dp;
3585         }
3586         if (GetEnumPopup (dateMonth, months_alist, &month)) {
3587           dp->data [2] = (Uint1) month;
3588         }
3589         GetTitle (dateDay, str, sizeof (str));
3590         StrToInt (str, &day);
3591         dp->data [3] = (Uint1) day;
3592       } else {
3593         dp = DateFree (dp);
3594       }
3595     } else {
3596       Message (MSG_ERROR, "Unknown date type");
3597     }
3598   }
3599   return dp;
3600 }
3601 
3602 static void GBQualPtrToQualsDialog (DialoG d, Pointer data)
3603 
3604 {
3605   ValNodePtr  head;
3606   Int2        j;
3607   size_t      len;
3608   GBQualPtr   list;
3609   CharPtr     str;
3610   TagListPtr  tlp;
3611   ValNodePtr  vnp;
3612 
3613   tlp = (TagListPtr) GetObjectExtra (d);
3614   list = (GBQualPtr) data;
3615   if (tlp != NULL) {
3616     head = NULL;
3617     while (list != NULL) {
3618       vnp = ValNodeNew (head);
3619       if (head == NULL) {
3620         head = vnp;
3621       }
3622       if (vnp != NULL) {
3623         if (list->qual != NULL && list->val != NULL) {
3624           len = StringLen (list->qual) + StringLen (list->val);
3625           str = MemNew (len + 4);
3626           if (str != NULL) {
3627             StringCpy (str, list->qual);
3628             StringCat (str, "\t");
3629             StringCat (str, list->val);
3630             StringCat (str, "\n");
3631           }
3632           vnp->data.ptrvalue = str;
3633         }
3634       }
3635       list = list->next;
3636     }
3637     SendMessageToDialog (tlp->dialog, VIB_MSG_RESET);
3638     tlp->vnp = head;
3639     SendMessageToDialog (tlp->dialog, VIB_MSG_REDRAW);
3640     for (j = 0, vnp = tlp->vnp; vnp != NULL; j++, vnp = vnp->next) {
3641     }
3642     tlp->max = MAX ((Int2) 0, (Int2) (j - tlp->rows + 1));
3643     CorrectBarMax (tlp->bar, tlp->max);
3644     CorrectBarPage (tlp->bar, tlp->rows - 1, tlp->rows - 1);
3645   }
3646 }
3647 
3648 static Pointer QualsDialogToGBQualPtr (DialoG d)
3649 
3650 {
3651   Char         ch;
3652   GBQualPtr    gbq;
3653   GBQualPtr    gbqlast;
3654   GBQualPtr    head;
3655   Int2         j;
3656   Int2         len;
3657   Boolean      okay;
3658   CharPtr      str;
3659   TagListPtr   tlp;
3660   ValNodePtr   vnp;
3661 
3662   head = NULL;
3663   tlp = (TagListPtr) GetObjectExtra (d);
3664   if (tlp != NULL && tlp->vnp != NULL) {
3665     gbq = NULL;
3666     gbqlast = NULL;
3667     for (vnp = tlp->vnp; vnp != NULL; vnp = vnp->next) {
3668       str = (CharPtr) vnp->data.ptrvalue;
3669       okay = FALSE;
3670       len = StringLen (str);
3671       for (j = 0; j < len; j++) {
3672         ch = str [j];
3673         if (ch != ' ' && ch != '\t' && ch != '\n') {
3674           okay = TRUE;
3675         }
3676       }
3677       if (okay) {
3678         gbq = GBQualNew ();
3679         if (gbqlast == NULL) {
3680           head = gbq;
3681         } else {
3682           gbqlast->next = gbq;
3683         }
3684         gbqlast = gbq;
3685         if (gbq != NULL) {
3686           gbq->qual = ExtractTagListColumn ((CharPtr) vnp->data.ptrvalue, 0);
3687           gbq->val = ExtractTagListColumn ((CharPtr) vnp->data.ptrvalue, 1);
3688         }
3689       }
3690     }
3691   }
3692   return (Pointer) head;
3693 }
3694 
3695 Uint2 gbqual_types [] = {
3696   TAGLIST_TEXT, TAGLIST_TEXT
3697 };
3698 
3699 Uint2 gbqual_widths [] = {
3700   0, 0, 0, 0
3701 };
3702 
3703 extern DialoG CreateQualsDialog (GrouP h, Uint2 rows, Int2 spacing,
3704                                  Int2 width1, Int2 width2);
3705 extern DialoG CreateQualsDialog (GrouP h, Uint2 rows, Int2 spacing,
3706                                  Int2 width1, Int2 width2)
3707 
3708 {
3709   gbqual_widths [0] = width1;
3710   gbqual_widths [1] = width2;
3711   return CreateTagListDialog (h, rows, 2, spacing,
3712                               gbqual_types, gbqual_widths, NULL,
3713                               GBQualPtrToQualsDialog,
3714                               QualsDialogToGBQualPtr);
3715 }
3716 
3717 extern void CreateSeqAlignLabel (SeqAlignPtr salp, CharPtr buf, Int4 buf_size)
3718 {
3719   Int4     remaining_len, aln_pos, id_len;
3720   SeqIdPtr sip;
3721   CharPtr  buf_ptr;
3722   
3723   if (buf == NULL || buf_size < 1)
3724   {
3725     return;
3726   }
3727   MemSet (buf, 0, buf_size);
3728   if (salp == NULL)
3729   {
3730     return;
3731   }
3732   remaining_len = buf_size - 1;
3733   buf_ptr = buf;
3734   StringNCat (buf_ptr, "aln|", remaining_len);
3735   remaining_len -= 4;
3736   
3737   if (remaining_len <= 3)
3738   {
3739     return;
3740   }
3741   buf_ptr += 4;
3742   
3743   for (aln_pos = 1; aln_pos <= salp->dim && remaining_len > 2; aln_pos++)
3744   {
3745     sip = AlnMgr2GetNthSeqIdPtr(salp, aln_pos);
3746     SeqIdWrite (sip, buf_ptr, PRINTID_REPORT, remaining_len);
3747     id_len = StringLen (buf_ptr);
3748     remaining_len -= id_len;
3749     buf_ptr += id_len;
3750     /* put comma between IDs in list */
3751     if (aln_pos < salp->dim && remaining_len > 2)
3752     {
3753       StringCat (buf_ptr, ",");
3754       remaining_len -= 1;
3755       buf_ptr += 1;
3756     }
3757   }
3758   
3759   /* add ellipsis to indicate unshown IDs */
3760   if (remaining_len == 0 && aln_pos < salp->dim)
3761   {
3762     buf_ptr -= 3;
3763     StringCat (buf_ptr, "...");
3764   }
3765 }
3766 
3767 
3768 static Boolean HasDisqualifyingUserObjects(SeqAnnotPtr sap)
3769 {
3770   AnnotDescrPtr desc;
3771   UserObjectPtr uop;
3772   ObjectIdPtr   oip;
3773   
3774   if (sap == NULL) {
3775     return FALSE;
3776   }
3777   
3778   desc = sap->desc;
3779   while (desc != NULL) {
3780     if (desc->choice == Annot_descr_user) {
3781       uop = (UserObjectPtr) desc->data.ptrvalue;
3782       while(uop) {
3783         if(uop->type) {
3784           oip = uop->type;
3785           if(StringCmp(oip->str, "Hist Seqalign") == 0
3786              || StringCmp (oip->str, "Blast Type") == 0) {
3787             return TRUE;
3788           }
3789         }
3790         uop = uop->next;
3791       }
3792     }
3793     desc = desc->next;
3794   }
3795   return FALSE;
3796 }
3797 
3798 
3799 extern void GetAlignmentsInSeqEntryCallback (SeqAnnotPtr sap, Pointer userdata)
3800 {
3801   SeqAlignPtr PNTR salp_list;
3802   SeqAlignPtr salp, last_salp;
3803   
3804   if (sap == NULL || sap->type != 2 || userdata == NULL || HasDisqualifyingUserObjects(sap)) 
3805   {
3806     return;
3807   }
3808   salp_list = (SeqAlignPtr PNTR) userdata;
3809   salp = (SeqAlignPtr) sap->data;
3810   if (salp == NULL) return;
3811   salp = SeqAlignListDup(salp);
3812   AlnMgr2IndexSeqAlign(salp);
3813   if (*salp_list == NULL)
3814   {
3815     *salp_list = salp; 
3816   }
3817   else
3818   {
3819     last_salp = *salp_list;
3820     while (last_salp->next != NULL)
3821     {
3822       last_salp = last_salp->next;
3823     }
3824     last_salp->next = salp;
3825   }
3826 }
3827 
3828 typedef struct intervalpage {
3829   DIALOG_MESSAGE_BLOCK
3830   DialoG             ivals;
3831   ButtoN             partial5;
3832   ButtoN             partial3;
3833   ButtoN             nullsBetween;
3834   Int2               count;
3835   SeqIdPtr           PNTR sip_list;
3836   EnumFieldAssoc     PNTR alist;
3837   EnumFieldAssocPtr  alists [5];
3838   Int4               PNTR lengths;
3839   Boolean            nucsOK;
3840   Boolean            protsOK;
3841   Boolean            showIdTags;
3842   FeatureFormPtr     ffp;
3843   IntEdPartialProc   proc;
3844   
3845   Int4               strand_col;  /* column for selecting strand.
3846                                    * 2 if nucsOK, -1 otherwise
3847                                    */
3848   Int4               seqid_col;   /* column for entering SeqIds,
3849                                    * which could be in a different place 
3850                                    * depending on nucsOK or -1 if not show_seqid
3851                                    */
3852   Int4               aln_col;     /* column for selecting alignments,
3853                                    * could be in a different place if not show_seqid
3854                                    * or -1 if not use_aln
3855                                    */
3856   Boolean            show_seqids; /* false when entering coordinates for an entire
3857                                    * alignments, true otherwise
3858                                    */
3859   
3860   /* for editing alignment intervals */
3861   SeqAlignPtr        PNTR salp_list; 
3862   TaglistCallback    PNTR callbacks;
3863   EnumFieldAssoc     PNTR aln_alist;
3864   Int2               aln_count;
3865   Int4               PNTR aln_lengths;
3866   Boolean            allow_nulls_in_list; /* some sequences may be all gap
3867                                            * in the specified interval -
3868                                            * if this value is FALSE, return
3869                                            * a NULL for the entire list,
3870                                            * otherwise allow NULLs to appear
3871                                            * in the list.
3872                                            */
3873   
3874 } IntervalPage, PNTR IntervalPagePtr;
3875 
3876 #define NUM_IVAL_ROWS  7
3877 #define EXTRA_HEIGHT   2
3878 
3879 static void ClearBspScratch (BioseqPtr bsp, Pointer userdata)
3880 
3881 {
3882   if (bsp == NULL) return;
3883   bsp->idx.scratch = NULL;
3884 }
3885 
3886 static void AddToSipList (IntervalPagePtr ipp, BioseqPtr bsp)
3887 
3888 {
3889   /*
3890   Int2      j;
3891   */
3892   SeqIdPtr  sip;
3893 
3894   if (ipp == NULL || bsp == NULL) return;
3895   if (bsp->idx.scratch != NULL) return;
3896   sip = SeqIdFindBest (bsp->id, 0);
3897   if (sip == NULL) return;
3898   /*
3899   for (j = 1; j <= ipp->count; j++) {
3900     if (SeqIdComp (sip, ipp->sip_list [j]) == SIC_YES) return;
3901   }
3902   */
3903   ipp->count++;
3904   ipp->sip_list [ipp->count] = SeqIdDup (sip);
3905   bsp->idx.scratch = (Pointer) bsp;
3906 }
3907 
3908 static void FillInProducts (SeqEntryPtr sep, Pointer mydata,
3909                             Int4 index, Int2 indent)
3910 
3911 {
3912   BioseqPtr        bsp;
3913   IntervalPagePtr  ipp;
3914   SeqIdPtr         sip;
3915   SeqLocPtr        slp;
3916   ValNode          vn;
3917 
3918   if (sep != NULL && mydata != NULL && sep->choice == 1) {
3919     ipp = (IntervalPagePtr) mydata;
3920     bsp = (BioseqPtr) sep->data.ptrvalue;
3921     if (bsp != NULL) {
3922       if ((ipp->nucsOK && ISA_na (bsp->mol)) ||
3923           (ipp->protsOK && ISA_aa (bsp->mol))) {
3924         AddToSipList (ipp, bsp);
3925       }
3926       if (bsp->repr == Seq_repr_seg && bsp->seq_ext != NULL) {
3927         vn.choice = SEQLOC_MIX;
3928         vn.next = NULL;
3929         vn.data.ptrvalue = bsp->seq_ext;
3930         slp = SeqLocFindNext (&vn, NULL);
3931         while (slp != NULL) {
3932           sip = SeqLocId (slp);
3933           if (sip != NULL) {
3934             bsp = BioseqFindCore (sip);
3935             if (bsp != NULL) {
3936               AddToSipList (ipp, bsp);
3937             } else {
3938               bsp = BioseqLockById (sip);
3939               if (bsp != NULL) {
3940                 AddToSipList (ipp, bsp);
3941                 BioseqUnlock (bsp);
3942               }
3943             }
3944           }
3945           slp = SeqLocFindNext (&vn, slp);
3946         }
3947       }
3948     }
3949   }
3950 }
3951 
3952 static Int4 SegmentedEntryList (SeqEntryPtr sep, Pointer mydata,
3953                                 SeqEntryFunc mycallback,
3954                                 Int4 index, Int2 indent)
3955 
3956 {
3957   BioseqPtr     bsp;
3958   BioseqSetPtr  bssp;
3959   SeqLocPtr     slp;
3960   ValNode       vn;
3961 
3962   if (sep == NULL) return index;
3963   if (IS_Bioseq (sep)) {
3964     if (mycallback != NULL)
3965       (*mycallback) (sep, mydata, index, indent);
3966     bsp = (BioseqPtr) sep->data.ptrvalue;
3967     if (bsp != NULL && bsp->repr == Seq_repr_seg && bsp->seq_ext != NULL) {
3968       vn.choice = SEQLOC_MIX;
3969       vn.next = NULL;
3970       vn.data.ptrvalue = bsp->seq_ext;
3971       slp = SeqLocFindNext (&vn, NULL);
3972       while (slp != NULL) {
3973         index++;
3974         slp = SeqLocFindNext (&vn, slp);
3975       }
3976     }
3977     return index + 1;
3978   }
3979   /*
3980   if (Bioseq_set_class (sep) == 4) return index;
3981   index++;
3982   */
3983   bssp = (BioseqSetPtr) sep->data.ptrvalue;
3984   sep = bssp->seq_set;
3985   indent++;
3986   while (sep != NULL) {
3987     index = SegmentedEntryList (sep, mydata, mycallback, index, indent);
3988     sep = sep->next;
3989   }
3990   return index;
3991 }
3992 
3993 #define SegmentedEntryCount( a )  SegmentedEntryList( a ,NULL,NULL,0,0);
3994 
3995 static ENUM_ALIST(strand_alist)
3996 {" ",             Seq_strand_unknown},  /* 0 */
3997 {"Plus",          Seq_strand_plus},     /* 1 */
3998 {"Minus",         Seq_strand_minus},    /* 2 */
3999 /*
4000 {"Both",          Seq_strand_both},
4001 {"Reverse",       Seq_strand_both_rev},
4002 */
4003 {"Other",         Seq_strand_other},    /* 255 */
4004 END_ENUM_ALIST
4005 
4006 static Boolean IsSeqIdInValNodeList (SeqIdPtr sip, ValNodePtr list)
4007 {
4008   if (sip == NULL)
4009   {
4010     return FALSE;
4011   }
4012   while (list != NULL)
4013   {
4014     if (SeqIdComp (sip, list->data.ptrvalue) == SIC_YES)
4015     {
4016       return TRUE;
4017     }
4018     list = list->next;
4019   }
4020   return FALSE;
4021 }
4022 
4023 static Int4 FindLastStopForSeqId (SeqLocPtr slp, SeqIdPtr sip)
4024 {
4025   Int4      last_stop = 0, new_stop;
4026   SeqLocPtr tmp_slp;
4027   SeqIdPtr  tmp_sip;
4028   
4029   tmp_slp = SeqLocFindNext (slp, NULL);
4030   while (tmp_slp != NULL)
4031   {
4032     tmp_sip = SeqLocId (tmp_slp);
4033     if (SeqIdComp (sip, tmp_sip) == SIC_YES)
4034     {
4035       new_stop = SeqLocStop (slp);
4036       if (new_stop > last_stop)
4037       {
4038         last_stop = new_stop;
4039       }
4040     }
4041     tmp_slp = SeqLocFindNext (slp, tmp_slp);
4042   }
4043   return last_stop;
4044 }
4045 
4046 extern void UpdateTagListPopupChoices (DialoG d, Int4 column);
4047 
4048 /* We need to make sure that all IDs in the location are found in the enum list
4049  * for the interval editor ID Enum */
4050 static void CorrectIntervalEditorSeqIdEnum (IntervalPagePtr ipp, SeqLocPtr slp)
4051 {
4052   SeqLocPtr           tmp_slp;
4053   SeqIdPtr            sip;
4054   Int4                j = 0;
4055   Boolean             found;
4056   ValNodePtr          missing_list = NULL, missing_vnp;
4057   Int4                new_count;
4058   SeqIdPtr PNTR       new_sip_list;
4059   EnumFieldAssoc PNTR new_alist;
4060   Int4 PNTR           new_lengths;
4061   BioseqPtr           bsp;
4062   Char                str [128];
4063   CharPtr             ptr;
4064   
4065   if (ipp == NULL || slp == NULL)
4066   {
4067     return;
4068   }
4069   
4070   tmp_slp = SeqLocFindNext (slp, NULL);
4071   while (tmp_slp != NULL)
4072   {
4073     sip = SeqLocId (tmp_slp);
4074     if (!IsSeqIdInValNodeList (sip, missing_list))
4075     {
4076       found = FALSE;
4077       for (j = 1; j <= ipp->count && !found; j++)
4078       {
4079         if (SeqIdComp (sip, ipp->sip_list [j]) == SIC_YES)
4080         {
4081           found = TRUE;
4082         }
4083       }
4084       /* this process takes longer, so don't combine it with the above
4085        * loop
4086        */
4087       if (!found)
4088       {
4089         for (j = 1; j <= ipp->count && !found; j++)
4090         {
4091           bsp = BioseqFindCore (ipp->sip_list [j]);
4092           if (bsp == NULL) {
4093             bsp = BioseqLockById (ipp->sip_list [j]);
4094             BioseqUnlock (bsp);
4095           }
4096           if (bsp != NULL && SeqIdIn (sip, bsp->id))
4097           {
4098             found = TRUE;
4099           }
4100         }
4101       }
4102       if (!found)
4103       {
4104         ValNodeAddPointer (&missing_list, 0, sip);
4105       }
4106     }
4107     
4108     tmp_slp = SeqLocFindNext (slp, tmp_slp);
4109   }
4110   
4111   if (missing_list != NULL)
4112   {
4113     new_count = ipp->count + 4 + ValNodeLen (missing_list);
4114     new_sip_list = MemNew (sizeof (SeqIdPtr) * (size_t) new_count);
4115     new_alist = MemNew (sizeof (EnumFieldAssoc) * (size_t) new_count);
4116     new_lengths = MemNew (sizeof (Int4) * (size_t) new_count);
4117     if (ipp->sip_list != NULL) {
4118       /* first one is blank, remainder are actual data */
4119       for (j = 0; j < ipp->count + 1; j++)
4120       {
4121         new_sip_list [j] = ipp->sip_list [j];
4122         ipp->sip_list [j] = NULL;
4123         new_alist [j].name = ipp->alist [j].name;
4124         ipp->alist [j].name = NULL;
4125         new_alist [j].value = ipp->alist [j].value;
4126         new_lengths [j] = ipp->lengths [j];
4127       }
4128     }
4129     
4130     missing_vnp = missing_list;
4131     while (j < new_count - 1 && missing_vnp != NULL)
4132     {
4133       new_sip_list [j] = SeqIdDup (missing_vnp->data.ptrvalue);
4134       new_alist [j].value = j;
4135       
4136       sip = new_sip_list [j];
4137       bsp = BioseqFindCore (sip);
4138       if (bsp == NULL) {
4139         bsp = BioseqLockById (sip);
4140         BioseqUnlock (bsp);
4141       }
4142       if (bsp != NULL)
4143       {
4144         new_lengths [j] = bsp->length;
4145         sip = SeqIdFindWorst (bsp->id);
4146       }
4147       else
4148       {
4149         new_lengths [j] = FindLastStopForSeqId (slp, sip);
4150       }
4151       SeqIdWrite (sip, str, PRINTID_REPORT, sizeof (str));
4152       ptr = StringChr (str, '|');
4153       if (ptr == NULL) {
4154         ptr = str;
4155       } else {
4156         ptr++;
4157       }
4158       new_alist [j].name = StringSave (ptr);
4159       missing_vnp = missing_vnp->next;
4160       j++;
4161     }
4162     /* set terminator for enum list */
4163     new_alist [j].name = NULL;
4164     new_alist [j].value = (UIEnum) 0;
4165 
4166     ipp->sip_list = MemFree (ipp->sip_list);
4167     ipp->sip_list = new_sip_list;
4168     ipp->alist = MemFree (ipp->alist);
4169     ipp->alist = new_alist;
4170     ipp->lengths = MemFree (ipp->lengths);
4171     ipp->lengths = new_lengths;
4172     ipp->count = j;
4173     
4174     ipp->alists [ipp->seqid_col] = ipp->alist;
4175     UpdateTagListPopupChoices (ipp->ivals, ipp->seqid_col);
4176   }
4177   missing_list = ValNodeFree (missing_list);
4178 }
4179 
4180 static void 
4181 BuildIntervalString 
4182 (IntervalPagePtr ipp,
4183  CharPtr         fuzz_from_ch,
4184  Int4            start,
4185  CharPtr         fuzz_to_ch,
4186  Int4            stop,
4187  Uint2           strand,
4188  Int4            seq,
4189  Int4            salp_num,
4190  Boolean         isInterval,
4191  Boolean         isPoint,
4192  CharPtr         buf,
4193  Int4            buf_len)
4194 {
4195   CharPtr cp;
4196   Boolean need_tab = FALSE;
4197   
4198   if (ipp == NULL || fuzz_from_ch == NULL || fuzz_to_ch == NULL || buf == NULL || buf_len == 0)
4199   {
4200     return;
4201   }
4202   
4203   MemSet (buf, 0, buf_len);  
4204   
4205   if (isInterval)
4206   {
4207     sprintf (buf, "%s%ld\t%s%ld",
4208              fuzz_from_ch, (long) (start + 1),
4209              fuzz_to_ch, (long) (stop + 1));
4210     need_tab = TRUE;
4211   }
4212   else if (isPoint)
4213   {
4214     sprintf (buf, "%ld%s\t%ld%s",
4215              (long) (start + 1), fuzz_from_ch,
4216              (long) (stop + 1), fuzz_to_ch);
4217     need_tab = TRUE;
4218   }
4219   
4220   cp = buf + StringLen (buf);
4221   if (ipp->strand_col > -1)
4222   {
4223     if (need_tab)
4224     {
4225       StringCat (cp, "\t");
4226       cp++;
4227     }
4228     sprintf (cp, "%d", (int) strand);
4229     need_tab = TRUE;
4230     cp += StringLen (cp);
4231   }
4232   
4233   if (ipp->seqid_col > -1)
4234   {
4235     if (need_tab)
4236     {
4237       StringCat (cp, "\t");
4238       cp++;
4239     }
4240     sprintf (cp, "%d", (int) seq);
4241     need_tab = TRUE;
4242     cp += StringLen (cp);
4243   }
4244   
4245   if (ipp->aln_col > -1)
4246   {
4247     if (need_tab)
4248     {
4249       StringCat (cp, "\t");
4250       cp++;
4251     }
4252     sprintf (cp, "%d", (int) salp_num);
4253     need_tab = TRUE;
4254     cp += StringLen (cp);
4255   }
4256   StringCat (cp, "\n");
4257 }
4258 
4259 static void SeqLocPtrToIntervalPage (DialoG d, Pointer data)
4260 
4261 {
4262   BioseqPtr        bsp;
4263   SeqLocPtr        firstSlp;
4264   Char             fuzz_from_ch [4];
4265   Char             fuzz_to_ch [4];
4266   ValNodePtr       head;
4267   SeqIdPtr         id;
4268   IntFuzzPtr       ifp;
4269   IntervalPagePtr  ipp;
4270   Boolean          isInterval;
4271   Boolean          isPoint;
4272   Int2             j;
4273   SeqLocPtr        lastSlp;
4274   SeqLocPtr        location;
4275   SeqLocPtr        next;
4276   SeqEntryPtr      oldscope;
4277   Boolean          partial5;
4278   Boolean          partial3;
4279   Int2             seq;
4280   SeqIntPtr        sip;
4281   SeqLocPtr        slp;
4282   SeqPntPtr        spp;
4283   Int4             start;
4284   Int4             stop;
4285   Char             str [255];
4286   Uint1            strand, aln_strand;
4287   TagListPtr       tlp;
4288   ValNodePtr       vnp;
4289   Int4             salp_num, salp_row;
4290   SeqIdPtr         tmp_sip, bsp_id;
4291 
4292   ipp = (IntervalPagePtr) GetObjectExtra (d);
4293   if (ipp == NULL) return;
4294   SafeSetStatus (ipp->partial5, FALSE);
4295   SafeSetStatus (ipp->partial3, FALSE);
4296   tlp = GetObjectExtra (ipp->ivals);
4297   if (tlp == NULL) return;
4298 
4299   location = (SeqLocPtr) data;
4300   partial5 = FALSE;
4301   partial3 = FALSE;
4302   head = NULL;
4303 
4304   if (location == NULL)
4305   {
4306       if (ipp->count == 1)
4307       {
4308        sprintf (str, "\t\t\t1\n");
4309        vnp = ValNodeNew (head);
4310        if (head == NULL) {
4311          head = vnp;
4312        }
4313        if (vnp != NULL) {
4314          vnp->data.ptrvalue = StringSave (str);
4315        }    
4316       }
4317   }
4318   if (location != NULL) {
4319     CorrectIntervalEditorSeqIdEnum (ipp, location);
4320     firstSlp = NULL;
4321     lastSlp = NULL;
4322     slp = SeqLocFindNext (location, NULL);
4323     while (slp != NULL) {
4324       if (firstSlp == NULL) {
4325         firstSlp = slp;
4326       }
4327       lastSlp = slp;
4328       next = SeqLocFindNext (location, slp);
4329       if (slp->choice == SEQLOC_NULL) {
4330         SafeSetStatus (ipp->nullsBetween, TRUE);
4331         SafeShow (ipp->nullsBetween);
4332       } else {
4333         id = SeqLocId (slp);
4334         if (id != NULL) {
4335           bsp = BioseqFind (id);
4336           if (bsp == NULL) {
4337             oldscope = SeqEntrySetScope (NULL);
4338             if (oldscope != NULL) {
4339               bsp = BioseqFind (id);
4340               SeqEntrySetScope (oldscope);
4341             }
4342           }
4343           isInterval = TRUE;
4344           isPoint = FALSE;
4345           StringCpy (fuzz_from_ch, "");
4346           StringCpy (fuzz_to_ch, "");
4347           if (bsp == NULL) {
4348             start = SeqLocStart (slp);
4349             stop = SeqLocStop (slp);
4350           }
4351           else
4352           {
4353             start = GetOffsetInBioseq (slp, bsp, SEQLOC_START);
4354             stop = GetOffsetInBioseq (slp, bsp, SEQLOC_STOP);
4355           }
4356           if (start == stop && slp->choice == SEQLOC_PNT) {
4357             spp = (SeqPntPtr) slp->data.ptrvalue;
4358             if (spp != NULL) {
4359               ifp = spp->fuzz;
4360               if (ifp != NULL && ifp->choice == 4 && ifp->a ==  3) {
4361                 isInterval = FALSE;
4362                 isPoint = TRUE;
4363                 StringCpy (fuzz_from_ch, "^");
4364                 /* start--; */  /* compensate for other fix */
4365                 stop++; /* compensate for other fix */
4366               }
4367             }
4368           }
4369           strand = SeqLocStrand (slp);
4370           if (strand > Seq_strand_both_rev && strand != Seq_strand_other) {
4371             strand = Seq_strand_unknown;
4372           }
4373           /*
4374           if (strand == Seq_strand_unknown) {
4375             strand = Seq_strand_plus;
4376           }
4377           */
4378           if (! ipp->nucsOK) {
4379             strand = 0;
4380           }
4381           seq = 0;
4382           if (ipp->sip_list != NULL && bsp != NULL) {
4383             for (j = 1; j <= ipp->count && seq == 0; j++) {
4384               if (SeqIdComp (SeqIdFindBest (bsp->id, 0), ipp->sip_list[j]) == SIC_YES) {
4385                 seq = j;
4386               }
4387             }
4388           }
4389           
4390           salp_num = 0;
4391           salp_row = 0;
4392           if (seq > 0 && ipp->salp_list != NULL)
4393           {
4394             salp_num = 1;
4395             while (salp_num <= ipp->aln_count
4396                    && salp_row == 0)
4397             {
4398               tmp_sip = SeqIdPtrFromSeqAlign (ipp->salp_list [salp_num]);
4399               bsp = BioseqFind (ipp->sip_list [seq]);
4400               if (bsp != NULL)
4401               {
4402                 for (bsp_id = bsp->id;
4403                      bsp_id != NULL && salp_row == 0; 
4404                      bsp_id = bsp_id->next)
4405                 {
4406                   salp_row = SeqIdOrderInBioseqIdList(bsp_id, tmp_sip);
4407                 }
4408               }
4409               if (salp_row < 1)
4410               {
4411                 salp_num++;
4412               }
4413             }
4414             if (salp_row < 1)
4415             {
4416               salp_num = 0;
4417             }
4418             else
4419             {
4420               start = AlnMgr2MapBioseqToSeqAlign(ipp->salp_list [salp_num], start, salp_row);
4421               stop = AlnMgr2MapBioseqToSeqAlign(ipp->salp_list [salp_num], stop, salp_row);
4422               aln_strand = AlnMgr2GetNthStrand (ipp->salp_list [salp_num], salp_row);
4423               /* if reverse strand in alignment, reverse strand for location */
4424               if (aln_strand == Seq_strand_minus)
4425               {
4426                 if (strand == Seq_strand_minus)
4427                 {
4428                   strand = Seq_strand_plus;
4429                 }
4430                 else if (strand == Seq_strand_plus)
4431                 {
4432                   strand = Seq_strand_minus;
4433                 }
4434               }
4435             }
4436           }
4437           
4438           BuildIntervalString (ipp, fuzz_from_ch, start, fuzz_to_ch, stop,
4439                                strand, seq, salp_num, isInterval, isPoint,
4440                                str, sizeof (str));
4441           vnp = ValNodeNew (head);
4442           if (head == NULL) {
4443             head = vnp;
4444           }
4445           if (vnp != NULL) {
4446             vnp->data.ptrvalue = StringSave (str);
4447           }
4448         }
4449       }
4450       slp = next;
4451     }
4452     if (firstSlp != NULL) {
4453       if (firstSlp->choice == SEQLOC_INT && firstSlp->data.ptrvalue != NULL) {
4454         sip = (SeqIntPtr) firstSlp->data.ptrvalue;
4455         if (sip->strand == Seq_strand_minus || sip->strand == Seq_strand_both_rev) {
4456           ifp = sip->if_to;
4457           if (ifp != NULL && ifp->choice == 4 && ifp->a == 1) {
4458             partial5 = TRUE;
4459           }
4460         } else {
4461           ifp = sip->if_from;
4462           if (ifp != NULL && ifp->choice == 4 && ifp->a == 2) {
4463             partial5 = TRUE;
4464           }
4465         }
4466       } else if (firstSlp->choice == SEQLOC_PNT && firstSlp->data.ptrvalue != NULL) {
4467         spp = (SeqPntPtr) firstSlp->data.ptrvalue;
4468         if (spp->strand == Seq_strand_minus || spp->strand == Seq_strand_both_rev) {
4469           ifp = spp->fuzz;
4470           if (ifp != NULL && ifp->choice == 4 && ifp->a == 1) {
4471             partial5 = TRUE;
4472           }
4473         } else {
4474           ifp = spp->fuzz;
4475           if (ifp != NULL && ifp->choice == 4 && ifp->a == 2) {
4476             partial5 = TRUE;
4477           }
4478         }
4479       }
4480     }
4481     if (lastSlp != NULL) {
4482       if (lastSlp->choice == SEQLOC_INT && lastSlp->data.ptrvalue != NULL) {
4483         sip = (SeqIntPtr) lastSlp->data.ptrvalue;
4484         if (sip->strand == Seq_strand_minus || sip->strand == Seq_strand_both_rev) {
4485           ifp = sip->if_from;
4486           if (ifp != NULL && ifp->choice == 4 && ifp->a == 2) {
4487             partial3 = TRUE;
4488           }
4489         } else {
4490           ifp = sip->if_to;
4491           if (ifp != NULL && ifp->choice == 4 && ifp->a == 1) {
4492             partial3 = TRUE;
4493           }
4494         }
4495       } else if (lastSlp->choice == SEQLOC_PNT && lastSlp->data.ptrvalue != NULL) {
4496         spp = (SeqPntPtr) lastSlp->data.ptrvalue;
4497         if (spp->strand == Seq_strand_minus || spp->strand == Seq_strand_both_rev) {
4498           ifp = spp->fuzz;
4499           if (ifp != NULL && ifp->choice == 4 && ifp->a == 2) {
4500             partial3 = TRUE;
4501           }
4502         } else {
4503           ifp = spp->fuzz;
4504           if (ifp != NULL && ifp->choice == 4 && ifp->a == 1) {
4505             partial3 = TRUE;
4506           }
4507         }
4508       }
4509     }
4510   }
4511   SafeSetStatus (ipp->partial5, partial5);
4512   SafeSetStatus (ipp->partial3, partial3);
4513 
4514   SendMessageToDialog (tlp->dialog, VIB_MSG_RESET);
4515   tlp->vnp = head;
4516   SendMessageToDialog (tlp->dialog, VIB_MSG_REDRAW);
4517   for (j = 0, vnp = tlp->vnp; vnp != NULL; j++, vnp = vnp->next) {
4518   }
4519   tlp->max = MAX ((Int2) 0, (Int2) (j - tlp->rows + 1));
4520   CorrectBarMax (tlp->bar, tlp->max);
4521   CorrectBarPage (tlp->bar, tlp->rows - 1, tlp->rows - 1);
4522 }
4523 
4524 extern void SetSequenceAndStrandForIntervalPage (DialoG d)
4525 {
4526   SendMessageToDialog (d, NUM_VIB_MSG + 1);
4527 }
4528 
4529 static Boolean 
4530 ReadFromValueFromDialogLine 
4531 (CharPtr line,
4532  Int4Ptr p_from_val,
4533  BoolPtr fuzz_after)
4534 {
4535   CharPtr txt, ptr;
4536   Int4    len;
4537   Boolean okay;
4538   Int4    val;
4539   Int4    j;
4540   Char    ch;
4541   
4542   if (p_from_val == NULL || fuzz_after == NULL || StringHasNoText (line))
4543   {
4544     return FALSE;
4545   }
4546 
4547   txt = ExtractTagListColumn (line, 0);
4548   okay = FALSE;
4549   len = StringLen (txt);
4550   for (j = 0; j < len; j++) {
4551     ch = txt [j];
4552     if (ch != ' ' && ch != '\t' && ch != '\n') {
4553       okay = TRUE;
4554     }
4555   }
4556   if (okay) {
4557     *fuzz_after = FALSE;
4558     *p_from_val = 0;
4559 
4560     ptr = StringChr (txt, '<');
4561     if (ptr != NULL) {
4562       *ptr = ' ';
4563     }
4564     ptr = StringChr (txt, '>');
4565     if (ptr != NULL) {
4566       *ptr = ' ';
4567     }
4568     ptr = StringChr (txt, '^');
4569     if (ptr != NULL) {
4570       *fuzz_after = TRUE;
4571       *ptr = ' ';
4572     }
4573     TrimSpacesAroundString (txt);
4574     if (StrToLong (txt, &val)) {
4575       *p_from_val = val;
4576     } else {
4577       okay = FALSE;
4578     }
4579   } else {
4580     okay = FALSE;
4581   }
4582   MemFree (txt);
4583   return okay;
4584 }
4585 
4586 static Boolean 
4587 ReadToValueFromDialogLine 
4588 (CharPtr line,
4589  Int4Ptr p_to_val,
4590  Int4Ptr p_from_val,
4591  BoolPtr fuzz_after,
4592  BoolPtr isInterval,
4593  BoolPtr isPoint,
4594  Boolean partial5,
4595  Boolean partial3)
4596 {
4597   CharPtr txt, ptr;
4598   Int4    val, tmp;
4599   Boolean okay = TRUE;
4600   
4601   if (p_to_val == NULL || p_from_val == NULL || fuzz_after == NULL
4602       || isInterval == NULL || isPoint == NULL)
4603   {
4604     return FALSE;
4605   }
4606   
4607   *p_to_val = 0;
4608   
4609   txt = ExtractTagListColumn (line, 1);
4610   if (! StringHasNoText (txt)) {
4611     ptr = StringChr (txt, '<');
4612     if (ptr != NULL) {
4613       *ptr = ' ';
4614     }
4615     ptr = StringChr (txt, '>');
4616     if (ptr != NULL) {
4617       *ptr = ' ';
4618     }
4619     ptr = StringChr (txt, '^');
4620     if (ptr != NULL) {
4621       *fuzz_after = TRUE;
4622       *ptr = ' ';
4623     }
4624     TrimSpacesAroundString (txt);
4625     if (StrToLong (txt, &val)) {
4626       *p_to_val = val;
4627       if (*fuzz_after && *p_to_val == *p_from_val + 1) {
4628         *isInterval = FALSE;
4629         *isPoint = TRUE;
4630         /* from++; */ /* this was causing point to be thrown off */
4631       } else if (*p_to_val == *p_from_val && (! partial5) && (! partial3)) {
4632         *isInterval = FALSE;
4633         *isPoint = TRUE;
4634       }
4635     } else {
4636       okay = FALSE;
4637     }
4638   } else {
4639     /*
4640     okay = FALSE;
4641     */
4642     *isInterval = FALSE;
4643     *isPoint = TRUE;
4644     *p_to_val = *p_from_val;
4645   }
4646   MemFree (txt);
4647   
4648   if (okay && *isInterval) {
4649     if (*p_from_val > *p_to_val) {
4650       tmp = *p_from_val;
4651       *p_from_val = *p_to_val;
4652       *p_to_val = tmp;
4653     }
4654   }
4655   
4656 
4657   return okay;
4658 }
4659 
4660 static Boolean 
4661 ReadStrandFromDialogLine 
4662 (CharPtr  line,
4663  Int4     strand_col,
4664  Uint2Ptr p_strand_val,
4665  Uint2Ptr p_prev_strand_val)
4666 {
4667   CharPtr txt;
4668   Int2    val2;
4669   
4670   if (p_strand_val == NULL
4671       || p_prev_strand_val == NULL)
4672   {
4673     return FALSE;
4674   }
4675   
4676   *p_strand_val = Seq_strand_unknown;
4677   if (strand_col > -1) {
4678     txt = ExtractTagListColumn (line, strand_col);
4679     if (txt != NULL && StrToInt (txt, &val2)) {
4680       *p_strand_val = val2;
4681       if (*p_strand_val > Seq_strand_both_rev) {
4682         *p_strand_val = Seq_strand_other;
4683       }
4684       *p_prev_strand_val = *p_strand_val;
4685     } else {
4686       *p_strand_val = *p_prev_strand_val;
4687     }
4688     MemFree (txt);
4689   }
4690   if (*p_strand_val == Seq_strand_unknown) {
4691     *p_strand_val = Seq_strand_plus;
4692   }
4693   return TRUE;
4694 }
4695 
4696 static Boolean 
4697 ReadSeqIdFromDialogLine 
4698 (CharPtr       line, 
4699  Int4          seqid_col,
4700  SeqIdPtr PNTR p_sip_val,
4701  SeqIdPtr PNTR p_prev_sip_val,
4702  SeqIdPtr PNTR sip_list,
4703  Int4          sip_count)
4704 {
4705   CharPtr txt;
4706   Int2    val2;
4707   Boolean okay = TRUE;
4708   
4709   if (p_sip_val == NULL || p_prev_sip_val == NULL
4710       || sip_count == 0 || sip_list == NULL
4711       || seqid_col < 0)
4712   {
4713     return FALSE;
4714   }
4715   *p_sip_val = NULL;
4716   txt = ExtractTagListColumn (line, seqid_col);
4717   if (txt != NULL) {
4718     if (! StrToInt (txt, &val2) || val2 <= 0)
4719     {
4720       if (*p_prev_sip_val != NULL)
4721       {
4722         *p_sip_val = *p_prev_sip_val;
4723       }
4724       else
4725       {
4726         okay = FALSE;
4727       }
4728     }
4729     else if (val2 <= sip_count)
4730     {
4731       *p_sip_val = sip_list [val2];
4732       *p_prev_sip_val = *p_sip_val;
4733     } else {
4734       okay = FALSE;
4735     }
4736   }
4737   else
4738   {
4739     okay = FALSE;
4740   }
4741   MemFree (txt);
4742   return okay;
4743 }
4744 
4745 static Boolean 
4746 ReadSeqAlignFromDialogLine
4747 (CharPtr          line,
4748  Int4             aln_col,
4749  SeqAlignPtr PNTR p_salp_val,
4750  SeqAlignPtr PNTR p_prev_salp_val,
4751  SeqAlignPtr PNTR salp_list,
4752  Int4             salp_count)
4753 {
4754   CharPtr txt;
4755   Int2    val2;
4756   Boolean okay = TRUE;
4757   
4758   if (p_salp_val == NULL || p_prev_salp_val == NULL 
4759       || salp_count == 0 || salp_list == NULL
4760       || aln_col < 0)
4761   {
4762     return FALSE;
4763   }
4764   
4765   txt = ExtractTagListColumn (line, aln_col);
4766   if (txt != NULL) {
4767     if (! StrToInt (txt, &val2) || val2 <= 0)
4768     {
4769       if (*p_prev_salp_val != NULL)
4770       {
4771         *p_salp_val = *p_prev_salp_val;
4772       }
4773       else
4774       {
4775         okay = FALSE;
4776       }
4777     }
4778     else if (val2 <= salp_count)
4779     {
4780       *p_salp_val = salp_list [val2];
4781       *p_prev_salp_val = *p_salp_val;
4782     } else {
4783       okay = FALSE;
4784     }
4785   }
4786   else
4787   {
4788     if (*p_prev_salp_val == NULL)
4789     {
4790       okay = FALSE;
4791     }
4792     else
4793     {
4794       *p_salp_val = *p_prev_salp_val;
4795     }
4796  }
4797  MemFree (txt);
4798  return okay;
4799 }
4800 
4801 static Boolean 
4802 GetBioseqAlignmentRow 
4803 (SeqAlignPtr salp,
4804  BioseqPtr   bsp, 
4805  Int4Ptr     aln_row)
4806 {
4807   SeqIdPtr  tmp_sip, bsp_id;
4808   if (salp == NULL || bsp == NULL || aln_row == NULL)
4809   {
4810     return FALSE;
4811   }
4812   
4813   *aln_row = 0;
4814 
4815   tmp_sip = SeqIdPtrFromSeqAlign (salp);
4816   
4817   for (bsp_id = bsp->id;
4818        bsp_id != NULL && *aln_row == 0; 
4819        bsp_id = bsp_id->next)
4820   {
4821     *aln_row = SeqIdOrderInBioseqIdList(bsp_id, tmp_sip);
4822   }
4823   if (*aln_row < 1)
4824   {
4825     return FALSE;
4826   }
4827   else
4828   {
4829     return TRUE;
4830   }
4831 }
4832 
4833 static void 
4834 ListAlignmentsThatContainSequence 
4835 (SeqAlignPtr PNTR salp_list, 
4836  Int4        salp_count,
4837  BioseqPtr   bsp,
4838  SeqIdPtr    sip,
4839  ValNodePtr  PNTR head)
4840 {
4841   Int4       i;
4842   Char       str [34];
4843   Char       id_label [128];
4844   Int4       aln_row;
4845   ValNodePtr good_aln = NULL;
4846   CharPtr    none_found_fmt = "%s is not found in any alignments";
4847   CharPtr    one_found_fmt = "%s is found in %s";
4848   CharPtr    some_found_fmt = "%s is found in the following alignments";
4849   CharPtr    err_msg;
4850   CharPtr    cp;
4851   
4852   if (head == NULL || bsp == NULL || sip == NULL)
4853   {
4854     return;
4855   }
4856   SeqIdWrite (sip, id_label, PRINTID_REPORT, sizeof (id_label));
4857   
4858   /* indent the names of the alignments */
4859   str [0] = ' ';
4860   str [1] = ' ';
4861   str [2] = ' ';
4862   str [3] = ' ';
4863   for (i = 0; i < salp_count; i++)
4864   {
4865     if (GetBioseqAlignmentRow (salp_list [i], bsp, &aln_row))
4866     {
4867       CreateSeqAlignLabel (salp_list [i], str + 4, sizeof (str) - 4);
4868       ValNodeAddPointer (&good_aln, 0, StringSave (str));
4869     }           
4870   }
4871   if (good_aln == NULL)
4872   {
4873     err_msg = (CharPtr) MemNew ((StringLen (none_found_fmt) + StringLen (id_label))
4874                                 * sizeof (Char));
4875     if (err_msg != NULL)
4876     {
4877       sprintf (err_msg, none_found_fmt, id_label);
4878       ValNodeAddPointer (head, 0, err_msg);
4879     }
4880   }
4881   else if (good_aln->next == NULL)
4882   {
4883     err_msg = (CharPtr) MemNew ((StringLen (one_found_fmt) 
4884                                  + StringLen (id_label)
4885                                  + StringLen (good_aln->data.ptrvalue))
4886                                 * sizeof (Char));
4887     if (err_msg != NULL)
4888     {
4889       cp = (CharPtr) good_aln->data.ptrvalue;
4890       /* skip over indented space */
4891       cp += 4;
4892       sprintf (err_msg, one_found_fmt, id_label, cp);
4893       ValNodeAddPointer (head, 0, err_msg);
4894       good_aln = ValNodeFreeData (good_aln);
4895     }
4896   }
4897   else
4898   {
4899     err_msg = (CharPtr) MemNew ((StringLen (some_found_fmt) 
4900                                  + StringLen (id_label))
4901                                 * sizeof (Char));
4902     if (err_msg != NULL)
4903     {
4904       sprintf (err_msg, some_found_fmt, id_label);
4905       ValNodeAddPointer (head, 0, err_msg);
4906       ValNodeLink (head, good_aln);
4907     }
4908   }
4909 }
4910 
4911 static void 
4912 ListSequencesInAlignment 
4913 (SeqIdPtr    PNTR sip_list,
4914  Int4        sip_count,
4915  SeqAlignPtr salp,
4916  ValNodePtr  PNTR head)
4917 {
4918   SeqIdPtr   tmp_sip, aln_id;
4919   BioseqPtr  bsp;
4920   Int4       i;
4921   Boolean    found;
4922   Char       id_label [128];
4923   
4924   if (salp == NULL || head == NULL)
4925   {
4926     return;
4927   }
4928 
4929   tmp_sip = SeqIdPtrFromSeqAlign (salp);
4930   if (tmp_sip == NULL)
4931   {
4932     ValNodeAddPointer (head, 0, StringSave ("Selected alignment contains no sequences"));
4933     return;
4934   }
4935   /* indent list of sequence IDs */
4936   id_label [0] = ' ';
4937   id_label [1] = ' ';
4938   id_label [2] = ' ';
4939   id_label [3] = ' ';
4940   ValNodeAddPointer (head, 0, StringSave ("The selected alignment contains the following sequences:"));
4941   for (aln_id = tmp_sip; aln_id != NULL; aln_id = aln_id->next)
4942   {
4943     bsp = BioseqFind (aln_id);
4944     if (bsp == NULL)
4945     {
4946       continue;
4947     }
4948     found = FALSE;
4949     for (i = 0; i < sip_count && ! found; i++)
4950     {
4951       if (SeqIdIn (sip_list [i], bsp->id))
4952       {
4953         SeqIdWrite (sip_list [i], id_label + 4, PRINTID_REPORT, sizeof (id_label) - 4);
4954         ValNodeAddPointer (head, 0, StringSave (id_label));
4955         found = TRUE;
4956       }
4957     }
4958   }
4959 }
4960 
4961 extern Boolean AdjustFromForGap (Int4Ptr p_from, SeqAlignPtr salp, Int4 aln_len, Int4 aln_row)
4962 {
4963   Int4 aln_from, aln_offset = 0;
4964   
4965   if (p_from == NULL || salp == NULL || aln_len == 0 || aln_row == 0
4966       || *p_from < 1)
4967   {
4968     return FALSE;
4969   }
4970   
4971   aln_from = AlnMgr2MapSeqAlignToBioseq(salp, (*p_from) - 1, aln_row);
4972   
4973   while (aln_from == -2 && (*p_from) + aln_offset < aln_len)
4974   {
4975     aln_offset ++;
4976     aln_from = AlnMgr2MapSeqAlignToBioseq(salp, (*p_from) + aln_offset - 1, aln_row);
4977   }
4978   if (aln_from < 0)
4979   {
4980     return FALSE;
4981   }
4982   else
4983   {
4984     *p_from = aln_from + 1;
4985     return TRUE;
4986   }
4987 }
4988 
4989 extern Boolean AdjustToForGap (Int4Ptr p_to, SeqAlignPtr salp, Int4 aln_row)
4990 {
4991   Int4 aln_to, aln_offset = 0;
4992   
4993   if (p_to == NULL || salp == NULL || aln_row == 0
4994       || *p_to < 1)
4995   {
4996     return FALSE;
4997   }
4998   
4999   aln_to = AlnMgr2MapSeqAlignToBioseq(salp, (*p_to) - 1, aln_row);
5000   aln_offset = 0;
5001   while (aln_to == -2 && (*p_to) - 1 - aln_offset >= 0)
5002   {
5003     aln_offset ++;
5004     aln_to = AlnMgr2MapSeqAlignToBioseq(salp, (*p_to) - 1 - aln_offset, aln_row);
5005   }
5006   if (aln_to < 0)
5007   {
5008     return FALSE;
5009   }
5010   else
5011   {
5012     *p_to = aln_to + 1;
5013     return TRUE;
5014   }
5015 }
5016 
5017 static Boolean AdjustStrandForAlignment (Uint2Ptr p_strand, SeqAlignPtr salp, Int4 aln_row)
5018 {
5019   Uint2 aln_strand;
5020   
5021   if (p_strand == NULL || salp == NULL || aln_row < 1)
5022   {
5023     return FALSE;
5024   }
5025   
5026   aln_strand = AlnMgr2GetNthStrand (salp, aln_row);
5027   if (aln_strand == Seq_strand_minus)
5028   {
5029     /* if alignment strand is minus, reverse strand of location */
5030     if (*p_strand == Seq_strand_minus)
5031     {
5032       *p_strand = Seq_strand_plus;
5033     }
5034     else if (*p_strand == Seq_strand_plus)
5035     {
5036       *p_strand = Seq_strand_minus;
5037     }
5038   }
5039   return TRUE;
5040 }
5041 
5042 static Boolean CoordinatesValidForBioseq (Int4 from, Int4 to, BioseqPtr bsp)
5043 {
5044   if (bsp == NULL || from < 1 || to > bsp->length) 
5045   {
5046     return FALSE;
5047   }
5048   else
5049   {
5050     return TRUE;
5051   }
5052 }
5053 
5054 static ValNodePtr TestIntervalEditor (DialoG d)
5055 {
5056   IntervalPagePtr  ipp;
5057   TagListPtr       tlp;
5058   ValNodePtr       head = NULL, vnp;
5059   Boolean          from_ok, to_ok, seqid_ok, salp_ok;
5060   Boolean          partial5, partial3;
5061   Int4             to, from, aln_row;
5062   Int4             interval_num;
5063   Uint2            strand, prev_strand = Seq_strand_unknown;
5064   SeqIdPtr         seqid, prev_seqid = NULL;
5065   SeqAlignPtr      salp, prev_salp = NULL;
5066   Char             err_msg[200];
5067   BioseqPtr        bsp = NULL;
5068   Int4             aln_len;
5069   Boolean          isInterval, isPoint, fuzz_before, fuzz_after;
5070   Int2             fuzz_from;
5071   Int2             fuzz_to;
5072   
5073   ipp = (IntervalPagePtr) GetObjectExtra (d);
5074   if (ipp == NULL) 
5075   {
5076     ValNodeAddPointer (&head, 0, StringSave ("No dialog data"));
5077     return head;
5078   }
5079   
5080   tlp = GetObjectExtra (ipp->ivals);
5081   if (tlp == NULL)
5082   {
5083     ValNodeAddPointer (&head, 0, StringSave ("No dialog data"));
5084     return head;
5085   }
5086   
5087   if (tlp->vnp == NULL)
5088   {
5089     ValNodeAddPointer (&head, 0, StringSave ("No location intervals listed!"));
5090   }
5091 
5092   partial5 = GetStatus (ipp->partial5);
5093   partial3 = GetStatus (ipp->partial3);
5094 
5095   for (vnp = tlp->vnp, interval_num = 1;
5096        vnp != NULL; 
5097        vnp = vnp->next, interval_num++) {
5098     isInterval = TRUE;
5099     isPoint = FALSE;
5100     fuzz_from = -1;
5101     fuzz_to = -1;
5102     fuzz_before = FALSE;
5103     fuzz_after = FALSE;
5104     from = 0;
5105     to = 0;
5106 
5107     from_ok = ReadFromValueFromDialogLine ((CharPtr) vnp->data.ptrvalue,
5108                                            &from, &fuzz_after);
5109     if (!from_ok)
5110     {
5111       /* we'll silently ignore lines that have no from value */
5112       continue;
5113     }
5114     
5115     to_ok = ReadToValueFromDialogLine ((CharPtr) vnp->data.ptrvalue, 
5116                                          &to, &from, &fuzz_after, &isInterval, &isPoint,
5117                                          partial5, partial3);
5118     if (!to_ok)
5119     {
5120       sprintf (err_msg, "Bad to value in interval %d", interval_num);
5121       ValNodeAddPointer (&head, 0, StringSave ("err_msg"));
5122     }
5123     strand = Seq_strand_unknown;
5124     ReadStrandFromDialogLine (vnp->data.ptrvalue, ipp->strand_col, 
5125                               &strand, &prev_strand);
5126     
5127     seqid_ok = ReadSeqIdFromDialogLine (vnp->data.ptrvalue, ipp->seqid_col,
5128                                          &seqid, &prev_seqid,
5129                                          ipp->sip_list, ipp->count);
5130     if (seqid_ok)
5131     {
5132       bsp = BioseqFind (seqid);
5133       if (bsp == NULL)
5134       {
5135         sprintf (err_msg, "Can't find bioseq for interval %d", interval_num);
5136         ValNodeAddPointer (&head, 0, StringSave (err_msg));
5137         seqid_ok = FALSE;
5138       }
5139     }
5140     else
5141     {
5142       sprintf (err_msg, "No sequence ID in interval %d", interval_num);
5143       ValNodeAddPointer (&head, 0, StringSave (err_msg));
5144     }
5145 
5146       
5147     /* get SeqAlign */
5148     salp = NULL;
5149     if (ipp->salp_list == NULL)
5150     {
5151       salp_ok = TRUE;
5152       if (bsp != NULL)
5153       {
5154         if (!CoordinatesValidForBioseq (from, to, bsp))
5155         {
5156           sprintf (err_msg, "Coordinates for interval %d are not in sequence (1-%d)",
5157                    interval_num, bsp->length);
5158           ValNodeAddPointer (&head, 0, StringSave (err_msg));
5159         }
5160       }
5161     }
5162     else
5163     {
5164       salp_ok = ReadSeqAlignFromDialogLine (vnp->data.ptrvalue, 
5165                                             ipp->aln_col,
5166                                             &salp, &prev_salp,
5167                                             ipp->salp_list,
5168                                             ipp->aln_count);
5169       if (!salp_ok)
5170       {
5171         sprintf (err_msg, "No alignment for interval %d", interval_num);
5172         ValNodeAddPointer (&head, 0, StringSave (err_msg));
5173       }
5174       
5175       if (salp_ok && seqid_ok)
5176       {
5177         aln_row = 0;
5178         if (GetBioseqAlignmentRow (salp, bsp, &aln_row))
5179         {
5180           aln_len = SeqAlignLength (salp);
5181           if (from < 1 || to > aln_len)
5182           {
5183             sprintf (err_msg, "Coordinates for interval %d are not in alignment interval (%d-%d)",
5184                      interval_num, 1, aln_len);
5185             ValNodeAddPointer (&head, 0, StringSave (err_msg));
5186           }
5187           else
5188           {
5189             /* check for locations in gaps */
5190             if (!AdjustFromForGap (&from, salp, aln_len, aln_row)
5191                 || ! AdjustToForGap (&to, salp, aln_row))
5192             {
5193               sprintf (err_msg, "Interval %d is completely contained in a gap in the alignment.",
5194                        interval_num);
5195               ValNodeAddPointer (&head, 0, StringSave (err_msg));
5196             }
5197           }
5198         }
5199         else
5200         {
5201           sprintf (err_msg, "Sequence for interval %d not in selected alignment", interval_num);
5202           ValNodeAddPointer (&head, 0, StringSave (err_msg));
5203           ListAlignmentsThatContainSequence (ipp->salp_list, ipp->aln_count, 
5204                                              bsp, seqid, &head);
5205           ListSequencesInAlignment (ipp->sip_list, ipp->count, salp, &head);
5206         }
5207       }
5208     }
5209   }
5210   
5211   return head;
5212 }
5213 
5214 
5215 static Boolean 
5216 AddLocToList 
5217 (SeqIdPtr       seqid,
5218  Uint2          strand,
5219  Int4           from,
5220  Int4           to,
5221  Boolean        add_null,
5222  Int2           fuzz_from,
5223  Int2           fuzz_to,
5224  Boolean        fuzz_before,
5225  Boolean        fuzz_after,
5226  Boolean        partial5,
5227  Boolean        partial3,
5228  SeqLocPtr PNTR pslp)
5229 {
5230   SeqLocPtr slp;
5231   SeqLocPtr tmploc1, tmploc2;
5232   Boolean   isInterval;
5233   Boolean   isPoint;
5234   Int4      tmp;
5235   
5236   if (pslp == NULL || seqid == NULL)
5237   {
5238     return FALSE;
5239   }
5240             
5241   if (add_null) {
5242     /* add NULL location between last location and this one */
5243     slp = ValNodeNew (NULL);
5244     if (slp != NULL) {
5245       slp->choice = SEQLOC_NULL;
5246       tmploc1 = *pslp;
5247       if (tmploc1 != NULL) {
5248         if (tmploc1->choice == SEQLOC_MIX) {
5249           tmploc2 = (ValNodePtr) (tmploc1->data.ptrvalue);
5250           if (tmploc2 != NULL) {
5251             while (tmploc2->next != NULL) {
5252               tmploc2 = tmploc2->next;
5253             }
5254             tmploc2->next = slp;
5255           }
5256         } else {
5257           tmploc2 = ValNodeNew (NULL);
5258           if (tmploc2 != NULL) {
5259             tmploc2->choice = SEQLOC_MIX;
5260             tmploc2->data.ptrvalue = (Pointer) tmploc1;
5261             tmploc1->next = slp;
5262             *pslp = tmploc2;
5263           }
5264         }
5265       }
5266     }
5267   }
5268     
5269   isInterval = TRUE;
5270   isPoint = FALSE;
5271   
5272   /* make sure from and to are in correct order */
5273   if (from > to)
5274   {
5275     tmp = from;
5276     from = to;
5277     to = tmp;
5278   }
5279 
5280   if (fuzz_after && to == from + 1) {
5281     isInterval = FALSE;
5282     isPoint = TRUE;
5283     /* from++; */ /* this was causing point to be thrown off */
5284   } else if (to == from && (! partial5) && (! partial3)) {
5285     isInterval = FALSE;
5286     isPoint = TRUE;
5287   }
5288           
5289   if (isInterval) {
5290     AddIntToSeqLoc (pslp, from - 1, to - 1, seqid,
5291                     fuzz_from, fuzz_to, strand);
5292   } else if (isPoint) {
5293     AddSeqLocPoint (pslp, seqid, from, fuzz_before, fuzz_after, strand);
5294   }
5295   return TRUE;
5296 }
5297 
5298 static void SetPartialsForOneLocation (SeqLocPtr master_slp, Boolean partial5, Boolean partial3)
5299 {
5300   SeqLocPtr  firstSlp, lastSlp, tmp_slp;
5301   IntFuzzPtr ifp;
5302   SeqIntPtr  sip;
5303   SeqPntPtr  spp;
5304   
5305   if (master_slp == NULL)
5306   {
5307     return;
5308   }
5309   
5310   /* now set partials for location */
5311   firstSlp = NULL;
5312   lastSlp = NULL;
5313   tmp_slp = SeqLocFindNext (master_slp, NULL);
5314   while (tmp_slp != NULL) {
5315     if (firstSlp == NULL) {
5316       firstSlp = tmp_slp;
5317     }
5318     lastSlp = tmp_slp;
5319     tmp_slp = SeqLocFindNext (master_slp, tmp_slp);
5320   }
5321   if (firstSlp != NULL && partial5) {
5322     if (firstSlp->choice == SEQLOC_INT && firstSlp->data.ptrvalue != NULL) {
5323       sip = (SeqIntPtr) firstSlp->data.ptrvalue;
5324       ifp = IntFuzzNew ();
5325       if (ifp != NULL) {
5326         ifp->choice = 4;
5327         if (sip->strand == Seq_strand_minus || sip->strand == Seq_strand_both_rev) {
5328           sip->if_to = ifp;
5329           ifp->a = 1;
5330         } else {
5331           sip->if_from = ifp;
5332           ifp->a = 2;
5333         }
5334       }
5335     } else if (firstSlp->choice == SEQLOC_PNT && firstSlp->data.ptrvalue != NULL) {
5336       spp = (SeqPntPtr) firstSlp->data.ptrvalue;
5337       ifp = IntFuzzNew ();
5338       if (ifp != NULL) {
5339         ifp->choice = 4;
5340         if (spp->strand == Seq_strand_minus || spp->strand == Seq_strand_both_rev) {
5341           spp->fuzz = ifp;
5342           ifp->a = 1;
5343         } else {
5344           spp->fuzz = ifp;
5345           ifp->a = 2;
5346         }
5347       }
5348     }
5349   }
5350   if (lastSlp != NULL && partial3) {
5351     if (lastSlp->choice == SEQLOC_INT && lastSlp->data.ptrvalue != NULL) {
5352       sip = (SeqIntPtr) lastSlp->data.ptrvalue;
5353       ifp = IntFuzzNew ();
5354       if (ifp != NULL) {
5355         ifp->choice = 4;
5356         if (sip->strand == Seq_strand_minus || sip->strand == Seq_strand_both_rev) {
5357           sip->if_from = ifp;
5358           ifp->a = 2;
5359         } else {
5360           sip->if_to = ifp;
5361           ifp->a = 1;
5362         }
5363       }
5364     } else if (lastSlp->choice == SEQLOC_PNT && lastSlp->data.ptrvalue != NULL) {
5365       spp = (SeqPntPtr) lastSlp->data.ptrvalue;
5366       ifp = IntFuzzNew ();
5367       if (ifp != NULL) {
5368         ifp->choice = 4;
5369         if (spp->strand == Seq_strand_minus || spp->strand == Seq_strand_both_rev) {
5370           spp->fuzz = ifp;
5371           ifp->a = 2;
5372         } else {
5373           spp->fuzz = ifp;
5374           ifp->a = 1;
5375         }
5376       }
5377     }
5378   }
5379 }
5380 
5381 static SeqLocPtr 
5382 ReadSingleSeqLoc 
5383 (TagListPtr      tlp,
5384  IntervalPagePtr ipp,
5385  Boolean         partial5,
5386  Boolean         partial3, 
5387  Boolean         nullsBetween)
5388 {
5389   Int4             from, to, aln_row, aln_len;
5390   Boolean          fuzz_after;
5391   Boolean          fuzz_before;
5392   Int2             fuzz_from;
5393   Int2             fuzz_to;
5394   Boolean          isInterval;
5395   Boolean          isPoint;
5396   Boolean          notFirst;
5397   Boolean          okay;
5398   SeqIdPtr         seqid, prev_sip;
5399   SeqLocPtr        master_slp;
5400   Uint2            strand, prev_strand;
5401   ValNodePtr       vnp;
5402   SeqAlignPtr      salp, prev_salp;
5403   BioseqPtr        bsp;
5404 
5405   if (tlp == NULL)
5406   {
5407     return NULL;
5408   }
5409   
5410   prev_sip = NULL;
5411   prev_salp = NULL;
5412   prev_strand = Seq_strand_unknown;
5413   master_slp = NULL;
5414 
5415   notFirst = FALSE;
5416   for (vnp = tlp->vnp; vnp != NULL; vnp = vnp->next) {
5417     if (StringHasNoText (vnp->data.ptrvalue))
5418     {
5419       continue;
5420     }
5421     fuzz_from = -1;
5422     fuzz_to = -1;
5423     fuzz_before = FALSE;
5424     fuzz_after = FALSE;
5425     from = 0;
5426     to = 0;
5427     isInterval = TRUE;
5428     isPoint = FALSE;
5429     strand = Seq_strand_unknown;
5430     seqid = NULL;
5431     salp = NULL;
5432     aln_row = 0;
5433     if (!ReadFromValueFromDialogLine ((CharPtr) vnp->data.ptrvalue,
5434                                            &from, &fuzz_after))
5435     {
5436       continue;
5437     }
5438     okay = ReadToValueFromDialogLine ((CharPtr) vnp->data.ptrvalue,
5439                                          &to, &from, &fuzz_after,
5440                                          &isInterval, &isPoint, 
5441                                          partial5, partial3)
5442            && ReadStrandFromDialogLine ((CharPtr) vnp->data.ptrvalue,
5443                                         ipp->strand_col,
5444                                         &strand, &prev_strand)
5445            && ReadSeqIdFromDialogLine ((CharPtr) vnp->data.ptrvalue, ipp->seqid_col,
5446                                        &seqid, &prev_sip,
5447                                        ipp->sip_list, ipp->count)
5448            && (ipp->salp_list == NULL 
5449                || ReadSeqAlignFromDialogLine ((CharPtr) vnp->data.ptrvalue, ipp->aln_col,
5450                                               &salp, &prev_salp, 
5451                                               ipp->salp_list, ipp->aln_count))
5452            && ((bsp = BioseqFind (seqid)) != NULL)
5453            && (salp != NULL || CoordinatesValidForBioseq (from, to, bsp))
5454            && (salp == NULL || 
5455                 (GetBioseqAlignmentRow (salp, bsp, &aln_row)
5456                  && AdjustStrandForAlignment (&strand, salp, aln_row)
5457                  && (aln_len = SeqAlignLength (salp)) > 0
5458                  && AdjustFromForGap (&from, salp, aln_len, aln_row)
5459                  && AdjustToForGap (&to, salp, aln_row)))
5460            && AddLocToList (seqid, strand, from, to,
5461                             (nullsBetween  && notFirst),
5462                             fuzz_from, fuzz_to,
5463                             fuzz_before, fuzz_after,
5464                             partial5, partial3,
5465                             &(master_slp));
5466     if (okay)
5467     {
5468       notFirst = TRUE;
5469     }
5470     else
5471     {
5472       master_slp = SeqLocFree (master_slp);
5473       return NULL;
5474     }
5475   }
5476   
5477   /* now set partials for location */
5478   SetPartialsForOneLocation (master_slp, partial5, partial3);  
5479   return master_slp;
5480 }
5481 
5482 static SeqLocPtr PNTR FreeSeqLocArray (SeqLocPtr PNTR loc_list, Int4 num_loc)
5483 {
5484   Int4 j;
5485   if (loc_list != NULL)
5486   {
5487     for (j = 0; j < num_loc; j++)
5488     {
5489       loc_list [j] = SeqLocFree (loc_list [j]);
5490     }
5491     loc_list = MemFree (loc_list);
5492   }
5493   return loc_list;
5494 }
5495 
5496 static SeqLocPtr 
5497 ReadAlignedSeqLocList
5498 (TagListPtr      tlp,
5499  IntervalPagePtr ipp,
5500  Boolean         partial5,
5501  Boolean         partial3, 
5502  Boolean         nullsBetween)
5503 {
5504   Int4             from, to, aln_from, aln_to, aln_row, aln_len = 0;
5505   Boolean          fuzz_after;
5506   Boolean          fuzz_before;
5507   Int2             fuzz_from;
5508   Int2             fuzz_to;
5509   Boolean          isInterval;
5510   Boolean          isPoint;
5511   Boolean          notFirst;
5512   Boolean          okay;
5513   SeqIdPtr         seqid;
5514   SeqLocPtr        master_slp, tmp_slp;
5515   Uint2            strand, prev_strand, aln_strand;
5516   ValNodePtr       vnp;
5517   SeqAlignPtr      salp, prev_salp;
5518   BioseqPtr        bsp;
5519   SeqLocPtr PNTR   loc_list;
5520   Int4             loc_num, max_locs;
5521   Boolean          asked_about_repair = FALSE;
5522   MsgAnswer        ans = ANS_YES;
5523 
5524   if (tlp == NULL || ipp == NULL || ipp->salp_list == NULL)
5525   {
5526     return NULL;
5527   }
5528   
5529   prev_salp = NULL;
5530   prev_strand = Seq_strand_unknown;
5531   master_slp = NULL;
5532   
5533   max_locs = 0;
5534   for (vnp = tlp->vnp; vnp != NULL; vnp = vnp->next)
5535   {
5536     if (!ReadSeqAlignFromDialogLine ((CharPtr) vnp->data.ptrvalue, ipp->aln_col,
5537                                      &salp, &prev_salp, 
5538                                      ipp->salp_list, ipp->aln_count))
5539     {
5540       return NULL;
5541     }
5542     else
5543     {
5544       max_locs = MAX (max_locs, salp->dim);
5545     }
5546   }
5547   if (max_locs == 0)
5548   {
5549     return NULL;
5550   }
5551   
5552   loc_list = (SeqLocPtr PNTR) MemNew (max_locs * sizeof (SeqLocPtr));
5553   if (loc_list == NULL)
5554   {
5555     return NULL;
5556   }
5557 
5558   prev_salp = NULL;
5559 
5560   okay = TRUE;
5561   notFirst = FALSE;
5562   for (vnp = tlp->vnp; vnp != NULL && okay; vnp = vnp->next) {
5563     if (StringHasNoText (vnp->data.ptrvalue))
5564     {
5565       continue;
5566     }
5567     fuzz_from = -1;
5568     fuzz_to = -1;
5569     fuzz_before = FALSE;
5570     fuzz_after = FALSE;
5571     from = 0;
5572     to = 0;
5573     isInterval = TRUE;
5574     isPoint = FALSE;
5575     strand = Seq_strand_unknown;
5576     seqid = NULL;
5577     salp = NULL;
5578     aln_row = 0;
5579     if (! ReadFromValueFromDialogLine ((CharPtr) vnp->data.ptrvalue,
5580                                            &from, &fuzz_after))
5581     {
5582       continue;
5583     }
5584     okay = ReadToValueFromDialogLine ((CharPtr) vnp->data.ptrvalue,
5585                                        &to, &from, &fuzz_after,
5586                                        &isInterval, &isPoint, 
5587                                        partial5, partial3)
5588            && ReadStrandFromDialogLine ((CharPtr) vnp->data.ptrvalue,
5589                                         ipp->strand_col,
5590                                         &strand, &prev_strand)
5591            && ReadSeqAlignFromDialogLine ((CharPtr) vnp->data.ptrvalue, ipp->aln_col,
5592                                               &salp, &prev_salp, 
5593                                               ipp->salp_list, ipp->aln_count);
5594     if (okay)
5595     {
5596       for (loc_num = 0; loc_num < salp->dim; loc_num++)
5597       {
5598         aln_row = loc_num + 1;
5599         aln_from = from;
5600         aln_to = to;
5601         aln_strand = strand;
5602         seqid = AlnMgr2GetNthSeqIdPtr (salp, aln_row);
5603         okay = ((bsp = BioseqFind (seqid)) != NULL)
5604                  && AdjustStrandForAlignment (&aln_strand, salp, aln_row)
5605                  && (aln_len = SeqAlignLength (salp)) > 0;
5606         if (okay)
5607         {
5608           if (ipp->allow_nulls_in_list)
5609           {
5610             if (! AdjustFromForGap (&aln_from, salp, aln_len, aln_row)
5611                 || ! AdjustToForGap (&aln_to, salp, aln_row)
5612                 || ! AddLocToList (seqid, aln_strand, aln_from, aln_to,
5613                                    (nullsBetween  && notFirst),
5614                                    fuzz_from, fuzz_to,
5615                                    fuzz_before, fuzz_after,
5616                                    partial5, partial3,
5617                                    &(loc_list [loc_num])))
5618             {
5619               if (loc_list[loc_num] == NULL) 
5620               {
5621                 loc_list [loc_num] = ValNodeNew (NULL);
5622                 loc_list [loc_num]->choice = SEQLOC_NULL;
5623               }
5624             }
5625           }
5626           else
5627           {
5628             okay = AdjustFromForGap (&aln_from, salp, aln_len, aln_row)
5629                    && AdjustToForGap (&aln_to, salp, aln_row)
5630                    && AddLocToList (seqid, aln_strand, aln_from, aln_to,
5631                               (nullsBetween  && notFirst),
5632                               fuzz_from, fuzz_to,
5633                               fuzz_before, fuzz_after,
5634                               partial5, partial3,
5635                               &(loc_list [loc_num]));
5636           }
5637         }
5638       }
5639     }
5640       
5641     notFirst = TRUE;
5642   }
5643   
5644   if (!okay)
5645   {
5646     loc_list = FreeSeqLocArray (loc_list, max_locs);
5647     return NULL;
5648   }
5649   
5650   /* now fix intervals that are out of order and set partials for locations */
5651   for (loc_num = 0; loc_num < max_locs && loc_list [loc_num] != NULL; loc_num++)
5652   {
5653     bsp = BioseqFindFromSeqLoc (loc_list [loc_num]);
5654     if (bsp != NULL && SeqLocBadSortOrder (bsp, loc_list [loc_num])) 
5655     {
5656       if (!asked_about_repair)
5657       {
5658         ans = Message (MSG_YN,
5659             "Feature location intervals are out of order.  Do you want them repaired?");
5660         asked_about_repair = TRUE;
5661       }
5662       if (ans == ANS_YES) {
5663         tmp_slp = SeqLocMerge (bsp, loc_list [loc_num], NULL, FALSE, FALSE, nullsBetween);
5664         loc_list [loc_num] = SeqLocFree (loc_list [loc_num]);
5665         loc_list [loc_num] = tmp_slp;
5666         if (bsp->repr == Seq_repr_seg) {
5667           tmp_slp = SegLocToParts (bsp, loc_list [loc_num]);
5668           loc_list [loc_num] = SeqLocFree (loc_list [loc_num]);
5669           loc_list [loc_num] = tmp_slp;
5670         }
5671         FreeAllFuzz (loc_list [loc_num]);
5672       }
5673     }
5674     SetSeqLocPartial (loc_list [loc_num], partial5, partial3);
5675   }
5676   
5677   /* now make chain */
5678   master_slp = loc_list [0];
5679   tmp_slp = loc_list [0];
5680   for (tmp_slp = loc_list [0], loc_num = 1; 
5681        tmp_slp != NULL && loc_num < max_locs;
5682        loc_num++)
5683   {
5684     tmp_slp->next = loc_list [loc_num];
5685     tmp_slp = tmp_slp->next;
5686   }
5687   
5688   loc_list = MemFree (loc_list);
5689   
5690   return master_slp;
5691 }
5692 
5693 static Pointer IntervalPageToSeqLocPtr (DialoG d)
5694 
5695 {
5696   IntervalPagePtr  ipp;
5697   Boolean          nullsBetween;
5698   Boolean          partial5;
5699   Boolean          partial3;
5700   SeqLocPtr        master_slp;
5701   TagListPtr       tlp;
5702 
5703   ipp = (IntervalPagePtr) GetObjectExtra (d);
5704   if (ipp == NULL) return NULL;
5705   tlp = GetObjectExtra (ipp->ivals);
5706   if (tlp == NULL) return NULL;
5707   
5708 
5709   nullsBetween = GetStatus (ipp->nullsBetween);
5710   partial5 = GetStatus (ipp->partial5);
5711   partial3 = GetStatus (ipp->partial3);
5712   
5713   if (ipp->seqid_col > -1)
5714   {
5715     master_slp = ReadSingleSeqLoc (tlp, ipp, partial5, partial3, nullsBetween);
5716   }
5717   else
5718   {
5719     master_slp = ReadAlignedSeqLocList (tlp, ipp, partial5, partial3, nullsBetween);
5720   }
5721   return (Pointer) master_slp;
5722 }
5723 
5724 static void CleanupIntervalPage (GraphiC g, VoidPtr data)
5725 
5726 {
5727   IntervalPagePtr  ipp;
5728   Int2             j;
5729 
5730   ipp = (IntervalPagePtr) data;
5731   if (ipp != NULL) {
5732     /* free seq ID list */
5733     if (ipp->sip_list != NULL)
5734     {
5735       for (j = 0; j <= ipp->count + 1; j++) {
5736         ipp->sip_list [j] = SeqIdFree (ipp->sip_list [j]);
5737       }
5738     }
5739     MemFree (ipp->sip_list);
5740     if (ipp->alist != NULL) {
5741       for (j = 0; j <= ipp->count + 1; j++) {
5742         MemFree (ipp->alist [j].name);
5743       }
5744     }
5745     MemFree (ipp->alist);
5746     MemFree (ipp->lengths);
5747 
5748     /* free list of alignments */
5749     if (ipp->salp_list != NULL)
5750     {
5751       for (j = 0; j <= ipp->aln_count + 1; j++) {
5752         ipp->salp_list [j] = SeqAlignFree (ipp->salp_list [j]); 
5753       }
5754     }
5755     MemFree (ipp->salp_list);
5756     /* free alignment tags */
5757     if (ipp->aln_alist != NULL) {
5758       for (j = 0; j <= ipp->aln_count + 1; j++) {
5759         MemFree (ipp->aln_alist [j].name);
5760       }
5761     }
5762     MemFree (ipp->aln_alist);
5763     /* free alignment lengths */
5764     MemFree (ipp->aln_lengths);
5765 
5766     
5767     /* free callback list */
5768     MemFree (ipp->callbacks);
5769   }
5770   MemFree (data);
5771 }
5772 
5773 Uint2 interval_types [] = {
5774   TAGLIST_TEXT, TAGLIST_TEXT, TAGLIST_POPUP, TAGLIST_POPUP, TAGLIST_POPUP
5775 };
5776 
5777 Uint2 interval_widths [] = {
5778   5, 5, 0, 0, 0
5779 };
5780 
5781 static Boolean ReadSeqLocDialog (DialoG d, CharPtr filename)
5782 
5783 {
5784   AsnIoPtr         aip;
5785   IntervalPagePtr  ipp;
5786   SeqLocPtr        slp;
5787   Char             path [PATH_MAX];
5788 
5789   path [0] = '\0';
5790   StringNCpy_0 (path, filename, sizeof (path));
5791   ipp = (IntervalPagePtr) GetObjectExtra (d);
5792   if (ipp != NULL) {
5793     if (path [0] != '\0' || GetInputFileName (path, sizeof (path), "", "TEXT")) {
5794       aip = AsnIoOpen (path, "r");
5795       if (aip != NULL) {
5796         slp = SeqLocAsnRead (aip, NULL);
5797         AsnIoClose (aip);
5798         if (slp != NULL) {
5799           SeqLocPtrToIntervalPage (ipp->dialog, slp);
5800           slp = SeqLocFree (slp);
5801           Update ();
5802           return TRUE;
5803         }
5804       }
5805     }
5806   }
5807   return FALSE;
5808 }
5809 
5810 static Boolean WriteSeqLocDialog (DialoG d, CharPtr filename)
5811 
5812 {
5813   AsnIoPtr         aip;
5814   IntervalPagePtr  ipp;
5815   SeqLocPtr        slp;
5816   Char             path [PATH_MAX];
5817 #ifdef WIN_MAC
5818   FILE            *f;
5819 #endif
5820 
5821   path [0] = '\0';
5822   StringNCpy_0 (path, filename, sizeof (path));
5823   ipp = (IntervalPagePtr) GetObjectExtra (d);
5824   if (ipp != NULL) {
5825     if (path [0] != '\0' || GetOutputFileName (path, sizeof (path), NULL)) {
5826 #ifdef WIN_MAC
5827       f = FileOpen (path, "r");
5828       if (f != NULL) {
5829         FileClose (f);
5830       } else {
5831         FileCreate (path, "TEXT", "ttxt");
5832       }
5833 #endif
5834       aip = AsnIoOpen (path, "w");
5835       if (aip != NULL) {
5836         slp = IntervalPageToSeqLocPtr (ipp->dialog);
5837         SeqLocAsnWrite (slp, aip, NULL);
5838         AsnIoClose (aip);
5839         slp = SeqLocFree (slp);
5840         return TRUE;
5841       }
5842     }
5843   }
5844   return FALSE;
5845 }
5846 
5847 static void SetOnlySequenceAndStrand (IntervalPagePtr ipp)
5848 {
5849   TagListPtr       tlp;
5850   ValNodePtr       vnp;
5851   CharPtr          cp;
5852   CharPtr          tabptr;
5853   ValNodePtr       saved_list;
5854   
5855   if (ipp == NULL) return;
5856   tlp = GetObjectExtra (ipp->ivals);
5857   if (tlp == NULL) return;
5858   saved_list = tlp->vnp;
5859   tlp->vnp = NULL;
5860   SendMessageToDialog (tlp->dialog, VIB_MSG_RESET);
5861   tlp->vnp = saved_list;
5862   for (vnp = tlp->vnp; vnp != NULL; vnp = vnp->next) 
5863   {
5864     cp = vnp->data.ptrvalue;
5865     if (cp != NULL)
5866     {
5867       tabptr = StringChr (cp, '\t');
5868       if (tabptr != NULL)
5869       {
5870           tabptr = StringChr (tabptr + 1, '\t');
5871       }
5872       if (tabptr != NULL)
5873       {
5874           cp[0] = '\t';
5875           cp++;
5876           while (*tabptr != 0)
5877           {
5878             *cp = *tabptr;
5879             cp++;
5880             tabptr++;
5881           }
5882           *cp = 0;
5883       }
5884     }
5885   }
5886 
5887   SendMessageToDialog (tlp->dialog, VIB_MSG_REDRAW);  
5888 }
5889 
5890 static void ClearLocationPartialCheckboxes (IntervalPagePtr ipp)
5891 {
5892   if (ipp != NULL)
5893   {
5894     SetStatus (ipp->partial5, FALSE);
5895     SetStatus (ipp->partial3, FALSE); 
5896     SetStatus (ipp->nullsBetween, FALSE); 
5897     if (ipp->proc != NULL) {
5898       ipp->proc (ipp->ffp, FALSE, FALSE, FALSE);
5899     }
5900   }
5901 }
5902 
5903 static void IntervalEditorMessage (DialoG d, Int2 mssg)
5904 
5905 {
5906   IntervalPagePtr  ipp;
5907 
5908   ipp = (IntervalPagePtr) GetObjectExtra (d);
5909   if (ipp != NULL) {
5910     switch (mssg) {
5911       case VIB_MSG_INIT:
5912         SeqLocPtrToIntervalPage (d, NULL);
5913         break;
5914       case VIB_MSG_ENTER:
5915         SendMessageToDialog (ipp->ivals, VIB_MSG_ENTER);
5916         break;
5917       case VIB_MSG_RESET:
5918         /* do nothing */
5919         break;
5920       case NUM_VIB_MSG + 1:
5921         SetOnlySequenceAndStrand (ipp);
5922         break;
5923       case NUM_VIB_MSG + 2:
5924         ClearLocationPartialCheckboxes (ipp);
5925         break;
5926       case VIB_MSG_CUT :
5927         StdCutTextProc (NULL);
5928         break;
5929       case VIB_MSG_COPY :
5930         StdCopyTextProc (NULL);
5931         break;
5932       case VIB_MSG_PASTE :
5933         StdPasteTextProc (NULL);
5934         break;
5935       case VIB_MSG_DELETE :
5936         StdDeleteTextProc (NULL);
5937         break;
5938     }
5939   }
5940 }
5941 
5942 extern void StdFeatIntEdPartialCallback (FeatureFormPtr ffp, Boolean partial5, Boolean partial3, Boolean order)
5943 
5944 {
5945   if (ffp == NULL) return;
5946   SafeSetStatus (ffp->partial, (partial5 || partial3 || order));
5947   Update ();
5948 }
5949 
5950 static void ChangedPartialProc (ButtoN b)
5951 
5952 {
5953   IntervalPagePtr  ipp;
5954 
5955   ipp = (IntervalPagePtr) GetObjectExtra (b);
5956   if (ipp == NULL) return;
5957   if (ipp->proc != NULL) {
5958     ipp->proc (ipp->ffp, GetStatus (ipp->partial5),
5959                GetStatus (ipp->partial3), GetStatus (ipp->nullsBetween));
5960   }
5961 }
5962 
5963 
5964 static CharPtr MakeBlankLocationLine (CharPtr orig)
5965 {
5966   CharPtr cp, rval = NULL;
5967   
5968   if (StringHasNoText (orig)) {
5969     return StringSave ("");
5970   }
5971   /* skip first two values, keep the rest */
5972   cp = StringChr (orig, '\t');
5973   if (cp == NULL) {
5974     return StringSave ("");
5975   }
5976   cp = StringChr (cp + 1, '\t');
5977   if (cp == NULL) {
5978     return StringSave ("");
5979   }
5980   cp++;
5981 
5982   rval = (CharPtr) MemNew (sizeof (Char) * (StringLen (cp) + 3));
5983   sprintf (rval, "\t\t%s", cp);
5984   return rval;
5985 }
5986 
5987 
5988 static void InsertFirstLocation (ButtoN b)
5989 {
5990   DialoG ivals;
5991   TagListPtr tlp;
5992   ValNodePtr vnp;
5993   Int2 j;
5994 
5995   ivals = (DialoG) GetObjectExtra (b);
5996   if (ivals == NULL) {
5997     return;
5998   }
5999   tlp = (TagListPtr) GetObjectExtra (ivals);
6000   if (tlp == NULL || tlp->vnp == NULL || StringHasNoText (tlp->vnp->data.ptrvalue)) {
6001     return;
6002   }
6003 
6004   /* add blank line to current lines */
6005   vnp = ValNodeNew (NULL);
6006   vnp->data.ptrvalue = MakeBlankLocationLine (tlp->vnp->data.ptrvalue);
6007   vnp->next = tlp->vnp;
6008   /* disconnect lines from taglist, so they won't be erased by the reset */
6009   tlp->vnp = NULL;
6010 
6011   /* reset the dialog */
6012   SendMessageToDialog (tlp->dialog, VIB_MSG_RESET);
6013 
6014   /* attach the lines to the dialog */
6015   tlp->vnp = vnp;
6016 
6017   /* redraw dialog */
6018   SendMessageToDialog (tlp->dialog, VIB_MSG_REDRAW);
6019   /* adjust scroll bars */
6020   for (j = 0, vnp = tlp->vnp; vnp != NULL; j++, vnp = vnp->next) {
6021   }
6022   tlp->max = MAX ((Int2) 0, (Int2) (j - tlp->rows + 1));
6023   CorrectBarMax (tlp->bar, tlp->max);
6024   CorrectBarPage (tlp->bar, tlp->rows - 1, tlp->rows - 1);
6025   SendMessageToDialog (tlp->dialog, VIB_MSG_ENTER);
6026 }
6027 
6028 extern DialoG CreateTagListDialogEx (GrouP h, Uint2 rows, Uint2 cols,
6029                                      Int2 spacing, Uint2Ptr types,
6030                                      Uint2Ptr textWidths, EnumFieldAssocPtr PNTR alists,
6031                                      Boolean useBar, Boolean noExtend,
6032                                      ToDialogFunc tofunc, FromDialogFunc fromfunc);
6033 
6034 extern DialoG CreateIntervalEditorDialogExEx (GrouP h, CharPtr title, Uint2 rows,
6035                                               Int2 spacing, SeqEntryPtr sep,
6036                                               Boolean nucsOK, Boolean protsOK,
6037                                               Boolean useBar, Boolean showPartials,
6038                                               Boolean allowGaps, FeatureFormPtr ffp,
6039                                               IntEdPartialProc proc, 
6040                                               Boolean use_aln, Boolean show_seqid,
6041                                               TaglistCallback tlp_callback, 
6042                                               Pointer callback_data,
6043                                               Boolean allow_nulls_in_list)
6044 
6045 {
6046   BioseqPtr        bsp;
6047   Int4             count;
6048   GrouP            f;
6049   IntervalPagePtr  ipp;
6050   Int2             j;
6051   GrouP            m;
6052   GrouP            p;
6053   GrouP            btn_grp;
6054   PrompT           p1;
6055   PrompT           p2;
6056   PrompT           p3;
6057   PrompT           p_from, p_to;
6058   CharPtr          ptr;
6059   GrouP            q;
6060   GrouP            s;
6061   Boolean          showIdTags;
6062   SeqIdPtr         sip;
6063   Char             str [128];
6064   TagListPtr       tlp;
6065   SeqAlignPtr      salp_list = NULL, salp;
6066   Int4             num_cols;
6067   ButtoN           b;
6068 
6069   p = HiddenGroup (h, 1, 0, NULL);
6070   SetGroupSpacing (p, 10, 10);
6071 
6072   ipp = (IntervalPagePtr) MemNew (sizeof (IntervalPage));
6073   if (ipp != NULL) {
6074 
6075     SetObjectExtra (p, ipp, CleanupIntervalPage);
6076     ipp->dialog = (DialoG) p;
6077     ipp->todialog = SeqLocPtrToIntervalPage;
6078     ipp->fromdialog = IntervalPageToSeqLocPtr;
6079     ipp->dialogmessage = IntervalEditorMessage;
6080     ipp->testdialog = TestIntervalEditor;
6081     ipp->importdialog = ReadSeqLocDialog;
6082     ipp->exportdialog = WriteSeqLocDialog;
6083 
6084     ipp->ffp = ffp;
6085     ipp->proc = proc;
6086      
6087     ipp->allow_nulls_in_list = allow_nulls_in_list;
6088 
6089     if (title != NULL && title [0] != '\0') {
6090       s = NormalGroup (p, 0, -2, title, systemFont, NULL);
6091     } else {
6092       s = HiddenGroup (p, 0, -2, NULL);
6093     }
6094     m = HiddenGroup (s, -1, 0, NULL);
6095     /*
6096     SetGroupSpacing (m, 10, 10);
6097     */
6098 
6099     ipp->nucsOK = nucsOK;
6100     ipp->protsOK = protsOK;
6101     ipp->showIdTags = FALSE;
6102     ipp->count = 0;
6103         
6104     if (ipp->nucsOK)
6105     {
6106       ipp->strand_col = 2;
6107     }
6108     else
6109     {
6110       ipp->strand_col = -1;
6111     }
6112     
6113     ipp->aln_col = -1;
6114     if (show_seqid)
6115     {
6116       if (ipp->nucsOK)
6117       {
6118         ipp->seqid_col = 3;
6119       }
6120       else
6121       {
6122         ipp->seqid_col = 2;
6123       }
6124       if (use_aln)
6125       {
6126         ipp->aln_col = ipp->seqid_col + 1;
6127       }
6128     }
6129     else
6130     {
6131       ipp->seqid_col = -1;
6132       if (use_aln)
6133       {
6134         if (ipp->nucsOK)
6135         {
6136           ipp->aln_col = 3;
6137         }
6138         else
6139         {
6140           ipp->aln_col = 2;
6141         }
6142       }
6143     }
6144     
6145     /* set up callbacks */
6146     ipp->callbacks = (TaglistCallback PNTR) MemNew (5 * sizeof (TaglistCallback));
6147     if (ipp->callbacks != NULL)
6148     {
6149       ipp->callbacks [0] = NULL;
6150       ipp->callbacks [1] = NULL;
6151       ipp->callbacks [2] = NULL;
6152       ipp->callbacks [3] = NULL;
6153       ipp->callbacks [4] = NULL;
6154   
6155       if (ipp->seqid_col > -1)
6156       {
6157         ipp->callbacks [ipp->seqid_col] = tlp_callback;
6158       }
6159       if (ipp->aln_col > -1)
6160       {
6161         ipp->callbacks [ipp->aln_col] = tlp_callback;
6162       }
6163     }
6164 
6165     if (sep != NULL) {
6166       if (use_aln)
6167       {
6168         VisitAnnotsInSep (sep, &salp_list, GetAlignmentsInSeqEntryCallback);
6169         count = 4;
6170         salp = salp_list;
6171         while (salp != NULL)
6172         {
6173           count++;
6174           salp = salp->next;
6175         }
6176         
6177         ipp->salp_list = (SeqAlignPtr PNTR) MemNew (sizeof (SeqAlignPtr) * count);
6178         ipp->aln_alist = MemNew (sizeof (EnumFieldAssoc) * (size_t) count);
6179         ipp->aln_lengths = MemNew (sizeof (Int4) * (size_t) count);
6180         ipp->aln_count = 0;
6181         if (ipp->salp_list != NULL && ipp->aln_alist != NULL && ipp->aln_lengths != NULL)
6182         {
6183           /* first one is NULL */
6184           ipp->salp_list [0] = NULL;
6185           ipp->aln_lengths [0] = 0;
6186           ipp->aln_alist [0].name = StringSave ("     ");
6187           ipp->aln_alist [0].value = (UIEnum) 0;
6188           ipp->aln_count ++;
6189           salp = salp_list;
6190           while (salp != NULL)
6191           {
6192             ipp->salp_list [ipp->aln_count] = salp;
6193             ipp->aln_lengths [ipp->aln_count] = SeqAlignLength (ipp->salp_list [ipp->aln_count]);
6194             CreateSeqAlignLabel (ipp->salp_list [ipp->aln_count], str, 30);
6195             ipp->aln_alist [ipp->aln_count].name = StringSave (str);
6196             ipp->aln_alist [ipp->aln_count].value = (UIEnum) ipp->aln_count;
6197                         
6198             salp = salp->next;
6199             /* sever chain */
6200             ipp->salp_list [ipp->aln_count]->next = NULL;
6201             ipp->aln_count++;
6202           }
6203         }
6204         /* add end of list marker */
6205         ipp->aln_alist [ipp->aln_count].name = NULL;
6206         ipp->aln_alist [ipp->aln_count].value = (UIEnum) 0;
6207 
6208       }
6209       else
6210       {
6211         ipp->salp_list = NULL;
6212         ipp->aln_alist = NULL;
6213         ipp->aln_lengths = NULL;
6214         ipp->aln_count = 0;
6215       }
6216       count = SegmentedEntryCount (sep);
6217       count += 4;
6218       ipp->sip_list = MemNew (sizeof (SeqIdPtr) * (size_t) count);
6219       ipp->alist = MemNew (sizeof (EnumFieldAssoc) * (size_t) count);
6220       ipp->lengths = MemNew (sizeof (Int4) * (size_t) count);
6221       ipp->count = 0;
6222 
6223       if (ipp->sip_list != NULL && ipp->alist != NULL && ipp->lengths != NULL) {
6224         VisitBioseqsInSep (sep, NULL, ClearBspScratch);
6225         SeqEntryExplore (sep, (Pointer) ipp, FillInProducts);
6226         VisitBioseqsInSep (sep, NULL, ClearBspScratch);
6227         j = 0;
6228         ipp->alist [j].name = StringSave ("     ");
6229         ipp->alist [j].value = (UIEnum) 0;
6230         for (j = 1; j <= ipp->count; j++) {
6231           sip = ipp->sip_list [j];
6232           if (sip != NULL) {
6233             bsp = BioseqFindCore (sip);
6234             if (bsp == NULL) {
6235               bsp = BioseqLockById (sip);
6236               BioseqUnlock (bsp);
6237             }
6238             if (bsp != NULL)
6239             {
6240               ipp->lengths [j] = bsp->length;
6241               sip = SeqIdFindWorst (bsp->id);
6242             }
6243             SeqIdWrite (sip, str, PRINTID_REPORT, sizeof (str));
6244             ptr = StringChr (str, '|');
6245             showIdTags = FALSE;
6246             if (ptr == NULL) {
6247               ptr = str;
6248             } else if (showIdTags) {
6249               ptr = str;
6250             } else {
6251               ptr++;
6252             }
6253             ipp->alist [j].name = StringSave (ptr);
6254             ipp->alist [j].value = (UIEnum) j;
6255           }
6256         }
6257         /* add end of list marker */
6258         j = ipp->count + 1;
6259         ipp->alist [j].name = NULL;
6260         ipp->alist [j].value = (UIEnum) 0;
6261       }
6262 #ifdef WIN_MOTIF
6263       if (ipp->count > 31) {
6264         if (ipp->seqid_col > -1)
6265         {
6266           interval_types [ipp->seqid_col] = TAGLIST_LIST;
6267         }
6268         if (ipp->aln_col > -1)
6269         {
6270           interval_types [ipp->aln_col] = TAGLIST_LIST;
6271         }
6272       } else {
6273         interval_types [2] = TAGLIST_POPUP;
6274         interval_types [3] = TAGLIST_POPUP;
6275         interval_types [4] = TAGLIST_POPUP;
6276       }
6277 #endif
6278 
6279     } else {
6280       ipp->alist = MemNew (sizeof (EnumFieldAssoc) * (size_t) 4);
6281       if (ipp->alist != NULL) {
6282         j = 0;
6283         ipp->alist [j].name = StringSave ("     ");
6284         ipp->alist [j].value = (UIEnum) 0;
6285         j = 1;
6286         ipp->alist [j].name = NULL;
6287         ipp->alist [j].value = (UIEnum) 0;
6288 
6289       }
6290     }
6291     
6292     ipp->alists [0] = NULL;
6293     ipp->alists [1] = NULL;
6294     ipp->alists [2] = NULL;
6295     ipp->alists [3] = NULL;
6296     ipp->alists [4] = NULL;
6297     if (ipp->strand_col > -1)
6298     {
6299       ipp->alists [ipp->strand_col] = strand_alist;
6300     }
6301     if (ipp->seqid_col > -1)
6302     {
6303       ipp->alists [ipp->seqid_col] = ipp->alist;
6304     }
6305     if (ipp->aln_col > -1)
6306     {
6307       ipp->alists [ipp->aln_col] = ipp->aln_alist;
6308     }
6309 
6310     q = NULL;
6311     if (showPartials) {
6312       q = HiddenGroup (m, 4, 0, NULL);
6313       SetGroupSpacing (q, 20, 2);
6314       if (nucsOK) {
6315         ipp->partial5 = CheckBox (q, "5' Partial", ChangedPartialProc);
6316       } else {
6317         ipp->partial5 = CheckBox (q, "NH2 Partial", ChangedPartialProc);
6318       }
6319       SetObjectExtra (ipp->partial5, ipp, NULL);
6320       if (nucsOK) {
6321         ipp->partial3 = CheckBox (q, "3' Partial", ChangedPartialProc);
6322       } else {
6323         ipp->partial3 = CheckBox (q, "CO2H Partial", ChangedPartialProc);
6324       }
6325       SetObjectExtra (ipp->partial3, ipp, NULL);
6326     }
6327 
6328     f = HiddenGroup (m, 5, 0, NULL);
6329     p_from = StaticPrompt (f, "From", 5 * stdCharWidth, 0, programFont, 'c');
6330     p_to = StaticPrompt (f, "To", 5 * stdCharWidth, 0, programFont, 'c');
6331     p1 = NULL;
6332     p2 = NULL;
6333     p3 = NULL;
6334     if (ipp->strand_col > -1)
6335     {
6336       p1 = StaticPrompt (f, "Strand", 0, 0, programFont, 'c');
6337     }
6338     if (ipp->seqid_col > -1)
6339     {
6340       p2 = StaticPrompt (f, "SeqID", 0, 0, programFont, 'c');
6341     }
6342     if (ipp->aln_col > -1)
6343     {
6344       p3 = StaticPrompt (f, "Alignment", 0, 0, programFont, 'c');
6345     }
6346 
6347     f = HiddenGroup (m, 0, 4, NULL);
6348     SetGroupSpacing (f, 0, 0);
6349 
6350     num_cols = 2;
6351     if (ipp->strand_col > -1)
6352     {
6353       num_cols ++;
6354     }
6355     if (ipp->seqid_col > -1)
6356     {
6357       num_cols ++;
6358     }
6359     if (ipp->aln_col > -1)
6360     {
6361       num_cols ++;
6362     }
6363     
6364     ipp->ivals = CreateTagListDialogEx3 (f, rows, num_cols, spacing,
6365                                         interval_types, interval_widths, ipp->alists,
6366                                         useBar, FALSE, NULL, NULL,
6367                                         ipp->callbacks, callback_data, FALSE, TRUE);
6368 
6369     /* put back static interval_types values that may have been changed */
6370     interval_types [2] = TAGLIST_POPUP;
6371     interval_types [3] = TAGLIST_POPUP;
6372     interval_types [4] = TAGLIST_POPUP;
6373     btn_grp = HiddenGroup (m, 2, 0, NULL);
6374     SetGroupSpacing (btn_grp, 10, 10);
6375     ipp->nullsBetween = CheckBox (btn_grp, "'order' (intersperse intervals with gaps)", ChangedPartialProc);
6376     SetObjectExtra (ipp->nullsBetween, ipp, NULL);
6377     b = PushButton (btn_grp, "Insert First Location", InsertFirstLocation);
6378     SetObjectExtra (b, ipp->ivals, NULL);
6379 
6380     AlignObjects (ALIGN_CENTER, (HANDLE) ipp->ivals,
6381                   (HANDLE) q, (HANDLE) btn_grp, NULL);
6382     tlp = (TagListPtr) GetObjectExtra (ipp->ivals);
6383     if (tlp != NULL) {
6384       AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [0], (HANDLE) p_from, NULL);
6385       AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [1], (HANDLE) p_to, NULL);
6386       if (ipp->strand_col > -1)
6387       {
6388         AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [ipp->strand_col], (HANDLE) p1, NULL);
6389       }
6390       if (ipp->seqid_col > -1)
6391       {
6392         AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [ipp->seqid_col], (HANDLE) p2, NULL);
6393       }
6394       if (ipp->aln_col > -1)
6395       {
6396         AlignObjects (ALIGN_JUSTIFY, (HANDLE) tlp->control [ipp->aln_col], (HANDLE) p3, NULL);
6397       }
6398     }
6399   }
6400 
6401   return (DialoG) p;
6402 }
6403 
6404 typedef struct intervalchoice 
6405 {
6406   DIALOG_MESSAGE_BLOCK
6407   GrouP  seq_or_aln;
6408   DialoG aln_dlg;
6409   DialoG seq_dlg;  
6410 } IntervalChoiceData, PNTR IntervalChoicePtr;
6411 
6412 static void IntervalChoiceCallback (Pointer userdata)
6413 {
6414   IntervalChoicePtr dlg;
6415   SeqLocPtr         slp, slp_tmp;
6416   Boolean           ok_for_aln = FALSE;
6417   BioseqPtr         bsp;
6418   SeqAlignPtr       salp, salp_next;
6419 
6420   if (userdata == NULL)
6421   {
6422     return;
6423   }
6424 
6425   dlg = (IntervalChoicePtr) userdata;
6426   
6427   if (GetValue (dlg->seq_or_aln) == 1)
6428   {
6429     slp = DialogToPointer (dlg->seq_dlg);
6430     if (slp != NULL)
6431     {
6432       ok_for_aln = TRUE;
6433       slp_tmp = SeqLocFindNext (slp, NULL);
6434       while (slp_tmp != NULL && ok_for_aln)
6435       {
6436         bsp = BioseqFindFromSeqLoc (slp_tmp);
6437         salp = FindAlignmentsForBioseq (bsp);
6438         if (salp == NULL)
6439         {
6440           ok_for_aln = FALSE;
6441         }
6442         else
6443         {
6444           while (salp != NULL)
6445           {
6446             salp_next = salp->next;
6447             salp->next = NULL;
6448             salp = SeqAlignFree (salp);
6449             salp = salp_next;
6450           }
6451         }
6452         slp_tmp = SeqLocFindNext (slp, slp_tmp);
6453       }
6454     }
6455     if (ok_for_aln)  
6456     {
6457       Enable (dlg->seq_or_aln);
6458     }
6459     else
6460     {
6461       Disable (dlg->seq_or_aln);
6462     }
6463   }
6464 }
6465 
6466 static void SeqLocToIntervalChoiceEditor (DialoG d, Pointer userdata)
6467 {
6468   IntervalChoicePtr dlg;
6469   
6470   dlg = (IntervalChoicePtr) GetObjectExtra (d);
6471   if (dlg == NULL)
6472   {
6473     return;
6474   }
6475   PointerToDialog (dlg->seq_dlg, userdata);
6476   PointerToDialog (dlg->aln_dlg, userdata); 
6477   IntervalChoiceCallback (dlg);
6478 }
6479 
6480 static Pointer IntervalChoiceEditorToSeqLoc (DialoG d)
6481 {
6482   IntervalChoicePtr dlg;
6483   
6484   dlg = (IntervalChoicePtr) GetObjectExtra (d);
6485   if (dlg == NULL)
6486   {
6487     return NULL;
6488   }
6489   if (GetValue (dlg->seq_or_aln) == 1)
6490   {
6491     return DialogToPointer (dlg->seq_dlg);
6492   }
6493   else
6494   {
6495     return DialogToPointer (dlg->aln_dlg);
6496   }
6497 }
6498 
6499 
6500 static Boolean WriteIntervalChoiceDialog (DialoG d, CharPtr filename)
6501 
6502 {
6503   AsnIoPtr          aip;
6504   IntervalChoicePtr dlg;
6505   SeqLocPtr         slp;
6506   Char              path [PATH_MAX];
6507 #ifdef WIN_MAC
6508   FILE            *f;
6509 #endif
6510 
6511   path [0] = '\0';
6512   StringNCpy_0 (path, filename, sizeof (path));
6513   dlg = (IntervalChoicePtr) GetObjectExtra (d);
6514   if (dlg != NULL) {
6515     if (path [0] != '\0' || GetOutputFileName (path, sizeof (path), NULL)) {
6516 #ifdef WIN_MAC
6517       f = FileOpen (path, "r");
6518       if (f != NULL) {
6519         FileClose (f);
6520       } else {
6521         FileCreate (path, "TEXT", "ttxt");
6522       }
6523 #endif
6524       aip = AsnIoOpen (path, "w");
6525       if (aip != NULL) {
6526         slp = IntervalChoiceEditorToSeqLoc (dlg->dialog);
6527         SeqLocAsnWrite (slp, aip, NULL);
6528         AsnIoClose (aip);
6529         slp = SeqLocFree (slp);
6530         return TRUE;
6531       }
6532     }
6533   }
6534   return FALSE;
6535 }
6536 
6537 
6538 static Boolean ReadIntervalChoiceDialog (DialoG d, CharPtr filename)
6539 
6540 {
6541   AsnIoPtr          aip;
6542   IntervalChoicePtr dlg;
6543   SeqLocPtr         slp;
6544   Char              path [PATH_MAX];
6545 
6546   path [0] = '\0';
6547   StringNCpy_0 (path, filename, sizeof (path));
6548   dlg = (IntervalChoicePtr) GetObjectExtra (d);
6549   if (dlg != NULL) {
6550     if (path [0] != '\0' || GetInputFileName (path, sizeof (path), "", "TEXT")) {
6551       aip = AsnIoOpen (path, "r");
6552       if (aip != NULL) {
6553         slp = SeqLocAsnRead (aip, NULL);
6554         AsnIoClose (aip);
6555         if (slp != NULL) {
6556           SeqLocToIntervalChoiceEditor (dlg->dialog, slp);
6557           slp = SeqLocFree (slp);
6558           Update ();
6559           return TRUE;
6560         }
6561       }
6562     }
6563   }
6564   return FALSE;
6565 }
6566 
6567 
6568 static void IntervalChoiceEditorMessage (DialoG d, Int2 mssg)
6569 
6570 {
6571   IntervalChoicePtr dlg;
6572 
6573   dlg = (IntervalChoicePtr) GetObjectExtra (d);
6574   if (dlg != NULL) {  
6575     if (GetValue (dlg->seq_or_aln) == 1)
6576     {
6577       SendMessageToDialog (dlg->seq_dlg, mssg);
6578     }
6579     else
6580     {
6581       SendMessageToDialog (dlg->aln_dlg, mssg);
6582     }
6583   }
6584 }
6585 
6586 static ValNodePtr TestIntervalChoiceEditor (DialoG d)
6587 {
6588   IntervalChoicePtr dlg;
6589 
6590   dlg = (IntervalChoicePtr) GetObjectExtra (d);
6591   if (dlg == NULL)
6592   {
6593     return NULL;
6594   }
6595   else if (GetValue (dlg->seq_or_aln) == 1)
6596   {
6597     return TestDialog (dlg->seq_dlg);
6598   }
6599   else
6600   {
6601     return TestDialog (dlg->aln_dlg);
6602   }
6603 }
6604 
6605 static void DisplayErrorMessagesOk (ButtoN b)
6606 {
6607   BoolPtr pdone;
6608   
6609   pdone = (BoolPtr) GetObjectExtra (b);
6610   if (pdone != NULL)
6611   {
6612     *pdone = TRUE;
6613   }
6614 }
6615 
6616 extern void DisplayErrorMessages (CharPtr title, ValNodePtr err_list)
6617 {
6618   WindoW     w;
6619   GrouP      h;
6620   DoC        doc;
6621   ButtoN     b;
6622   ValNodePtr vnp;
6623   Boolean    done = FALSE;
6624   
6625   w = MovableModalWindow(-20, -13, -10, -10, title, NULL);
6626   h = HiddenGroup (w, -1, 0, NULL);
6627   SetGroupSpacing (h, 10, 10);
6628 
6629   doc = DocumentPanel (h, stdCharWidth * 27, stdLineHeight * 8);
6630   SetDocAutoAdjust (doc, TRUE);
6631   for (vnp = err_list; vnp != NULL; vnp = vnp->next)
6632   {
6633     if (!StringHasNoText (vnp->data.ptrvalue))
6634     {
6635       AppendText (doc, vnp->data.ptrvalue, NULL, NULL, programFont);
6636     }
6637   }
6638   b = PushButton (h, "OK", DisplayErrorMessagesOk);
6639   SetObjectExtra (b, &done, NULL);
6640   AlignObjects (ALIGN_CENTER, (HANDLE) doc,
6641                               (HANDLE) b, 
6642                               NULL);
6643   Show (w);
6644   Select (w);
6645   while (!done)
6646   {
6647     ProcessExternalEvent ();
6648     Update ();
6649   }
6650   ProcessAnEvent ();
6651   Remove (w);
6652 }
6653 
6654 static void ShowIntervalChoice (IntervalChoicePtr dlg)
6655 {
6656   if (dlg == NULL)
6657   {
6658     return;
6659   }
6660   if (GetValue (dlg->seq_or_aln) == 1)
6661   {
6662     Show (dlg->seq_dlg);
6663     Hide (dlg->aln_dlg);
6664   }
6665   else
6666   {
6667     Show (dlg->aln_dlg);
6668     Hide (dlg->seq_dlg);
6669   }
6670 }
6671 
6672 static void ChangeIntervalChoice (GrouP g)
6673 {
6674   IntervalChoicePtr dlg;
6675   SeqLocPtr         slp;
6676   ValNodePtr        err_list = NULL;
6677   CharPtr           title = NULL;
6678 
6679   dlg = (IntervalChoicePtr) GetObjectExtra (g);
6680   if (dlg == NULL)
6681   {
6682     return;
6683   }
6684   if (GetValue (dlg->seq_or_aln) == 1)
6685   {
6686     title = "Unable to translate to sequence coordinates";
6687     err_list = TestDialog (dlg->aln_dlg);
6688     if (err_list == NULL)
6689     {
6690       slp = DialogToPointer (dlg->aln_dlg);
6691       PointerToDialog (dlg->seq_dlg, slp);
6692       slp = SeqLocFree (slp);
6693       err_list = TestDialog (dlg->seq_dlg);
6694     }    
6695     if (err_list != NULL)
6696     {
6697       SetValue (dlg->seq_or_aln, 2);       
6698     }
6699   }
6700   else
6701   {
6702     title = "Unable to translate to alignment coordinates";
6703     err_list = TestDialog (dlg->seq_dlg);
6704     if (err_list == NULL)
6705     {
6706       slp = DialogToPointer (dlg->seq_dlg);
6707       PointerToDialog (dlg->aln_dlg, slp);
6708       slp = SeqLocFree (slp);
6709       err_list = TestDialog (dlg->aln_dlg);
6710     }
6711     if (err_list != NULL)
6712     {
6713       SetValue (dlg->seq_or_aln, 1);
6714     }
6715   }
6716   ShowIntervalChoice (dlg);
6717   if (err_list != NULL)
6718   {
6719     DisplayErrorMessages (title, err_list);
6720     err_list = ValNodeFreeData (err_list);
6721   }
6722 }
6723 
6724 static DialoG CreateIntervalEditorDialogAlnChoice (GrouP h, CharPtr title, Uint2 rows,
6725                                             Int2 spacing, SeqEntryPtr sep,
6726                                             Boolean nucsOK, Boolean protsOK,
6727                                             Boolean useBar, Boolean showPartials,
6728                                             Boolean allowGaps, FeatureFormPtr ffp,
6729                                             IntEdPartialProc proc)
6730 {
6731   SeqAlignPtr       salp_list = NULL;
6732   GrouP             p, g;
6733   IntervalChoicePtr dlg;
6734   
6735   VisitAnnotsInSep (sep, &salp_list, GetAlignmentsInSeqEntryCallback);
6736   if (salp_list == NULL)
6737   {
6738     return CreateIntervalEditorDialogExEx (h, title, rows, spacing, sep,
6739                                          nucsOK, protsOK, useBar, showPartials,
6740                                          allowGaps, ffp, proc, FALSE, TRUE,
6741                                          NULL, NULL, FALSE);
6742   }
6743   else
6744   {
6745     dlg = (IntervalChoicePtr) MemNew (sizeof (IntervalChoiceData));
6746     if (dlg == NULL)
6747     {
6748       return NULL;
6749     }
6750   
6751     p = HiddenGroup (h, -1, 0, NULL);
6752     SetGroupSpacing (p, 10, 10);
6753     SetObjectExtra (p, dlg, StdCleanupExtraProc);
6754     
6755     dlg->dialog = (DialoG) p;
6756     dlg->todialog = SeqLocToIntervalChoiceEditor;
6757     dlg->fromdialog = IntervalChoiceEditorToSeqLoc;
6758     dlg->dialogmessage = IntervalChoiceEditorMessage;
6759     dlg->testdialog = TestIntervalChoiceEditor;
6760     dlg->exportdialog = WriteIntervalChoiceDialog;
6761     dlg->importdialog = ReadIntervalChoiceDialog;
6762 
6763     g = HiddenGroup (p, 0, 0, NULL);
6764     dlg->seq_dlg = CreateIntervalEditorDialogExEx (g, title, rows, spacing, sep,
6765                                          nucsOK, protsOK, useBar, showPartials,
6766                                          allowGaps, ffp, proc, FALSE, TRUE,
6767                                          IntervalChoiceCallback, dlg, FALSE);
6768                                          
6769     dlg->aln_dlg = CreateIntervalEditorDialogExEx (g, title, rows, spacing, sep,
6770                                          nucsOK, protsOK, useBar, showPartials,
6771                                          allowGaps, ffp, proc, TRUE, TRUE,
6772                                          IntervalChoiceCallback, dlg, FALSE);
6773     AlignObjects (ALIGN_CENTER, (HANDLE)dlg->seq_dlg, (HANDLE) dlg->aln_dlg, NULL);                                         
6774     dlg->seq_or_aln = HiddenGroup (p, 2, 0, ChangeIntervalChoice);
6775     SetObjectExtra (dlg->seq_or_aln, dlg, NULL);
6776     RadioButton (dlg->seq_or_aln, "Sequence Coordinates");
6777     RadioButton (dlg->seq_or_aln, "Alignment Coordinates");
6778     SetValue (dlg->seq_or_aln, 1);
6779     
6780     AlignObjects (ALIGN_CENTER, (HANDLE) g, (HANDLE) dlg->seq_or_aln, NULL);
6781     ShowIntervalChoice (dlg);
6782     IntervalChoiceCallback (dlg);
6783     return (DialoG) p; 
6784   }
6785 }
6786 
6787 extern DialoG CreateIntervalEditorDialogEx (GrouP h, CharPtr title, Uint2 rows,
6788                                             Int2 spacing, SeqEntryPtr sep,
6789                                             Boolean nucsOK, Boolean protsOK,
6790                                             Boolean useBar, Boolean showPartials,
6791                                             Boolean allowGaps, FeatureFormPtr ffp,
6792                                             IntEdPartialProc proc)
6793 {
6794   return CreateIntervalEditorDialogAlnChoice (h, title, rows, spacing, sep,
6795                                          nucsOK, protsOK, useBar, showPartials,
6796                                          allowGaps, ffp, proc);
6797 }
6798 
6799 extern DialoG CreateIntervalEditorDialog (GrouP h, CharPtr title, Uint2 rows,
6800                                           Int2 spacing, SeqEntryPtr sep,
6801                                           Boolean nucsOK, Boolean protsOK)
6802 
6803 {
6804   return CreateIntervalEditorDialogEx (h, title, rows, spacing, sep,
6805                                        nucsOK, protsOK, TRUE, TRUE, FALSE,
6806                                        NULL, NULL);
6807 }
6808 
6809 static void ValNodePtrToVisStringDialog (DialoG d, Pointer data)
6810 
6811 {
6812   ValNodePtr   head;
6813   Int2         j;
6814   ValNodePtr   list;
6815   CharPtr      str;
6816   TagListPtr   tlp;
6817   ValNodePtr   vnp;
6818 
6819   tlp = (TagListPtr) GetObjectExtra (d);
6820   list = (ValNodePtr) data;
6821   if (tlp != NULL) {
6822     head = NULL;
6823     while (list != NULL) {
6824       vnp = ValNodeNew (head);
6825       if (head == NULL) {
6826         head = vnp;
6827       }
6828       if (vnp != NULL) {
6829         str = MemNew (StringLen ((CharPtr) list->data.ptrvalue) + 3);
6830         if (str != NULL) {
6831           StringCpy (str, (CharPtr) list->data.ptrvalue);
6832           StringCat (str, "\n");
6833         }
6834         vnp->data.ptrvalue = str;
6835       }
6836       list = list->next;
6837     }
6838     SendMessageToDialog (tlp->dialog, VIB_MSG_RESET);
6839     tlp->vnp = head;
6840     SendMessageToDialog (tlp->dialog, VIB_MSG_REDRAW);
6841     for (j = 0, vnp = tlp->vnp; vnp != NULL; j++, vnp = vnp->next) {
6842     }
6843     tlp->max = MAX ((Int2) 0, (Int2) (j - tlp->rows + 1));
6844     CorrectBarMax (tlp->bar, tlp->max);
6845     CorrectBarPage (tlp->bar, tlp->rows - 1, tlp->rows - 1);
6846   }
6847 }
6848 
6849 static Pointer VisStringDialogToValNodePtr (DialoG d)
6850 
6851 {
6852   Char         ch;
6853   ValNodePtr   head;
6854   Int2         j;
6855   Int2         len;
6856   ValNodePtr   list;
6857   Boolean      okay;
6858   CharPtr      str;
6859   TagListPtr   tlp;
6860   ValNodePtr   vnp;
6861 
6862   head = NULL;
6863   tlp = (TagListPtr) GetObjectExtra (d);
6864   if (tlp != NULL && tlp->vnp != NULL) {
6865     list = NULL;
6866     for (vnp = tlp->vnp; vnp != NULL; vnp = vnp->next) {
6867       str = (CharPtr) vnp->data.ptrvalue;
6868       okay = FALSE;
6869       len = StringLen (str);
6870       for (j = 0; j < len; j++) {
6871         ch = str [j];
6872         if (ch != ' ' && ch != '\t' && ch != '\n') {
6873           okay = TRUE;
6874         }
6875       }
6876       if (okay) {
6877         list = ValNodeNew (list);
6878         if (head == NULL) {
6879           head = list;
6880         }
6881         if (list != NULL) {
6882           list->choice = 0;
6883           list->data.ptrvalue = ExtractTagListColumn ((CharPtr) vnp->data.ptrvalue, 0);
6884         }
6885       }
6886     }
6887   }
6888   return (Pointer) head;
6889 }
6890 
6891 static void DbtagPtrToDbtagDialog (DialoG d, Pointer data, Boolean readOnly)
6892 
6893 {
6894   DbtagPtr     dp;
6895   ValNodePtr   head;
6896   Int2         j;
6897   size_t       len;
6898   ValNodePtr   list;
6899   ObjectIdPtr  oid;
6900   CharPtr      ptr;
6901   CharPtr      str;
6902   TagListPtr   tlp;
6903   Char         tmp [16];
6904   ValNodePtr   vnp;
6905 
6906   tlp = (TagListPtr) GetObjectExtra (d);
6907   list = (ValNodePtr) data;
6908   if (tlp != NULL) {
6909     head = NULL;
6910     while (list != NULL) {
6911       vnp = ValNodeNew (head);
6912       if (head == NULL) {
6913         head = vnp;
6914       }
6915       if (vnp != NULL) {
6916         dp = (DbtagPtr) list->data.ptrvalue;
6917         if (dp != NULL && dp->db != NULL && dp->tag != NULL) {
6918           oid = dp->tag;
6919           ptr = NULL;
6920           if (oid->str != NULL) {
6921             ptr = oid->str;
6922           } else {
6923             sprintf (tmp, "%ld", (long) oid->id);
6924             ptr = tmp;
6925           }
6926           len = StringLen (dp->db) + StringLen (ptr);
6927           str = MemNew (len + 4);
6928           if (str != NULL) {
6929             StringCpy (str, dp->db);
6930             StringCat (str, "\t");
6931             StringCat (str, ptr);
6932             StringCat (str, "\n");
6933           }
6934           vnp->data.ptrvalue = str;
6935         }
6936       }
6937       list = list->next;
6938     }
6939     SendMessageToDialog (tlp->dialog, VIB_MSG_RESET);
6940     tlp->vnp = head;
6941     SendMessageToDialog (tlp->dialog, VIB_MSG_REDRAW);
6942     for (j = 0, vnp = tlp->vnp; vnp != NULL; j++, vnp = vnp->next) {
6943     }
6944     if (readOnly) {
6945       tlp->max = MAX ((Int2) 0, (Int2) (j - tlp->rows));
6946       CorrectBarMax (tlp->bar, tlp->max);
6947       CorrectBarPage (tlp->bar, tlp->rows - 1, tlp->rows - 1);
6948       if (tlp->max > 0) {
6949         SafeShow (tlp->bar);
6950       } else {
6951         SafeHide (tlp->bar);
6952       }
6953     } else {
6954       tlp->max = MAX ((Int2) 0, (Int2) (j - tlp->rows + 1));
6955       CorrectBarMax (tlp->bar, tlp->max);
6956       CorrectBarPage (tlp->bar, tlp->rows - 1, tlp->rows - 1);
6957     }
6958   }
6959 }
6960 
6961 static void DbtagPtrToRODbtagDialog (DialoG d, Pointer data)
6962 
6963 {
6964   DbtagPtrToDbtagDialog (d, data, TRUE);
6965 }
6966 
6967 static void DbtagPtrToRWDbtagDialog (DialoG d, Pointer data)
6968 
6969 {
6970   DbtagPtrToDbtagDialog (d, data, FALSE);
6971 }
6972 
6973 static Pointer DbtagDialogToDbtagPtr (DialoG d)
6974 
6975 {
6976   Boolean      alldigits;
6977   Char         ch;
6978   DbtagPtr     dp;
6979   ValNodePtr   head;
6980   Int2         j;
6981   Boolean      leadingzero;
6982   Int2         len;
6983   ValNodePtr   list;
6984   Boolean      notallzero;
6985   ObjectIdPtr  oid;
6986   Boolean      okay;
6987   CharPtr      ptr;
6988   CharPtr      str;
6989   TagListPtr   tlp;
6990   CharPtr      tmp;
6991   long int     val;
6992   ValNodePtr   vnp;
6993 
6994   head = NULL;
6995   tlp = (TagListPtr) GetObjectExtra (d);
6996   if (tlp != NULL && tlp->vnp != NULL) {
6997     list = NULL;
6998     for (vnp = tlp->vnp; vnp != NULL; vnp = vnp->next) {
6999       str = (CharPtr) vnp->data.ptrvalue;
7000       okay = FALSE;
7001       len = StringLen (str);
7002       for (j = 0; j < len; j++) {
7003         ch = str [j];
7004         if (ch != ' ' && ch != '\t' && ch != '\n') {
7005           okay = TRUE;
7006         }
7007       }
7008       if (okay) {
7009         list = ValNodeNew (list);
7010         if (head == NULL) {
7011           head = list;
7012         }
7013         if (list != NULL) {
7014           list->choice = 0;
7015           dp = DbtagNew ();
7016           list->data.ptrvalue = (Pointer) dp;
7017           if (dp != NULL) {
7018             dp->db = ExtractTagListColumn ((CharPtr) vnp->data.ptrvalue, 0);
7019             oid = ObjectIdNew ();
7020             dp->tag = oid;
7021             if (oid != NULL) {
7022               tmp = ExtractTagListColumn ((CharPtr) vnp->data.ptrvalue, 1);
7023               TrimSpacesAroundString (tmp);
7024               if (tmp != NULL) {
7025                 leadingzero = FALSE;
7026                 notallzero = FALSE;
7027                 alldigits = TRUE;
7028                 ptr = tmp;
7029                 ch = *ptr;
7030                 if (ch == '0') {
7031                   leadingzero = TRUE;
7032                 }
7033                 while (ch != '\0') {
7034                   if (! (IS_DIGIT (ch))) {
7035                     alldigits = FALSE;
7036                   } else if ('1'<= ch && ch <='9') {
7037                     notallzero = TRUE;
7038                   }
7039                   ptr++;
7040                   ch = *ptr;
7041                 }
7042                 if (alldigits && (! (leadingzero && notallzero)) && sscanf (tmp, "%ld", &val) == 1) {
7043                   oid->id = (Int4) val;
7044                   MemFree (tmp);
7045                 } else {
7046                   oid->str = tmp;
7047                 }
7048               } else {
7049                 oid->str = StringSave ("?");
7050               }
7051             }
7052           }
7053         }
7054       }
7055     }
7056   }
7057   return (Pointer) head;
7058 }
7059 
7060 Uint2 visstring_types [] = {
7061   TAGLIST_TEXT, TAGLIST_TEXT
7062 };
7063 
7064 Uint2 readonlystring_types [] = {
7065   TAGLIST_PROMPT, TAGLIST_PROMPT
7066 };
7067 
7068 Uint2 visstring_widths [] = {
7069   0, 0, 0, 0
7070 };
7071 
7072 extern DialoG CreateVisibleStringDialog (GrouP h, Uint2 rows,
7073                                          Int2 spacing, Int2 width)
7074 
7075 {
7076   visstring_widths [0] = width;
7077   return CreateTagListDialog (h, rows, 1, spacing,
7078                               visstring_types, visstring_widths, NULL,
7079                               ValNodePtrToVisStringDialog,
7080                               VisStringDialogToValNodePtr);
7081 }
7082 
7083 extern DialoG CreateDbtagDialog (GrouP h, Uint2 rows, Int2 spacing,
7084                                  Int2 width1, Int2 width2)
7085 
7086 {
7087   DialoG      d;
7088   TagListPtr  tlp;
7089 
7090   visstring_widths [0] = width1;
7091   visstring_widths [1] = width2;
7092   if (GetAppProperty ("ReadOnlyDbTags") == NULL) {
7093     return CreateTagListDialog (h, rows, 2, spacing,
7094                                 visstring_types, visstring_widths, NULL,
7095                                 DbtagPtrToRWDbtagDialog,
7096                                 DbtagDialogToDbtagPtr);
7097   } else {
7098     d = CreateTagListDialog (h, rows, 2, spacing,
7099                              readonlystring_types, visstring_widths, NULL,
7100                              DbtagPtrToRODbtagDialog,
7101                              DbtagDialogToDbtagPtr);
7102     tlp = (TagListPtr) GetObjectExtra (d);
7103     if (tlp != NULL) {
7104       Hide (tlp->bar);
7105     }
7106     return d;
7107   }
7108 }
7109 
7110 /* ValueList Editor */
7111 static CharPtr combine_strings (CharPtr s, CharPtr add)
7112 {
7113   CharPtr total;
7114   if (StringHasNoText (add))
7115   {
7116     return s;
7117   }
7118   if (StringHasNoText (s))
7119   {
7120     s = MemFree (s);
7121     s = StringSave (add);
7122   }
7123   else
7124   {
7125     total = (CharPtr) MemNew (sizeof (Char) * (StringLen (s) + StringLen (add) + 2));
7126     sprintf (total, "%s;%s", s, add);
7127     s = MemFree (s);
7128     s = total;
7129   }
7130   return s;
7131 }
7132 
7133 
7134 
7135 typedef struct simpletextvalueedit {
7136   DIALOG_MESSAGE_BLOCK
7137   TaglistCallback change_notify;
7138   Pointer         change_userdata;
7139   TexT text;
7140 } SimpleTextValueEditData, PNTR SimpleTextValueEditPtr;
7141 
7142 static void SimpleTextValueEditChange (TexT t)
7143 {
7144   SimpleTextValueEditPtr dlg;
7145   dlg = (SimpleTextValueEditPtr) GetObjectExtra (t);
7146   if (dlg != NULL && dlg->change_notify != NULL)
7147   {
7148     (dlg->change_notify) (dlg->change_userdata);
7149   }
7150 }
7151 
7152 static void StringToSimpleTextValueEditDialog (DialoG d, Pointer data)
7153 {
7154   SimpleTextValueEditPtr dlg;
7155   dlg = (SimpleTextValueEditPtr) GetObjectExtra (d);
7156  
7157   if (dlg != NULL)
7158   {
7159     SetTitle (dlg->text, (CharPtr) data);
7160     if (dlg->change_notify != NULL)
7161     {
7162       (dlg->change_notify) (dlg->change_userdata);
7163     }
7164   }
7165 }
7166 
7167 
7168 static Pointer SimpleTextValueEditDialogToString (DialoG d)
7169 {
7170   SimpleTextValueEditPtr dlg;
7171   dlg = (SimpleTextValueEditPtr) GetObjectExtra (d);
7172  
7173   if (dlg != NULL)
7174   {
7175     return SaveStringFromText (dlg->text);
7176   }
7177   else
7178   {
7179     return NULL;
7180   }
7181 }
7182 
7183 static DialoG SimpleTextValueEditDialog (GrouP h, Int2 width, ValueListParentPtr parent, TaglistCallback change_notify, Pointer change_userdata)
7184 {
7185   SimpleTextValueEditPtr dlg;
7186   GrouP           p;
7187 
7188   p = HiddenGroup (h, -1, 0, NULL);
7189   SetGroupSpacing (p, 10, 10);
7190   dlg = (SimpleTextValueEditPtr) MemNew (sizeof(SimpleTextValueEditData));
7191 
7192   SetObjectExtra (p, dlg, StdCleanupExtraProc);
7193   dlg->dialog = (DialoG) p;
7194   dlg->todialog = StringToSimpleTextValueEditDialog;
7195   dlg->fromdialog = SimpleTextValueEditDialogToString;
7196   dlg->testdialog = NULL;
7197   dlg->dialogmessage = NULL;
7198   dlg->change_notify = change_notify;
7199   dlg->change_userdata = change_userdata;
7200 
7201   dlg->text = DialogText (p, "", width, SimpleTextValueEditChange);
7202   SetObjectExtra (dlg->text, dlg, NULL);
7203   return (DialoG) p;
7204 }
7205 
7206 #define STRUCTURED_VALUE_EDIT_FIELDS \
7207   DIALOG_MESSAGE_BLOCK \
7208   TaglistCallback     change_notify; \
7209   Pointer             change_userdata; \
7210   GrouP               unparsable; \
7211   TexT                text; \
7212   ValueListParentPtr  parent; \
7213   DialoG              dlg; \
7214   ParseOK             parse_func;
7215 
7216 typedef struct structuredvalueeditdlg {
7217   STRUCTURED_VALUE_EDIT_FIELDS
7218 } StructuredValueEditDlgData, PNTR StructuredValueEditDlgPtr;
7219 
7220 
7221 static void CopyUnparsableToNote (ButtoN b)
7222 {
7223   StructuredValueEditDlgPtr dlg;
7224   CharPtr                   old_note, new_note;
7225 
7226   dlg = (StructuredValueEditDlgPtr) GetObjectExtra (b);
7227  
7228   if (dlg != NULL && dlg->parent != NULL && dlg->parent->note != NULL)
7229   {
7230     old_note = SaveStringFromText (dlg->parent->note);
7231     new_note = SaveStringFromText (dlg->text);
7232     old_note = combine_strings (old_note, new_note);
7233     SetTitle (dlg->parent->note, old_note); 
7234     new_note = MemFree (new_note);
7235     old_note = MemFree (old_note);
7236     SetTitle (dlg->text, "");
7237     Hide (dlg->unparsable);
7238     if (dlg != NULL && dlg->change_notify != NULL)
7239     {
7240       (dlg->change_notify) (dlg->change_userdata);
7241     }
7242   }
7243 }
7244 
7245 
7246 static void EraseUnparsable (ButtoN b)
7247 {
7248   StructuredValueEditDlgPtr dlg;
7249 
7250   dlg = (StructuredValueEditDlgPtr) GetObjectExtra (b);
7251 
7252   if (dlg != NULL)
7253   {
7254     SetTitle (dlg->text, "");
7255     Hide (dlg->unparsable);
7256     if (dlg != NULL && dlg->change_notify != NULL)
7257     {
7258       (dlg->change_notify) (dlg->change_userdata);
7259     }
7260   }
7261 }
7262 
7263 
7264 static void StringToStructuredValueEditDialog (DialoG d, Pointer data)
7265 {
7266   StructuredValueEditDlgPtr dlg;
7267   CharPtr                   txt;
7268 
7269   dlg = (StructuredValueEditDlgPtr) GetObjectExtra (d);
7270 
7271   txt = (CharPtr) data;
7272  
7273   if (dlg != NULL)
7274   {
7275     if (StringHasNoText (txt) || (dlg->parse_func != NULL && (dlg->parse_func) (txt)))
7276     {
7277       PointerToDialog (dlg->dlg, txt);
7278       SetTitle (dlg->text, "");
7279       Hide (dlg->unparsable);
7280     }
7281     else
7282     {
7283       SetTitle (dlg->text, (CharPtr) data);
7284       Show (dlg->unparsable);
7285     }
7286     if (dlg->change_notify != NULL)
7287     {
7288       (dlg->change_notify) (dlg->change_userdata);
7289     }
7290   }
7291 }
7292 
7293 
7294 static Pointer StructuredValueEditDialogToString (DialoG d)
7295 {
7296   StructuredValueEditDlgPtr dlg;
7297   CharPtr                   txt = NULL, tmp;
7298 
7299   dlg = (StructuredValueEditDlgPtr) GetObjectExtra (d);
7300 
7301   if (dlg != NULL)
7302   {
7303     if (dlg->dlg == NULL)
7304     {
7305       txt = StringSave ("");
7306     }
7307     else
7308     {
7309       txt = DialogToPointer (dlg->dlg);
7310     }
7311     
7312     if (!TextHasNoText (dlg->text))
7313     {
7314       tmp = SaveStringFromText (dlg->text);
7315       txt = combine_strings (txt, tmp);
7316       tmp = MemFree (tmp);
7317     }
7318   }
7319   return txt;
7320 
7321 }
7322 
7323 
7324 static void StructuredValueEditChange (TexT t)
7325 {
7326   StructuredValueEditDlgPtr dlg;
7327   CharPtr                    txt, tmp;
7328 
7329   dlg = (StructuredValueEditDlgPtr) GetObjectExtra (t);
7330 
7331   if (TextHasNoText (dlg->text))
7332   {
7333     Hide (dlg->unparsable);
7334   }
7335   else if (dlg->dlg != NULL)
7336   {
7337     txt = SaveStringFromText(dlg->text);
7338     tmp = DialogToPointer (dlg->dlg);
7339     if (StringHasNoText (tmp) && dlg->parse_func != NULL && (dlg->parse_func)(txt))
7340     {
7341       PointerToDialog (dlg->dlg, txt);
7342       SetTitle (dlg->text, "");
7343       Hide (dlg->unparsable);
7344     }
7345     txt = MemFree (txt);
7346     tmp = MemFree (tmp);
7347   }
7348 
7349   if (dlg != NULL && dlg->change_notify != NULL)
7350   {
7351     (dlg->change_notify) (dlg->change_userdata);
7352   }
7353 }
7354 
7355 
7356 
7357 typedef struct truefalsevalueedit {
7358   STRUCTURED_VALUE_EDIT_FIELDS
7359 } TrueFalseValueEditData, PNTR TrueFalseValueEditPtr;
7360 
7361 static DialoG TrueFalseValueEditDialog (GrouP h, Int2 width, ValueListParentPtr parent, TaglistCallback change_notify, Pointer change_userdata)
7362 {
7363   TrueFalseValueEditPtr dlg;
7364   GrouP           p;
7365   ButtoN          b;
7366 
7367   p = HiddenGroup (h, 2, 0, NULL);
7368   SetGroupSpacing (p, 10, 10);
7369   dlg = (TrueFalseValueEditPtr) MemNew (sizeof(TrueFalseValueEditData));
7370 
7371   SetObjectExtra (p, dlg, StdCleanupExtraProc);
7372   dlg->dialog = (DialoG) p;
7373   dlg->todialog = StringToStructuredValueEditDialog;
7374   dlg->fromdialog = StructuredValueEditDialogToString;
7375   dlg->testdialog = NULL;
7376   dlg->dialogmessage = NULL;
7377   dlg->change_notify = change_notify;
7378   dlg->change_userdata = change_userdata;
7379   dlg->parent = parent;
7380 
7381   StaticPrompt (p, "TRUE", 0, 0, programFont, 'c');
7382 
7383   dlg->unparsable = HiddenGroup (p, 3, 0, NULL);
7384   SetGroupSpacing (dlg->unparsable, 10, 10);
7385   dlg->text = DialogText (dlg->unparsable, "", width - 15, StructuredValueEditChange);
7386   SetObjectExtra (dlg->text, dlg, NULL);
7387  
7388   b = PushButton (dlg->unparsable, "Copy to Note", CopyUnparsableToNote);
7389   SetObjectExtra (b, dlg, NULL);
7390 
7391   b = PushButton (dlg->unparsable, "Erase", EraseUnparsable);
7392   SetObjectExtra (b, dlg, NULL);
7393 
7394   Hide (dlg->unparsable);
7395 
7396   return (DialoG) p;
7397 }
7398 
7399 
7400 typedef struct latlondlg {
7401   DIALOG_MESSAGE_BLOCK
7402 
7403   PopuP dir_ns;
7404   PopuP dir_ew;
7405   TexT  deg_ns;
7406   TexT  deg_ew;
7407 
7408   TaglistCallback change_notify;
7409   Pointer change_userdata;
7410 
7411 } LatLonDlgData, PNTR LatLonDlgPtr;
7412 
7413 
7414 static void ResetLatLonDlg (LatLonDlgPtr dlg)
7415 {
7416   if (dlg == NULL) return;
7417 
7418   SetTitle (dlg->deg_ns, "");
7419   SetValue (dlg->dir_ns, 1);
7420   SetTitle (dlg->deg_ew, "");
7421   SetValue (dlg->dir_ew, 1);
7422 }
7423 
7424 
7425 static void StringToLatLonDlg (DialoG d, Pointer data)
7426 {
7427   LatLonDlgPtr dlg;
7428   CharPtr      str, ns, ew, tmp;
7429   Int4         len;
7430 
7431   dlg = (LatLonDlgPtr) GetObjectExtra (d);
7432   if (dlg == NULL) return;
7433   
7434   str = (CharPtr) data;
7435   if (str == NULL)
7436   {
7437     ResetLatLonDlg (dlg);
7438     return;
7439   }
7440 
7441   ew = str + StringLen (str) - 1;
7442   if (*ew != 'E' && *ew != 'W')
7443   {
7444     ResetLatLonDlg (dlg);
7445     return;
7446   }
7447   
7448   ns = StringChr (str, 'N');
7449   if (ns == NULL)
7450   {
7451     ns = StringChr (str, 'S');
7452   }
7453   if (ns == NULL)
7454   {
7455     ResetLatLonDlg (dlg);
7456     return;
7457   }
7458 
7459   len = ns - str + 1;
7460   tmp = (CharPtr) MemNew (sizeof (Char) * len);
7461   StringNCpy (tmp, str, len - 1);
7462   tmp [len - 1] = 0;
7463   TrimSpacesAroundString (tmp);
7464   SetTitle (dlg->deg_ns, tmp);
7465   tmp = MemFree (tmp);
7466   SetValue (dlg->dir_ns, *ns == 'N' ? 1 : 2);
7467   
7468   len = ew - ns;
7469   tmp = (CharPtr) MemNew (sizeof (Char) * len);
7470   StringNCpy (tmp, ns + 1, len - 1);
7471   tmp [len - 1] = 0;
7472   TrimSpacesAroundString (tmp);
7473   SetTitle (dlg->deg_ew, tmp);
7474   tmp = MemFree (tmp);
7475   SetValue (dlg->dir_ew, *ew == 'E' ? 1 : 2);
7476   
7477 }
7478 
7479 
7480 static Pointer LatLonDlgToString (DialoG d)
7481 {
7482   LatLonDlgPtr dlg;
7483   CharPtr      str, ns, ew;
7484   Int4         len;
7485 
7486   dlg = (LatLonDlgPtr) GetObjectExtra (d);
7487   if (dlg == NULL) return NULL;
7488 
7489   if (TextHasNoText (dlg->deg_ns) && TextHasNoText (dlg->deg_ew))
7490   {
7491     return NULL;
7492   }  
7493 
7494   ns = SaveStringFromText (dlg->deg_ns);
7495   ew = SaveStringFromText (dlg->deg_ew);
7496   len = StringLen (ns) + StringLen (ew) + 6;
7497   str = (CharPtr) MemNew (sizeof(Char) * len);
7498   sprintf (str, "%s %c %s %c", 
7499            ns == NULL ? "" : ns,
7500            GetValue (dlg->dir_ns) == 1 ? 'N' : 'S',
7501            ew == NULL ? "" : ew,
7502            GetValue (dlg->dir_ew) == 1 ? 'E' : 'W');
7503   ns = MemFree (ns);
7504   ew = MemFree (ew);
7505   return str;
7506 }
7507 
7508 static void LatLonTextChange (TexT t)
7509 {
7510   LatLonDlgPtr dlg;
7511 
7512   dlg = (LatLonDlgPtr) GetObjectExtra (t);
7513   if (dlg == NULL) return;
7514 
7515 
7516   if (dlg->change_notify != NULL)
7517   {
7518     (dlg->change_notify) (dlg->change_userdata);
7519   }
7520 }
7521 
7522 
7523 static void LatLonPopupChange (PopuP p)
7524 {
7525   LatLonDlgPtr dlg;
7526 
7527   dlg = (LatLonDlgPtr) GetObjectExtra (p);
7528   if (dlg == NULL) return;
7529 
7530 
7531   if (dlg->change_notify != NULL)
7532   {
7533     (dlg->change_notify) (dlg->change_userdata);
7534   }
7535 }
7536 
7537 
7538 static DialoG LatLonDialog (GrouP h, TaglistCallback change_notify, Pointer change_userdata)
7539 {
7540   LatLonDlgPtr dlg;
7541   GrouP        p;
7542 
7543   p = HiddenGroup (h, 4, 0, NULL);
7544   SetGroupSpacing (p, 10, 10);
7545   dlg = (LatLonDlgPtr) MemNew (sizeof(LatLonDlgData));
7546 
7547   SetObjectExtra (p, dlg, StdCleanupExtraProc);
7548   dlg->dialog = (DialoG) p;
7549   dlg->todialog = StringToLatLonDlg;
7550   dlg->fromdialog = LatLonDlgToString;
7551   dlg->testdialog = NULL;
7552   dlg->dialogmessage = NULL;
7553   dlg->change_notify = change_notify;
7554   dlg->change_userdata = change_userdata;
7555 
7556   dlg->deg_ns = DialogText (p, "", 3, LatLonTextChange);
7557   SetObjectExtra (dlg->deg_ns, dlg, NULL);
7558   dlg->dir_ns = PopupList (p, TRUE, LatLonPopupChange);
7559   SetObjectExtra (dlg->dir_ns, dlg, NULL);
7560   PopupItem (dlg->dir_ns, "N");
7561   PopupItem (dlg->dir_ns, "S");
7562   SetValue (dlg->dir_ns, 1);
7563 
7564   dlg->deg_ew = DialogText (p, "", 3, LatLonTextChange);
7565   SetObjectExtra (dlg->deg_ew, dlg, NULL);
7566   dlg->dir_ew = PopupList (p, TRUE, LatLonPopupChange);
7567   SetObjectExtra (dlg->dir_ew, dlg, NULL);
7568   PopupItem (dlg->dir_ew, "E");
7569   PopupItem (dlg->dir_ew, "W");
7570 
7571   SetValue (dlg->dir_ew, 1);
7572   
7573   return (DialoG) p;
7574 }
7575 
7576 
7577 static Boolean ParseLatLonOk (CharPtr str)
7578 {
7579   CharPtr ns, ew, cp;
7580 
7581   if (StringHasNoText (str))
7582   {
7583     return TRUE;
7584   }
7585   ew = str + StringLen (str) - 1;
7586   if (*ew != 'E' && *ew != 'W')
7587   {
7588     return FALSE;
7589   }
7590   ns = str;
7591   while (ns < ew && !isalpha (*ns))
7592   {
7593     ns++;
7594   }
7595   if (*ns != 'N' && *ns != 'S')
7596   {
7597     return FALSE;
7598   }
7599 
7600   cp = ns + 1;
7601   while (cp < ew)
7602   {
7603     if (isalpha (*cp))
7604     {
7605       return FALSE;
7606     }
7607     cp++;
7608   }
7609   return TRUE;
7610 }
7611 
7612 
7613 static DialoG ValueListLatLonDialog (GrouP h, Int2 width, ValueListParentPtr parent, TaglistCallback change_notify, Pointer change_userdata)
7614 {
7615   StructuredValueEditDlgPtr dlg;
7616   GrouP           p;
7617   ButtoN          b;
7618 
7619 
7620   p = HiddenGroup (h, 2, 0, NULL);
7621   SetGroupSpacing (p, 10, 10);
7622   dlg = (StructuredValueEditDlgPtr) MemNew (sizeof(StructuredValueEditDlgData));
7623 
7624   SetObjectExtra (p, dlg, StdCleanupExtraProc);
7625   dlg->dialog = (DialoG) p;
7626   dlg->todialog = StringToStructuredValueEditDialog;
7627   dlg->fromdialog = StructuredValueEditDialogToString;
7628   dlg->testdialog = NULL;
7629   dlg->dialogmessage = NULL;
7630   dlg->change_notify = change_notify;
7631   dlg->change_userdata = change_userdata;
7632   dlg->parent = parent;
7633  
7634   dlg->dlg = LatLonDialog (p, change_notify, change_userdata);
7635   dlg->parse_func = ParseLatLonOk;
7636   
7637   dlg->unparsable = HiddenGroup (p, 3, 0, NULL);
7638   SetGroupSpacing (dlg->unparsable, 10, 10);
7639   dlg->text = DialogText (dlg->unparsable, "", width - 15, StructuredValueEditChange);
7640   SetObjectExtra (dlg->text, dlg, NULL);
7641  
7642   b = PushButton (dlg->unparsable, "Copy to Note", CopyUnparsableToNote);
7643   SetObjectExtra (b, dlg, NULL);
7644 
7645   b = PushButton (dlg->unparsable, "Erase", EraseUnparsable);
7646   SetObjectExtra (b, dlg, NULL);
7647 
7648   Hide (dlg->unparsable);
7649 
7650   return (DialoG) p;
7651 }
7652 
7653 
7654 typedef struct specimenvoucherdlg {
7655   DIALOG_MESSAGE_BLOCK
7656 
7657   TexT  institution_code;
7658   TexT  collection_code;
7659   TexT  free_text;
7660 
7661   TaglistCallback change_notify;
7662   Pointer change_userdata;
7663 
7664 } SpecimenVoucherDlgData, PNTR SpecimenVoucherDlgPtr;
7665 
7666 
7667 static void ResetSpecimenVoucherDlg (SpecimenVoucherDlgPtr dlg)
7668 {
7669   if (dlg == NULL) return;
7670 
7671   SetTitle (dlg->institution_code, "");
7672   SetTitle (dlg->collection_code, "");
7673   SetTitle (dlg->free_text, "");
7674 }
7675 
7676 
7677 static void StringToSpecimenVoucherDlg (DialoG d, Pointer data)
7678 {
7679   SpecimenVoucherDlgPtr dlg;
7680   CharPtr      str, ptr, cp;
7681 
7682   dlg = (SpecimenVoucherDlgPtr) GetObjectExtra (d);
7683   if (dlg == NULL) return;
7684   
7685   ResetSpecimenVoucherDlg (dlg);
7686   str = (CharPtr) data;
7687   if (str == NULL)
7688   {
7689     return;
7690   }
7691 
7692   /* make copy so we don't worry about changing data */
7693   str = StringSave (str);
7694 
7695   cp = StringChr (str, ':');
7696   if (cp == NULL)
7697   {
7698     SetTitle (dlg->free_text, str);
7699   }
7700   else 
7701   {
7702     *cp = 0;
7703     SetTitle (dlg->institution_code, str);
7704     ptr = cp + 1;
7705     cp = StringChr (ptr, ':');
7706     if (cp == NULL) 
7707     {
7708       SetTitle (dlg->free_text, ptr);
7709     }
7710     else 
7711     {
7712       *cp = 0;
7713       SetTitle (dlg->collection_code, ptr);
7714       SetTitle (dlg->free_text, cp + 1);
7715     }
7716   }
7717   str = MemFree (str);
7718 }
7719 
7720 
7721 static Pointer SpecimenVoucherDlgToString (DialoG d)
7722 {
7723   SpecimenVoucherDlgPtr dlg;
7724   CharPtr      str;
7725   Int4         len;
7726   CharPtr      inst, coll, free_text;
7727 
7728   dlg = (SpecimenVoucherDlgPtr) GetObjectExtra (d);
7729   if (dlg == NULL) return NULL;
7730 
7731   if (TextHasNoText (dlg->institution_code) && TextHasNoText (dlg->collection_code) && TextHasNoText (dlg->free_text))
7732   {
7733     return NULL;
7734   }  
7735 
7736   inst = SaveStringFromText (dlg->institution_code);
7737   coll = SaveStringFromText (dlg->collection_code);
7738   free_text = SaveStringFromText (dlg->free_text);
7739 
7740   len = StringLen (inst) + StringLen (coll) + StringLen (free_text) + 3;
7741   str = (CharPtr) MemNew (sizeof(Char) * len);
7742   if (StringHasNoText (inst) && StringHasNoText (coll))
7743   {
7744     sprintf (str, "%s", free_text);
7745   }
7746   else if (StringHasNoText (coll))
7747   {
7748     sprintf (str, "%s:%s", inst, free_text == NULL ? "" : free_text);
7749   }
7750   else
7751   {
7752     sprintf (str, "%s:%s:%s", inst == NULL ? "" : inst,
7753                               coll == NULL ? "" : coll,
7754                               free_text == NULL ? "" : free_text);
7755   }
7756   inst = MemFree (inst);
7757   coll = MemFree (coll);
7758   free_text = MemFree (free_text);
7759   return str;
7760 }
7761 
7762 static void SpecimenVoucherTextChange (TexT t)
7763 {
7764   SpecimenVoucherDlgPtr dlg;
7765 
7766   dlg = (SpecimenVoucherDlgPtr) GetObjectExtra (t);
7767   if (dlg == NULL) return;
7768 
7769 
7770   if (dlg->change_notify != NULL)
7771   {
7772     (dlg->change_notify) (dlg->change_userdata);
7773   }
7774 }
7775 
7776 
7777 static DialoG SpecimenVoucherDialog (GrouP h, Int2 width, ValueListParentPtr parent, TaglistCallback change_notify, Pointer change_userdata)
7778 {
7779   SpecimenVoucherDlgPtr dlg;
7780   GrouP        p;
7781 
7782   p = HiddenGroup (h, 6, 0, NULL);
7783   SetGroupSpacing (p, 10, 10);
7784   dlg = (SpecimenVoucherDlgPtr) MemNew (sizeof(SpecimenVoucherDlgData));
7785 
7786   SetObjectExtra (p, dlg, StdCleanupExtraProc);
7787   dlg->dialog = (DialoG) p;
7788   dlg->todialog = StringToSpecimenVoucherDlg;
7789   dlg->fromdialog = SpecimenVoucherDlgToString;
7790   dlg->testdialog = NULL;
7791   dlg->dialogmessage = NULL;
7792   dlg->change_notify = change_notify;
7793   dlg->change_userdata = change_userdata;
7794 
7795   StaticPrompt (p, "Inst", 0, 0, programFont, 'r');
7796   dlg->institution_code = DialogText (p, "", 4, SpecimenVoucherTextChange);
7797   SetObjectExtra (dlg->institution_code, dlg, NULL);
7798   StaticPrompt (p, "Coll", 0, 0, programFont, 'r');
7799   dlg->collection_code = DialogText (p, "", 4, SpecimenVoucherTextChange);
7800   SetObjectExtra (dlg->collection_code, dlg, NULL);
7801 
7802   StaticPrompt (p, "SpecID/Text", 0, 0, programFont, 'r');
7803   dlg->free_text = DialogText (p, "", width - 8, SpecimenVoucherTextChange);
7804   SetObjectExtra (dlg->free_text, dlg, NULL);
7805   
7806   return (DialoG) p;
7807 }
7808 
7809 
7810 static Boolean ParseSpecimenVoucherOk (CharPtr str)
7811 {
7812   return TRUE;
7813 }
7814 
7815 
7816 typedef DialoG (*MakeValueEditDialogFunc) PROTO ((GrouP, Int2, ValueListParentPtr, TaglistCallback, Pointer));
7817 
7818 static MakeValueEditDialogFunc value_edit_dialog_list[] = {
7819   SimpleTextValueEditDialog,
7820   TrueFalseValueEditDialog,
7821   ValueListLatLonDialog,
7822   SpecimenVoucherDialog
7823 };
7824 
7825 
7826 extern NameValuePairPtr NameValuePairFree (NameValuePairPtr nvp)
7827 {
7828   if (nvp != NULL)
7829   {
7830     nvp->name_vnp = ValNodeFreeData (nvp->name_vnp);
7831     nvp->value = MemFree (nvp->value);
7832     nvp = MemFree (nvp);
7833   }
7834   return nvp;
7835 }
7836 
7837 
7838 extern NameValuePairPtr NameValuePairCopy (NameValuePairPtr nvp)
7839 {
7840   NameValuePairPtr cpy = NULL;
7841 
7842   if (nvp != NULL)
7843   {
7844     cpy = (NameValuePairPtr) MemNew (sizeof (NameValuePairData));
7845     if (nvp->name_vnp != NULL)
7846     {
7847       cpy->name_vnp = ValNodeNew (NULL);
7848       cpy->name_vnp->choice = nvp->name_vnp->choice;
7849       cpy->name_vnp->data.ptrvalue = StringSave (nvp->name_vnp->data.ptrvalue);
7850     }
7851     cpy->value = StringSave (nvp->value);
7852   }
7853   return cpy;
7854 }
7855 
7856 
7857 extern ValNodePtr NameValuePairListFree (ValNodePtr vnp)
7858 {
7859   ValNodePtr vnp_next;
7860 
7861   while (vnp != NULL)
7862   {
7863     vnp->data.ptrvalue = NameValuePairFree (vnp->data.ptrvalue);
7864     vnp_next = vnp->next;
7865     vnp->next= NULL;
7866     vnp = ValNodeFree (vnp);
7867     vnp = vnp_next;
7868   }
7869   return vnp;
7870 }
7871 
7872 
7873 typedef struct valuelistrowdialog {
7874   DIALOG_MESSAGE_BLOCK
7875 
7876   TaglistCallback change_notify;
7877   Pointer         change_userdata;
7878   DialoG          parent_dlg;
7879   DialoG          name_dlg;
7880   DialoG          editors[eNumValueEditors];
7881   Int4            current_editor;
7882 } ValueListRowDialogData, PNTR ValueListRowDialogPtr;
7883 
7884 
7885 static void ChangeValueListRowName (Pointer data)
7886 {
7887   ValueListRowDialogPtr dlg;
7888   ValNodePtr            vnp;
7889   Int4                  i;
7890   CharPtr               value = NULL;
7891 
7892   dlg = (ValueListRowDialogPtr) data;
7893   if (dlg == NULL) return;
7894 
7895   if (dlg->current_editor > -1)
7896   {
7897     value = (CharPtr) DialogToPointer (dlg->editors[dlg->current_editor]);
7898   }
7899   for (i = 0; i < eNumValueEditors; i++)
7900   {
7901     Hide (dlg->editors[i]);
7902   }
7903   vnp = (ValNodePtr) DialogToPointer (dlg->name_dlg);
7904   if (vnp != NULL && vnp->choice > 0)
7905   {
7906     dlg->current_editor = vnp->choice - 1;
7907     Show (dlg->editors[dlg->current_editor]);
7908     PointerToDialog (dlg->editors[dlg->current_editor], value);
7909     vnp = ValNodeFree (vnp);
7910   } else {
7911     dlg->current_editor = -1;
7912   }
7913   value = MemFree (value);
7914 
7915   if (dlg->change_notify != NULL)
7916   {
7917     (dlg->change_notify) (dlg->change_userdata);
7918   }
7919 }
7920 
7921 
7922 static void NameValuePairToDialog (DialoG d, Pointer data)
7923 {
7924   ValueListRowDialogPtr dlg;
7925   NameValuePairPtr      nvp;
7926   ValNode               vn;
7927   Int4                  i;
7928 
7929   dlg = (ValueListRowDialogPtr) GetObjectExtra (d);
7930   nvp = (NameValuePairPtr) data;
7931 
7932   if (dlg == NULL) return;
7933   if (nvp == NULL)
7934   {
7935     vn.next = NULL;
7936     vn.choice = 0;
7937     vn.data.ptrvalue = " ";
7938     PointerToDialog (dlg->name_dlg, &vn);
7939     for (i = 0; i < eNumValueEditors; i++)
7940     {
7941       PointerToDialog (dlg->editors[i], NULL);
7942     }
7943     ChangeValueListRowName (dlg);
7944   }
7945   else
7946   {
7947     PointerToDialog (dlg->name_dlg, nvp->name_vnp);
7948     ChangeValueListRowName (dlg);
7949     if (dlg->current_editor > -1)
7950     {
7951       PointerToDialog (dlg->editors[dlg->current_editor], nvp->value);
7952       if (dlg->change_notify != NULL)
7953       {
7954         (dlg->change_notify) (dlg->change_userdata);
7955       }
7956     }
7957   }    
7958 }
7959 
7960 
7961 static Pointer DialogToNameValuePair (DialoG d)
7962 {
7963   ValueListRowDialogPtr dlg;
7964   NameValuePairPtr      nvp;
7965   ValNodePtr            name_vnp;
7966 
7967   dlg = (ValueListRowDialogPtr) GetObjectExtra (d);
7968   if (dlg == NULL || dlg->current_editor < 0) return NULL;
7969 
7970   name_vnp = DialogToPointer (dlg->name_dlg);
7971   if (name_vnp == NULL) return NULL;
7972 
7973   nvp = (NameValuePairPtr) MemNew (sizeof (NameValuePairData));
7974   nvp->name_vnp = name_vnp;
7975   nvp->value = DialogToPointer (dlg->editors[dlg->current_editor]);
7976   return nvp;
7977 }
7978 
7979 
7980 static ValNodePtr CopyChoiceList (ValNodePtr orig)
7981 {
7982   ValNodePtr vnp, cpy = NULL, prev = NULL;
7983 
7984   while (orig != NULL)
7985   {
7986     vnp = ValNodeNew (NULL);
7987     vnp->choice = orig->choice;
7988     vnp->data.ptrvalue = StringSave (orig->data.ptrvalue);
7989     if (prev == NULL)
7990     {
7991       cpy = vnp;
7992     }
7993     else
7994     {
7995       prev->next = vnp;
7996     }
7997     prev = vnp;
7998     orig = orig->next;
7999   }
8000   return cpy;
8001 }
8002 
8003 static ValNodePtr TestValueListRowDialog (DialoG d)
8004 {
8005   ValueListRowDialogPtr dlg;
8006   ValNodePtr            name_vnp, err_list = NULL;
8007   CharPtr               val, err_str;
8008   CharPtr               err_fmt = "No modifier type selected for data %s";
8009 
8010   dlg = (ValueListRowDialogPtr) GetObjectExtra (d);
8011   if (dlg == NULL || dlg->current_editor < 0) return NULL;
8012 
8013   val = DialogToPointer (dlg->editors[dlg->current_editor]);
8014   if (!StringHasNoText (val))
8015   {
8016     name_vnp = DialogToPointer (dlg->name_dlg);
8017     if (name_vnp == NULL || name_vnp->choice == 0)
8018     {
8019       err_str = (CharPtr) MemNew (sizeof (Char) * (StringLen (err_fmt) + StringLen (val)));
8020       sprintf (err_str, err_fmt, val);
8021       err_list = ValNodeNew(NULL);
8022       err_list->choice = 0;     
8023       err_list->data.ptrvalue = err_str;
8024     }
8025     name_vnp = ValNodeFree (name_vnp);
8026   }
8027   val = MemFree (val);
8028   return err_list;
8029 }
8030 
8031 
8032 static void ClearValueListRow (ButtoN b)
8033 {
8034   ValueListRowDialogPtr dlg;
8035 
8036   dlg = (ValueListRowDialogPtr) GetObjectExtra (b);
8037 
8038   if (dlg != NULL)
8039   {  
8040     PointerToDialog (dlg->dialog, NULL);
8041     if (dlg->change_notify != NULL) 
8042     {
8043       (dlg->change_notify) (dlg->change_userdata);
8044     }
8045     SendMessageToDialog (dlg->parent_dlg, VIB_MSG_REDRAW);
8046   }
8047 }
8048  
8049  
8050 static DialoG ValueListRowDialog (GrouP h, Int2 width, ValNodePtr choice_list, ValueListParentPtr parent,
8051                                   TaglistCallback change_notify, Pointer change_userdata, DialoG parent_dlg)
8052 {
8053   ValueListRowDialogPtr dlg;
8054   GrouP           p, g;
8055   Int4            i;
8056   ValNode         vn;
8057   ButtoN          b;
8058 
8059   p = HiddenGroup (h, 3, 0, NULL);
8060   SetGroupSpacing (p, 2, 2);
8061   dlg = (ValueListRowDialogPtr) MemNew (sizeof(ValueListRowDialogData));
8062 
8063   SetObjectExtra (p, dlg, StdCleanupExtraProc);
8064   dlg->dialog = (DialoG) p;
8065   dlg->todialog = NameValuePairToDialog;
8066   dlg->fromdialog = DialogToNameValuePair;
8067   dlg->testdialog = TestValueListRowDialog;
8068   dlg->dialogmessage = NULL;
8069   dlg->change_notify = change_notify;
8070   dlg->change_userdata = change_userdata;
8071   dlg->parent_dlg = parent_dlg;
8072   
8073   dlg->name_dlg = ValNodeSelectionDialog (p, CopyChoiceList(choice_list), 1, 
8074                                      ValNodeStringName,
8075                                      ValNodeSimpleDataFree, ValNodeStringCopy,
8076                                      ValNodeStringMatch, NULL,
8077                                      ChangeValueListRowName, dlg, FALSE);
8078   vn.next = NULL;
8079   vn.choice = 0;
8080   vn.data.ptrvalue = " ";
8081   PointerToDialog (dlg->name_dlg, &vn);
8082 
8083   g = HiddenGroup (p, 0, 0, NULL);
8084   for (i = 0; i < eNumValueEditors; i++)
8085   {
8086     dlg->editors[i] = (value_edit_dialog_list[i]) (g, width, parent, change_notify, change_userdata);
8087   }
8088 
8089   b = PushButton (p, "X", ClearValueListRow);
8090   SetObjectExtra (b, dlg, NULL);
8091 
8092   ChangeValueListRowName (dlg);
8093     
8094   return (DialoG) p;
8095 }
8096 
8097 typedef struct valuelistdialog {
8098   DIALOG_MESSAGE_BLOCK
8099   TaglistCallback change_notify;
8100   Pointer         change_userdata;
8101   Int4            num_rows;
8102   DialoG *        rows;
8103   BaR             left_bar;
8104   BaR             right_bar;
8105   ValNodePtr      choice_list;
8106   ValNodePtr      value_list;
8107   Int4            scroll_pos;
8108   Boolean         suppress_notify;
8109 } ValueListDialogData, PNTR ValueListDialogPtr;
8110 
8111 
8112 static void CleanupValueListDialog (GraphiC g, VoidPtr data)
8113 
8114 {
8115   ValueListDialogPtr dlg;
8116   
8117   dlg = (ValueListDialogPtr) data;
8118   if (dlg != NULL)
8119   {
8120     dlg->rows = MemFree (dlg->rows);
8121   }
8122 
8123   StdCleanupExtraProc (g, data);
8124 }
8125 
8126 
8127 static NameValuePairPtr GetNthNameValuePair (Int4 n, ValNodePtr list)
8128 {
8129   while (n > 0 && list != NULL)
8130   {
8131     n--;
8132     list = list->next;
8133   }
8134   if (list == NULL)
8135   {
8136     return NULL;
8137   }
8138   else 
8139   {
8140     return (NameValuePairPtr) list->data.ptrvalue;
8141   }
8142 }
8143 
8144 static void PopulateValueListRows (ValueListDialogPtr dlg)
8145 {
8146   Int4 n;
8147   ValNodePtr vnp;
8148 
8149   n = dlg->scroll_pos;
8150   vnp = dlg->value_list;
8151 
8152   dlg->suppress_notify = TRUE;
8153 
8154   while (n > 0 && vnp != NULL)
8155   {
8156     n--;
8157     vnp = vnp->next;
8158   }
8159   for (n = 0; n < dlg->num_rows; n++)
8160   {
8161     if (vnp == NULL)
8162     {  
8163       PointerToDialog (dlg->rows[n], NULL);
8164     }
8165     else 
8166     {
8167       PointerToDialog (dlg->rows[n], vnp->data.ptrvalue);
8168       vnp = vnp->next;
8169     }
8170   }
8171   dlg->suppress_notify = FALSE;
8172 }
8173 
8174 
8175 static Boolean IsNameValuePairEmpty (NameValuePairPtr nvp)
8176 {
8177   if (nvp == NULL) 
8178   {
8179     return TRUE;
8180   }
8181   else if ((nvp->name_vnp == NULL || nvp->name_vnp->choice == 0 || StringHasNoText (nvp->name_vnp->data.ptrvalue)) /* no choice */
8182             && StringHasNoText (nvp->value)) /* no value */
8183   {
8184     return TRUE;
8185   }
8186   else 
8187   {
8188     return FALSE;
8189   }
8190 }
8191 
8192 /* Only trim blanks from the end of the list */
8193 static void RemoveBlanks (ValNodePtr PNTR pvnp)
8194 {
8195   ValNodePtr vnp, last_non_blank = NULL;
8196 
8197   if (pvnp == NULL) return;
8198 
8199   vnp = *pvnp;
8200   while (vnp != NULL) 
8201   {
8202     if (!IsNameValuePairEmpty ((NameValuePairPtr) vnp->data.ptrvalue)) 
8203     {
8204       last_non_blank = vnp;
8205     }
8206     vnp = vnp->next;
8207   }
8208   if (last_non_blank != NULL) {
8209     last_non_blank->next = NameValuePairListFree (last_non_blank->next);
8210   }
8211 }
8212 
8213 static void ValueListPairToDialog (DialoG d, Pointer data)
8214 {
8215   ValueListDialogPtr dlg;
8216   Int4               num_vals;
8217 
8218   dlg = (ValueListDialogPtr) GetObjectExtra (d);
8219   if (dlg == NULL) return;
8220 
8221   dlg->value_list = NameValuePairListFree (dlg->value_list);
8222   dlg->value_list = (ValNodePtr) data;
8223   RemoveBlanks (&(dlg->value_list));
8224 
8225   num_vals = ValNodeLen (dlg->value_list);
8226   if (num_vals >= dlg->num_rows)
8227   {
8228     SetBarMax (dlg->left_bar, num_vals + 1 - dlg->num_rows);
8229     SetBarMax (dlg->right_bar, num_vals + 1 - dlg->num_rows);
8230   }
8231   else
8232   {
8233     SetBarMax (dlg->left_bar, 0);
8234     SetBarMax (dlg->right_bar, 0);
8235   }
8236   if (GetValue (dlg->left_bar) > GetBarMax (dlg->left_bar))
8237   {
8238     CorrectBarValue (dlg->left_bar, 0);
8239     CorrectBarValue (dlg->right_bar, 0);
8240   }
8241   PopulateValueListRows (dlg);
8242 }
8243 
8244 
8245 static void ScrollValueListProc (BaR b, GraphiC g, Int2 _new, Int2 _old)
8246 {
8247   ValueListDialogPtr dlg;
8248 
8249   dlg = (ValueListDialogPtr) GetObjectExtra (b);
8250   if (dlg == NULL) return;
8251 
8252   /* synchronize left and right scroll bars */
8253   if (b == dlg->right_bar && dlg->left_bar != NULL)
8254   {
8255     CorrectBarValue (dlg->left_bar, GetBarValue (dlg->right_bar));
8256   }
8257   else if (b == dlg->left_bar && dlg->right_bar != NULL)
8258   {
8259     CorrectBarValue (dlg->right_bar, GetBarValue (dlg->left_bar));      
8260   }
8261 
8262   dlg->scroll_pos = _new;
8263   PopulateValueListRows (dlg);
8264 }
8265 
8266 
8267 static void ValueListDialogMessage (DialoG d, Int2 mssg)
8268 
8269 {
8270   ValNodePtr         new_value_list;
8271 
8272   switch (mssg) {
8273     case VIB_MSG_REDRAW :
8274       new_value_list = DialogToPointer (d);
8275       PointerToDialog (d, new_value_list);
8276       break;
8277     default :
8278       break;
8279   }
8280 }
8281 
8282 
8283 static void ChangeValueRow (Pointer data)
8284 {
8285   ValueListDialogPtr dlg;
8286   NameValuePairPtr   nvp;
8287   Int4               max, i;
8288   ValNodePtr         vnp;
8289 
8290   dlg = (ValueListDialogPtr) data;
8291 
8292   if (dlg == NULL) return;
8293 
8294   if (dlg->suppress_notify) return;
8295 
8296   /* copy values into list */
8297   /* first, skip over rows scrolled past */
8298   vnp = dlg->value_list;
8299   i = 0;
8300   while (i < dlg->scroll_pos)
8301   {
8302     if (vnp == NULL)
8303     {
8304       vnp = ValNodeAddPointer (&dlg->value_list, 0, NULL);
8305     }
8306     vnp = vnp->next;
8307     i++;
8308   }
8309   
8310   /* now copy in rows we can see */    
8311   for (i = 0; i < dlg->num_rows; i++)
8312   {
8313     if (vnp == NULL)
8314     {
8315       vnp = ValNodeAddPointer (&dlg->value_list, 0, DialogToPointer (dlg->rows[i]));
8316     }
8317     else
8318     {
8319       vnp->data.ptrvalue = NameValuePairFree (vnp->data.ptrvalue);
8320       vnp->data.ptrvalue = DialogToPointer (dlg->rows[i]);
8321     }
8322     vnp = vnp->next;
8323   }
8324   
8325 
8326   /* if editing last row, extend scrollbar for new row */
8327   max = GetBarMax (dlg->left_bar);
8328   if (dlg->scroll_pos == max)
8329   {
8330     nvp = DialogToPointer (dlg->rows[dlg->num_rows - 1]);
8331     if (!IsNameValuePairEmpty (nvp))
8332     {
8333       SetBarMax (dlg->left_bar, max + 1);
8334       SetBarMax (dlg->right_bar, max + 1); 
8335     }
8336     nvp = NameValuePairFree(nvp);    
8337   }   
8338 
8339   if (dlg->change_notify != NULL)
8340   {
8341     (dlg->change_notify)(dlg->change_userdata);
8342   }
8343 }
8344 
8345 
8346 static Pointer DialogToNameValuePairList (DialoG d)
8347 {
8348   ValueListDialogPtr dlg;
8349   ValNodePtr         value_list = NULL, vnp;
8350 
8351   dlg = (ValueListDialogPtr) GetObjectExtra (d);
8352   if (dlg == NULL) return NULL;
8353 
8354   for (vnp = dlg->value_list; vnp != NULL; vnp = vnp->next)
8355   {
8356     if (vnp->data.ptrvalue != NULL) 
8357     {
8358       ValNodeAddPointer (&value_list, 0, NameValuePairCopy (vnp->data.ptrvalue));
8359     }
8360   }
8361   return (Pointer) value_list;
8362 }
8363 
8364 
8365 static ValNodePtr TestValueListDialog (DialoG d)
8366 {
8367   ValNodePtr       err_list = NULL, value_list = NULL, vnp;
8368   NameValuePairPtr nvp;
8369   CharPtr          err_fmt = "No modifier type selected for data %s";
8370   CharPtr          err_str;
8371 
8372   value_list = DialogToPointer (d);
8373 
8374   for (vnp = value_list; vnp != NULL; vnp = vnp->next)
8375   {
8376     nvp = (NameValuePairPtr) vnp->data.ptrvalue;
8377     if (nvp != NULL 
8378         && !StringHasNoText (nvp->value) 
8379         && (nvp->name_vnp == NULL || StringHasNoText (nvp->name_vnp->data.ptrvalue)))
8380     {
8381       err_str = (CharPtr) MemNew (sizeof (Char) * (StringLen (err_fmt) + StringLen (nvp->value)));
8382       sprintf (err_str, err_fmt, nvp->value);
8383       ValNodeAddPointer (&err_list, 0, err_str);
8384     }
8385   }
8386   value_list = NameValuePairListFree (value_list);
8387   return err_list;
8388 }
8389 
8390 extern DialoG ValueListDialog (GrouP h, Uint2 num_rows, Int2 width, ValNodePtr choice_list, ValueListParentPtr parent, TaglistCallback change_notify, Pointer change_userdata)
8391 {
8392   ValueListDialogPtr dlg;
8393   GrouP           p, g;
8394   Int4            i;
8395 
8396   p = HiddenGroup (h, 3, 0, NULL);
8397   SetGroupSpacing (p, 2, 2);
8398   dlg = (ValueListDialogPtr) MemNew (sizeof(ValueListDialogData));
8399 
8400   SetObjectExtra (p, dlg, CleanupValueListDialog);
8401   dlg->dialog = (DialoG) p;
8402   dlg->todialog = ValueListPairToDialog;
8403   dlg->fromdialog = DialogToNameValuePairList;
8404   dlg->testdialog = TestValueListDialog;
8405   dlg->dialogmessage = ValueListDialogMessage;
8406   dlg->change_notify = change_notify;
8407   dlg->change_userdata = change_userdata;
8408   
8409   dlg->num_rows = num_rows;
8410 
8411   /* navigation bar on left */
8412   dlg->left_bar = ScrollBar (p, 0, dlg->num_rows, ScrollValueListProc);
8413   SetObjectExtra (dlg->left_bar, dlg, NULL);
8414   CorrectBarPage (dlg->left_bar, dlg->num_rows - 1, dlg->num_rows - 1);
8415 
8416   g = HiddenGroup (p, 1, 0, NULL);
8417   dlg->rows = (DialoG *) MemNew (num_rows * sizeof (DialoG));
8418   for (i = 0; i < dlg->num_rows; i++)
8419   {
8420     dlg->rows[i] = ValueListRowDialog (g, width, choice_list, parent, ChangeValueRow, dlg, (DialoG) p);
8421 
8422   }
8423 
8424   /* navigation bar on right */
8425   dlg->right_bar = ScrollBar (p, 0, dlg->num_rows, ScrollValueListProc);
8426   SetObjectExtra (dlg->right_bar, dlg, NULL);
8427   CorrectBarPage (dlg->right_bar, dlg->num_rows - 1, dlg->num_rows - 1);
8428 
8429   AlignObjects (ALIGN_LOWER, (HANDLE) g, (HANDLE) dlg->left_bar, (HANDLE) dlg->right_bar, NULL);
8430 
8431 
8432   return (DialoG) p;
8433 }
8434 
8435 
8436 typedef struct modifierlistdlg {
8437   VALUE_LIST_PARENT_FIELDS
8438 
8439   DialoG dlg;
8440   ButtoN type_strain;
8441   TexT   taxname;
8442 
8443   EnumFieldAssocPtr al;
8444 } ModifierListDlgData, PNTR ModifierListDlgPtr;
8445 
8446 
8447 static ValNodePtr TestModifierListDlg (DialoG d)
8448 {
8449   ModifierListDlgPtr dlg = (ModifierListDlgPtr) GetObjectExtra (d);
8450   if (dlg == NULL) return NULL;
8451   return TestDialog (dlg->dlg);
8452 }
8453 
8454 
8455 
8456 static ValNodePtr SubSourceListToNameValueList (SubSourcePtr ssp)
8457 {
8458   ValNodePtr val_list = NULL;
8459   NameValuePairPtr nvp;
8460 
8461   while (ssp != NULL)
8462   {
8463     if (ssp->subtype != SUBSRC_other)
8464     {
8465       nvp = (NameValuePairPtr) MemNew (sizeof (NameValuePairData));
8466       nvp->name_vnp = ValNodeNew(NULL);
8467       nvp->name_vnp->choice = 1;
8468       nvp->name_vnp->data.ptrvalue = StringSave (GetSubsourceQualName (ssp->subtype));
8469       nvp->value = StringSave (ssp->name);
8470       ValNodeAddPointer (&val_list, 0, nvp);
8471     }
8472     ssp = ssp->next;
8473   }
8474   return val_list;
8475 
8476 }
8477 
8478 
8479 static void SubSourceListToDialog (DialoG d, Pointer data)
8480 {
8481   ModifierListDlgPtr dlg;
8482   SubSourcePtr ssp;
8483   ValNodePtr   vnp;
8484   CharPtr      note_txt = NULL;
8485 
8486   dlg = (ModifierListDlgPtr) GetObjectExtra (d);
8487 
8488   if (dlg == NULL) return;
8489 
8490   ssp = (SubSourcePtr) data;
8491 
8492   vnp = SubSourceListToNameValueList (ssp);
8493   PointerToDialog (dlg->dlg, vnp);
8494 
8495   while (ssp != NULL) 
8496   {
8497     if (ssp->subtype == SUBSRC_other)
8498     {
8499       note_txt = combine_strings (note_txt, ssp->name);
8500     }
8501     ssp = ssp->next;
8502   }
8503   SetTitle (dlg->note, note_txt);
8504 
8505 }
8506 
8507 
8508 static Pointer DialogToSubSourceList (DialoG d)
8509 {
8510   ModifierListDlgPtr dlg;
8511   ValNodePtr   val_list, vnp;
8512   NameValuePairPtr nvp;
8513   SubSourcePtr     ssp_list = NULL, ssp_prev = NULL, ssp_new;
8514   Uint1            subtype;
8515   CharPtr          comment_str;
8516 
8517   dlg = (ModifierListDlgPtr) GetObjectExtra (d);
8518 
8519   if (dlg == NULL) return NULL;
8520 
8521   val_list = (ValNodePtr) DialogToPointer (dlg->dlg);
8522   for (vnp = val_list; vnp != NULL; vnp = vnp->next)
8523   {
8524     if (vnp->data.ptrvalue != NULL) 
8525     {
8526       nvp = (NameValuePairPtr) vnp->data.ptrvalue;
8527       if (nvp != NULL && nvp->name_vnp != NULL)
8528       {        
8529         subtype = EquivalentSubSourceEx (nvp->name_vnp->data.ptrvalue, TRUE);
8530         if (subtype != 0)
8531         {
8532           ssp_new = SubSourceNew ();
8533           ssp_new->subtype = subtype;
8534           ssp_new->name = StringSave (nvp->value == NULL ? "" : nvp->value);
8535           if (ssp_prev == NULL)
8536           {
8537             ssp_list = ssp_new;
8538           }
8539           else
8540           {
8541             ssp_prev->next = ssp_new;
8542           }
8543           ssp_prev = ssp_new;
8544         }
8545       }
8546     }
8547   }
8548   val_list = NameValuePairListFree (val_list);
8549 
8550   /* add comment */
8551   comment_str = SaveStringFromText (dlg->note);
8552   if (!StringHasNoText (comment_str))
8553   {
8554     ssp_new = SubSourceNew();
8555     ssp_new->subtype = SUBSRC_other;
8556     ssp_new->name = StringSave (comment_str);
8557     if (ssp_prev == NULL)
8558     {
8559       ssp_list = ssp_new;
8560     }
8561     else
8562     {
8563       ssp_prev->next = ssp_new;
8564     }
8565     ssp_prev = ssp_new;
8566   }
8567 
8568   return ssp_list;
8569 }
8570 
8571 
8572 static ValNodePtr GetSubSourceChoicesForValueList (EnumFieldAssocPtr al)
8573 {
8574   ValNodePtr choice_list = NULL;
8575   EnumFieldAssocPtr efap;
8576 
8577   efap = al;
8578   while (efap->name != NULL)
8579   {
8580     if (StringHasNoText (efap->name))
8581     {
8582       ValNodeAddStr (&choice_list, eValueEditSimpleText + 1, StringSave (efap->name));
8583     }
8584     else if (efap->value == SUBSRC_lat_lon)
8585     {
8586       ValNodeAddStr (&choice_list, eValueEditLatLon + 1, StringSave (efap->name));
8587     }
8588     else if (IsNonTextModifier (efap->name))
8589     {
8590       ValNodeAddStr (&choice_list, eValueEditTrueFalse + 1, StringSave (efap->name));
8591     }
8592     else
8593     {
8594       ValNodeAddStr (&choice_list, eValueEditSimpleText + 1, StringSave (efap->name));
8595     }
8596     efap ++;
8597   }
8598   return choice_list;
8599 }
8600 
8601 
8602 static CharPtr subsource_extra_prompts [] = {
8603   "Additional", "Source", "Information", NULL
8604 };
8605 
8606 extern DialoG CreateSubSourceDialog (GrouP h, EnumFieldAssocPtr al)
8607 {
8608   ModifierListDlgPtr dlg;
8609   GrouP        p, g, x;
8610   ValNodePtr   choice_list;
8611   Int2         max;
8612   
8613 
8614   p = HiddenGroup (h, -1, 0, NULL);
8615   SetGroupSpacing (p, 10, 10);
8616   dlg = (ModifierListDlgPtr) MemNew (sizeof(ModifierListDlgData));
8617 
8618   SetObjectExtra (p, dlg, StdCleanupExtraProc);
8619   dlg->dialog = (DialoG) p;
8620   dlg->todialog = SubSourceListToDialog;
8621   dlg->fromdialog = DialogToSubSourceList;
8622   dlg->testdialog = TestModifierListDlg;
8623   dlg->dialogmessage = NULL;
8624 
8625   choice_list = GetSubSourceChoicesForValueList (al);
8626   dlg->dlg = ValueListDialog (p, 3, 23, choice_list, (ValueListParentPtr) dlg, NULL, NULL);  
8627   choice_list = ValNodeFreeData (choice_list);
8628 
8629   g = HiddenGroup (p, 2, 0, NULL);
8630   SelectFont (programFont);
8631   max = MaxStringWidths (subsource_extra_prompts) + 2;
8632   x = MultiLinePrompt (g, "Additional Source Information", max, programFont);
8633   dlg->note = ScrollText (g, 20, 3, programFont, TRUE, NULL);
8634   AlignObjects (ALIGN_MIDDLE, (HANDLE) x, (HANDLE) dlg->note, NULL);
8635 
8636   AlignObjects (ALIGN_CENTER, (HANDLE) dlg->dlg, (HANDLE) g, NULL);
8637 
8638   return (DialoG) p;
8639 }
8640 
8641 
8642 static ValNodePtr OrgModListToNameValueList (OrgModPtr mod)
8643 {
8644   ValNodePtr val_list = NULL;
8645   NameValuePairPtr nvp;
8646 
8647   while (mod != NULL)
8648   {
8649     if (mod->subtype != ORGMOD_gb_acronym
8650         && mod->subtype != ORGMOD_gb_anamorph
8651         && mod->subtype != ORGMOD_gb_synonym
8652         && mod->subtype != ORGMOD_other)
8653     {
8654       nvp = (NameValuePairPtr) MemNew (sizeof (NameValuePairData));
8655       nvp->name_vnp = ValNodeNew(NULL);
8656       nvp->name_vnp->choice = 1;
8657       nvp->name_vnp->data.ptrvalue = StringSave (GetOrgModQualName (mod->subtype));
8658       nvp->value = StringSave (mod->subname);
8659       ValNodeAddPointer (&val_list, 0, nvp);
8660     }
8661     mod = mod->next;
8662   }
8663   return val_list;
8664 
8665 }
8666 
8667 
8668 static void OrgModListToDialog (DialoG d, Pointer data)
8669 {
8670   ModifierListDlgPtr dlg;
8671   OrgModPtr          mod;
8672   ValNodePtr         vnp;
8673   CharPtr            note_txt = NULL;
8674 
8675   dlg = (ModifierListDlgPtr) GetObjectExtra (d);
8676 
8677   if (dlg == NULL) return;
8678 
8679   mod = (OrgModPtr) data;
8680 
8681   vnp = OrgModListToNameValueList (mod);
8682   PointerToDialog (dlg->dlg, vnp);
8683 
8684   while (mod != NULL) 
8685   {
8686     if (mod->subtype == ORGMOD_other)
8687     {
8688       note_txt = combine_strings (note_txt, mod->subname);
8689     }
8690     mod = mod->next;
8691   }
8692   SetTitle (dlg->note, note_txt);
8693 
8694 }
8695 
8696 
8697 static Pointer DialogToOrgModList (DialoG d)
8698 {
8699   ModifierListDlgPtr dlg;
8700   ValNodePtr   val_list, vnp;
8701   NameValuePairPtr nvp;
8702   OrgModPtr        mod_list = NULL, mod_prev = NULL, mod_new;
8703   Uint1            subtype;
8704   CharPtr          comment_str;
8705 
8706   dlg = (ModifierListDlgPtr) GetObjectExtra (d);
8707 
8708   if (dlg == NULL) return NULL;
8709 
8710   val_list = (ValNodePtr) DialogToPointer (dlg->dlg);
8711   for (vnp = val_list; vnp != NULL; vnp = vnp->next)
8712   {
8713     if (vnp->data.ptrvalue != NULL) 
8714     {
8715       nvp = (NameValuePairPtr) vnp->data.ptrvalue;
8716       if (nvp != NULL && nvp->name_vnp != NULL)
8717       {        
8718         subtype = EquivalentOrgModEx (nvp->name_vnp->data.ptrvalue, TRUE);
8719         if (subtype != 0)
8720         {
8721           mod_new = OrgModNew ();
8722           mod_new->subtype = subtype;
8723           mod_new->subname = StringSave (nvp->value == NULL ? "" : nvp->value);
8724           if (mod_prev == NULL)
8725           {
8726             mod_list = mod_new;
8727           }
8728           else
8729           {
8730             mod_prev->next = mod_new;
8731           }
8732           mod_prev = mod_new;
8733         }
8734       }
8735     }
8736   }
8737   val_list = NameValuePairListFree (val_list);
8738 
8739   /* add comment */
8740   comment_str = SaveStringFromText (dlg->note);
8741   if (!StringHasNoText (comment_str))
8742   {
8743     mod_new = OrgModNew();
8744     mod_new->subtype = ORGMOD_other;
8745     mod_new->subname = StringSave (comment_str);
8746     if (mod_prev == NULL)
8747     {
8748       mod_list = mod_new;
8749     }
8750     else
8751     {
8752       mod_prev->next = mod_new;
8753     }
8754     mod_prev = mod_new;
8755   }
8756 
8757   return mod_list;
8758 }
8759 
8760 
8761 extern Boolean IsNonTextModifier (CharPtr mod_name)
8762 {
8763   if (StringICmp (mod_name, "transgenic") == 0
8764       || StringICmp (mod_name, "germline") == 0
8765       || StringICmp (mod_name, "metagenomic") == 0
8766       || StringICmp (mod_name, "environmental-sample") ==0
8767       || StringICmp (mod_name, "rearranged") == 0)
8768   {
8769     return TRUE;  
8770   }
8771   else
8772   {
8773     return FALSE;
8774   }
8775 }
8776 
8777 
8778 static ValNodePtr GetOrgModChoicesForValueList (EnumFieldAssocPtr al)
8779 {
8780   ValNodePtr choice_list = NULL;
8781   EnumFieldAssocPtr efap;
8782 
8783   efap = al;
8784   while (efap->name != NULL)
8785   {
8786     if (StringHasNoText (efap->name))
8787     {
8788       ValNodeAddStr (&choice_list, eValueEditSimpleText + 1, StringSave (efap->name));
8789     }
8790     else if (IsNonTextModifier (efap->name))
8791     {
8792       ValNodeAddStr (&choice_list, eValueEditTrueFalse + 1, StringSave (efap->name));
8793     }
8794     else if (efap->value == ORGMOD_specimen_voucher || efap->value == ORGMOD_culture_collection || efap->value == ORGMOD_bio_material)
8795     {
8796       ValNodeAddStr (&choice_list, eValueEditSpecimenVoucher + 1, StringSave (efap->name));
8797     }
8798     else
8799     {
8800       ValNodeAddStr (&choice_list, eValueEditSimpleText + 1, StringSave (efap->name));
8801     }
8802     efap ++;
8803   }
8804   return choice_list;
8805 }
8806 
8807 
8808 static CharPtr orgmod_extra_prompts [] = {
8809   "Additional", "Organism", "Information", NULL
8810 };
8811 
8812 static void ChangeOrgmodComment (TexT t)
8813 {
8814   ModifierListDlgPtr dlg;
8815   CharPtr str;
8816 
8817   dlg = (ModifierListDlgPtr) GetObjectExtra (t);
8818   if (dlg == NULL) return;
8819 
8820   str = SaveStringFromText (t);
8821   if ( StringStr (str, "type strain of ")) {
8822     Disable (dlg->type_strain);
8823   }
8824 
8825 }  
8826 
8827 
8828 static void AddTypeStrainProc (ButtoN b)
8829 {
8830   ModifierListDlgPtr dlg;
8831   CharPtr        old_orgcomment;
8832   Int4           old_orgcomment_len;
8833   CharPtr        org_name;
8834   Int4           org_name_len;
8835   const CharPtr  ts = "type strain of ";
8836   const CharPtr  sep = "; ";
8837   CharPtr        new_orgcomment;
8838 
8839   dlg = (ModifierListDlgPtr) GetObjectExtra (b);
8840   if (dlg == NULL) return;
8841 
8842   old_orgcomment_len = TextLength (dlg->note) + 1;
8843   old_orgcomment = MemNew (old_orgcomment_len + 1);
8844   if (old_orgcomment == NULL) return;
8845   org_name_len = TextLength (dlg->taxname) + 1;
8846   org_name = MemNew (org_name_len + 1);
8847   if (org_name == NULL) 
8848   {
8849     MemFree (old_orgcomment);
8850     return;
8851   }
8852   new_orgcomment = MemNew (old_orgcomment_len
8853             + StringLen (sep)
8854             + StringLen (ts)
8855             + org_name_len
8856             + 1);
8857   if (new_orgcomment == NULL)
8858   {
8859     MemFree (old_orgcomment);
8860     MemFree (org_name);
8861   }
8862 
8863   GetTitle (dlg->note, old_orgcomment, old_orgcomment_len);
8864   TrimSpacesAroundString (old_orgcomment);
8865   GetTitle (dlg->taxname, org_name, org_name_len);
8866   TrimSpacesAroundString (org_name);
8867   if (old_orgcomment[0] != 0)
8868   {
8869     StringCpy(new_orgcomment, old_orgcomment);
8870     StringCat(new_orgcomment, sep);