NCBI C Toolkit Cross Reference

C/desktop/asn2graphic.c


  1 /*   asn2graphic.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:  asn2graphic.c
 27 * Author:  Eric Northup
 28 *
 29 * Version Creation Date:   11/8/01
 30 *
 31 * $Revision: 6.131 $
 32 *
 33 * File Description:
 34 *
 35 * Modifications:
 36 * --------------------------------------------------------------------------
 37 *
 38 * ==========================================================================
 39 */
 40 
 41 #include <asn2graphicp.h>
 42 #include <sequtil.h>
 43 #include <sqnutils.h>
 44 #include <explore.h>
 45 #include <drawingp.h>
 46 #include <alignmgr2.h>
 47 
 48 /* type used to accumulate align score weights, should be small to save space, but large enough we do not overflow. */
 49 typedef Int2 AccumValue_t;
 50 typedef AccumValue_t PNTR AccumValuePtr_t;
 51 #define ACCUMVALUE_MAX  INT2_MAX
 52 
 53 /*
 54   Given a sequence alignment, look at its score
 55   and return a suitable integer (AccumValue_t) weight.'
 56   scoretype gives which part of the score to use.
 57 */
 58 enum {
 59   NLM_SCORE_COUNT = 0,
 60   NLM_SCORE_SCORE,
 61   NLM_SCORE_BIT,
 62   NLM_SCORE_EVALUE,
 63   NLM_SCORE_NUMBER,
 64   NLM_SCORE_TOOBIG
 65 };
 66 
 67 
 68 #define RELEVANT_FEATS_PER_CHUNK 128
 69 
 70 /* these do not apply to individual features; they are used, ie in "AddFeatureToFilter (FEATDEF_ANY_RNA. . .)
 71    that would make the filter include _all_ types of RNA */
 72 #define FEATDEF_ANY_PROT (APPEARANCEITEM_MAX + 1)
 73 #define FEATDEF_ANY_RNA (APPEARANCEITEM_MAX + 2)
 74 
 75 typedef struct renderInput {
 76   AppearanceItemPtr AIP;
 77   FilterItemPtr     FIP;
 78   RelevantFeatureItemPtr RFIP;
 79   SegmenT         labelSeg;
 80   SegmenT         drawSeg;
 81   Int4            yStart;
 82   Int4            decorationLeft;
 83   Int4            decorationRight;
 84   Int4            decorationHeight;
 85   Uint2           featureOffset;
 86   Uint2           Height, rowHeight;
 87 } RenderInput, PNTR RenderInputPtr;
 88 
 89 typedef struct viewerInstanceData {
 90   BioseqPtr       BSP;
 91   Uint4           viewScale;
 92   Uint4           seqLength;
 93   Int4Ptr         ceiling;
 94   SeqAnnotPtr PNTR sapList;
 95   Uint2           sapCount;
 96   SeqAnnotPtr     currentSAP;
 97   SegmenT         drawOnMe;
 98   SegmenT         sanLevelSeg;
 99   SegmenT         topLevelSeg;
100   Uint4           featureCount;
101   ValNodePtr      featVNP;      /* data.ptrvalue == RelevantFeatureItem [RELEVANT_FEATS_PER_CHUNK] */
102   ValNodePtr      BSPsegmentVNP;
103   AppearancePtr   AppPtr;
104   FilterPtr       FltPtr;
105   LayoutAlgorithm overrideLayout;
106   Int4            gphheight;
107   SegmenT         gphseg;
108   Int4            gphyOffset;
109   Int4            from;
110   Int4            to;
111   Boolean         allFeatures;
112   GraphicViewExtrasPtr extras;
113 } ViewerContext, PNTR ViewerContextPtr;
114 
115 typedef struct featureFilterState {
116   ValNodePtr       currentRFIPblockVNP;
117   Uint4            featureBlockOffset;
118   Uint2            indexInBlock;
119 } FeatureFilterState, PNTR FeatureFilterStatePtr;
120 
121 #define MAX_ALIGN_SORT_LABEL  20
122 typedef struct seq_align_sort_info {
123   SeqAlignPtr sap;
124   Int4  start, stop;
125   Uint1 strand;
126   Char  label[MAX_ALIGN_SORT_LABEL + 1];
127   AccumValue_t  normScore;
128 } SeqAlignSortInfo, PNTR SeqAlignSortInfoPtr;
129 
130 typedef struct AlignmentFilterState {
131  /* SeqAlignPtr      SAPhead, SAPcurrent; */ /* obsolete */
132   SeqAlignSortInfoPtr alignSorted;  /* array of SeqAlignPtrs & Info about them, sorted */
133   Int4             alignSortedLen;  /* len of alignSorted */
134   Int4             alignIndex;      /* index of current alignment in alignSorted */
135   Uint1            scoreType;       /* what kind of score will we filter and weight by? */
136   Int2             cutoffPercent;   /* include alignments in alignSorted only if their score is in the top cutoffPercent */
137   AccumValue_t     cutoffScore;     /* normalised score Below which aligns won't get into alignSorted. */
138   AccumValue_t     cutoffScoreHi;   /* normalised score Above which aligns won't get into alignSorted. */
139   Int4             minScore, maxScore;  /* un-normalised min & max scores seen on these alignments */
140 } AlignmentFilterState, PNTR AlignmentFilterStatePtr;
141 
142 /*
143 Bioseq's are still special-case for now.  todo: make bioseq segments use this mechanism
144 */
145 
146 typedef union filterState {
147   FeatureFilterState feat;
148   AlignmentFilterState align;
149 } FilterState, PNTR FilterStatePtr;  
150 
151 typedef struct filterProcessState {
152   FilterState      state;
153   FilterItemPtr    currentFIP;
154   ValNodePtr       currentFilterVNP;
155   ValNodePtr       needFreeList; /* things to be freed with ValNodeFreeData() after finishing this filter */
156   ValNodePtr       lastInFreeList;
157   SegmenT          labelSegs [APPEARANCEITEM_MAX];
158   SegmenT          drawSegs [APPEARANCEITEM_MAX];
159   Int4             ceiling;
160   RenderInput      renderParm;
161   Uint4            featuresProcessedCount;
162   BoolPtr          featuresProcessed;    /*points to an array of boolean [vContext->featureCount] */
163   ViewerContextPtr vContext;
164   Boolean          groupBoxDrawn;
165   Boolean          groupLabelDrawn;
166   Boolean          annotBoxDrawn;
167   Boolean          annotLabelDrawn;
168 } FilterProcessState, PNTR FilterProcessStatePtr;
169 
170 typedef void (*RenderFuncPtr)    (RenderInputPtr rip, ViewerContextPtr vContext);
171 typedef void (*GetDimensionsPtr) (RenderInputPtr, Int4Ptr start, Int4Ptr stop, Uint2Ptr height, ViewerContextPtr vContext);
172 
173 typedef struct renderClass {
174   RenderFuncPtr RenderFunc;
175   GetDimensionsPtr GetDimensions;
176 } RenderClass, PNTR RenderClassPtr;
177 
178 typedef struct internalRow {
179   Uint4           rowFeatureCount;
180   DataVal         layoutData;
181   ValNodePtr      feats;        /* data.ptrvalue == RelevantFeatureItemPtr */
182   struct internalRow PNTR next;
183 } InternalRow, PNTR InternalRowPtr;
184 
185 /* returns the total number of _rows_ */
186 typedef Uint2   (PNTR LayoutFunction) (InternalRowPtr firstRow, FilterProcessStatePtr FPSP);
187 
188 
189 static CharPtr LayoutStrings [] = {
190   "Inherit", /* Do not override layout in filter */
191   "Diagonal", /* One feature per row */
192   /* "DiagonalSawtooth" */
193   "ByType", /* like "compact", except that no row will contain _different_ types of features (this may use more rows) */
194   "Smear", /* one row contains all features of a given type (overlapping features will render as an anonymous smear */
195   /*  "Single Row", -- not working currently*/ /* _all_ features are rendered in a single row (equivalent to "smear" if given only one type of features)*/
196   "Compact", /* Overlapping features are drawn in different rows, but this tries to minimize the number of rows, */
197   "GeneProducts", /* Group associated mRNA and CDS regions . . . */
198   "GeneProductsX", /* Like GeneProducts, but may display the same mRNA multiple times if it maps to multiple CDS regions. . . */
199   NULL
200 };
201 
202 static LayoutAlgorithm LayoutValues [] = {
203   Layout_Inherit,
204   Layout_Diagonal,
205   /* Layout_DiagonalSawtooth, */
206   Layout_FeatTypePerLineGroup,
207   Layout_FeatTypePerLine,
208   /*  Layout_AllInOneLine, not working currently (and not all that useful, either)*/
209   Layout_PackUpward,
210   Layout_GroupCorrespondingFeats,
211   Layout_GroupCorrespondingFeatsRepeat
212 };
213 
214 static CharPtr AlnScoreStrings [] = {
215     "None",
216     "Score",
217     "Bit Score",
218     "E-value",
219     "Number",
220     NULL
221 };
222 
223 /*
224 static CharPtr AlnScoreCutoffStrings[] = {
225     "100%",
226     "50%",
227     "20%",
228     "10%",
229     "5%",
230     "2%",
231     "1%",
232     NULL
233 };
234 
235 static Int2 AlnScoreCutoffValues [] = {
236     100, 
237     50,
238     20, 
239     10, 
240     5, 
241     2, 
242     1, 
243     0
244 };
245 */
246 
247 
248 static CharPtr AlnScoreCutoffStrings[] = {
249     "None",
250     "40",
251     "50",
252     "60",
253     "70",
254     "80",
255     "100",
256     "200",
257     "400",
258     NULL
259 };
260 
261 static Int2 AlnScoreCutoffValues [] = {
262     -1,
263     40,
264     50,
265     60,
266     70,
267     80,
268     100,
269     200,
270     400,
271     0
272 };
273 
274 static CharPtr  LlocStrings [] = {
275   "none",
276   "clip",
277   "cull",
278   "inside",
279   "above",
280   "below",
281   "left",
282   "right",
283   NULL
284 };
285 
286 static LabelLocEnum LlocValues [] = {
287   LabelNone,
288   LabelAboveClip, /* above, but not wider than the feature*/
289   LabelAboveCull, /* above, but only displayed iff wider than the feature */
290   LabelInside,
291   LabelAbove,
292   LabelBelow,
293   LabelLeft,
294   LabelRight
295 };
296 
297 static CharPtr  BoolStrings [] = {
298   "yes",
299   "true",
300   "on",
301   "no",
302   "false",
303   "off",
304   NULL
305 };
306 
307 static Boolean  BoolValues [] = {
308   TRUE,
309   TRUE,
310   TRUE,
311   FALSE,
312   FALSE,
313   FALSE
314 };
315 
316 static CharPtr  RenderStrings [] = {
317   "none",
318   "line",
319   "cappedline",
320   "box",
321   "outlinebox",
322   NULL
323 };
324 
325 static RenderAlgorithm RenderValues [] = {
326   Do_Not_Render,
327   Render_Line,
328   Render_CappedLine,
329   Render_Box,
330   Render_OutlineBox
331 };
332 
333 static CharPtr  GapStrings [] = {
334   "none",
335   "line",
336   "angle",
337   NULL
338 };
339 
340 static GapEnum  GapValues [] = {
341   NoGap,
342   LineGap,
343   AngleGap
344 };
345 
346 static CharPtr GroupLabelLocations [] = {
347   "above",
348   "top",
349   "left",
350   "below",
351   "none",
352   NULL
353 };
354 
355 static GroupLabelLocation GroupLabelLocationValues [] = {
356   LabelOnTop,
357   LabelOnTop,
358   LabelOnSide,
359   LabelOnBottom,
360   NoLabel
361 };
362 
363 static CharPtr BioseqFormat [] = {
364   "accession",
365   "accn",
366   "fasta",
367   "long",
368   NULL
369 };
370 
371 static Uint1 BioseqFormatValues [] = {
372   PRINTID_TEXTID_ACC_VER,
373   PRINTID_TEXTID_ACC_VER,
374   PRINTID_FASTA_SHORT,
375   PRINTID_FASTA_LONG
376 };
377 
378 static CharPtr StrandStrings [] = {
379   "both",
380   "minus",
381   "plus",
382 /* these could be added if people would find them useful. . .
383    "-",
384    "+",
385 */
386   NULL
387 };
388 
389 static StrandChoice StrandValues [] = {
390   BothStrands,
391   MinusStrand,
392   PlusStrand
393 };
394 
395 static CharPtr ColorStrings [] = {
396 "black",    /*0,0,0*/
397 "blue",     /*0,0,255*/
398 "brown",    /*133,62,38*/
399 "coral",    /*255,127,80*/
400 "cyan",     /*0,255,255*/
401 "gray",     /*127,127,127*/
402 "green",    /*0,255,0*/
403 "grey",     /*127,127,127*/
404 "lavender", /*230,230,250*/
405 "magenta",  /*255,0,255*/
406 "maroon",   /*176,48,96*/
407 "orange",   /*255,165,0*/
408 "olive",    /*107,142,35*/
409 "pink",     /*255,192,203*/
410 "purple",   /*175,0,255*/
411 "red",      /*255,0,0*/
412 "white",    /*255,255,255*/
413 "yellow",   /*255,255,0*/
414 NULL
415 };
416 
417 static Uint1 ColorValues[][3] = {
418 /*black*/    {0,   0,   0},
419 /*blue*/     {0,   0,   255},
420 /*brown*/    {133, 62,  38},
421 /*coral*/    {255, 127, 80},
422 /*cyan*/     {0,   255, 255},
423 /*gray*/     {127, 127, 127},
424 /*green*/    {0,   255, 0},
425 /*grey*/     {127, 127, 127},
426 /*lavender*/ {230, 230, 250},
427 /*magenta*/  {255, 0,   255},
428 /*maroon*/   {176, 48,  96},
429 /*orange*/   {255, 165, 0},
430 /*olive*/    {107, 142, 35},
431 /*pink*/     {255, 192, 203},
432 /*purple*/   {175, 0,   255},
433 /*red*/      {255, 0,   0},
434 /*white*/    {255, 255, 255},
435 /*yellow*/   {255, 255, 0}
436 };
437 
438 static Char  config_filename [] = "asn2gph";
439 
440 /*** Static function declarations ****/
441 
442 static Int4 PixelsBetweenSeqCoords(Int4 left, Int4 right, Uint4 viewScale);
443 static Boolean IsInsertTic(RelevantFeatureItemPtr RFIP);
444 static void render_insert_tics(RenderInputPtr RIP);
445 
446 /* functions for working with alignments */
447 /* perhaps some of these should be moved to the alignment manager. */
448 static Boolean SeqAlignContentLabel(SeqAlignPtr sap, SeqIdPtr notThisSID, CharPtr buf, Int4 buflen, Uint1 format);
449 AccumValue_t NormaliseScore(Int4 rawscore, Int4 min, Int4 max);
450 static Int4 WeightFromAlignScore(SeqAlignPtr sap, Uint1 scoreType);
451 static void FindCutoffScore(SeqAlignPtr sap, Int4 alignCnt, AlignmentFilterStatePtr afsp);
452 static void GatherAlignInfo(
453   SeqAlignPtr sap, 
454   Int4        alignCnt,
455   SeqIdPtr    bioSeqId,
456   AlignmentFilterStatePtr afsp
457 );
458 static Boolean AlignmentFilterStateInit(SeqAlignPtr sap, SeqIdPtr sid, AlignmentFilterStatePtr afsp, GraphicViewExtrasPtr extras);
459 static Boolean AlignmentFilterStateDone(AlignmentFilterStatePtr afsp);
460 static void AlignmentFilterStateFree( AlignmentFilterStatePtr afsp);
461 
462 typedef struct align_seg_iterator {
463   SeqAlignPtr sap;
464   SeqIdPtr sip;
465   
466   Int4    nsegs;
467   Int4    start, stop;  /* left and rightmost bioseq coords for this alignment. */
468   Uint1   strand;   /* so we iterate the right direction */
469   Int4    alignRow; /* from sip on non-stdseg aligns */
470   Int4    currentSeg; /* for indexed aligns */
471   StdSegPtr currentStdSeg;  /* for std seg aligns */
472 } AlignSegIterator, PNTR AlignSegIteratorPtr;
473 
474 static Int4 AlignSegIteratorCreate(SeqAlignPtr sap, SeqIdPtr sip,  AlignSegIteratorPtr asi);
475 static Boolean AlignSegIteratorNext(AlignSegIteratorPtr asip, Int4Ptr start, Int4Ptr stop, Uint1Ptr strand, Uint1Ptr segType);
476 
477 
478 static Int1 StringIndexInStringList (CharPtr testString, CharPtr PNTR stringList) {
479 
480   Int1  i;
481 
482   if (testString == NULL || stringList == NULL) return -1;
483   /*
484   for (i = 0; stringList [i] != NULL; i++) {
485     if (StringICmp (testString, stringList [i]) == 0) return i;
486   }
487   */
488   i = 0;
489   while (stringList [i] != NULL) {
490     if (StringICmp (testString, stringList [i]) == 0) return i;
491     i++;
492   }
493   return -1;
494 }
495 
496 /* this parses fonts either in the format used by Vibrant's ParseFont, or allows "small" "meduim", etc. . . */
497 static FonT LocalParseFont (
498   CharPtr FontSpec
499 )
500 
501 {
502   if (FontSpec == NULL) return NULL;
503   if (StringICmp (FontSpec, "small") == 0) {
504     return SetSmallFont ();
505   } else if (StringICmp (FontSpec, "medium") == 0) {
506     return SetMediumFont ();
507   } else if (StringICmp (FontSpec, "large") == 0) {
508     return SetLargeFont ();
509   } else if (StringICmp (FontSpec, "system") == 0) {
510     return systemFont;
511   } else if (StringICmp (FontSpec, "program") == 0) {
512     return programFont;
513   } else {
514     return ParseFont (FontSpec);
515   }
516 }
517 
518 static CharPtr ColorModifiers [] = {
519 "light",
520 "lt",
521 "dark",
522 "drk",
523 "dk",
524 NULL
525 };
526 
527 /* COLOR must point to an array of Uint1 [3]; */
528 static Boolean ParseColor (
529   CharPtr string,
530   Uint1Ptr color
531 )
532 
533 {
534   unsigned  sscanfOffset, localColor [3] = { 0, 0, 0 }; /* "unsigned", to match %u and %n*/
535   Uint1     offset = 0;
536   Uint1     i;
537   Boolean   isLight = FALSE, isDark = FALSE;
538   Char      ch, modifierBuffer [8];
539   CharPtr   tmp;
540 
541 
542   if (string == NULL || color == NULL) return FALSE;
543 
544   /* first try to parse a human-readable color (ie, light blue)*/
545   StringNCpy_0 (modifierBuffer, string, sizeof (modifierBuffer));
546   /* truncate at space */
547   tmp = modifierBuffer;
548   ch = *tmp;
549   while (ch != '\0' && ch != ' ') {
550     tmp++;
551     ch = *tmp;
552   }
553   *tmp = '\0';
554 
555   i = StringIndexInStringList (modifierBuffer, ColorModifiers);
556   if (i > 0 && i < DIM (ColorModifiers)) {
557     switch (i) {
558       case 1 :
559       case 2 :
560         isLight = TRUE;
561         offset = StringLen (modifierBuffer);
562         break;
563       case 3 :
564       case 4 :
565       case 5 :
566         isDark = TRUE;
567         offset = StringLen (modifierBuffer);
568         break;
569       default :
570         break;
571     }
572   }
573   while (string[offset] != '\0' && (! IS_ALPHA (string[offset]))) {
574     offset ++;
575   }
576 
577   i = StringIndexInStringList (string + offset, ColorStrings);
578   if (i > 0 && i < DIM (ColorValues)) {
579     color [0] = ColorValues [i] [0];
580     color [1] = ColorValues [i] [1];
581     color [2] = ColorValues [i] [2];
582     if (isLight) {
583       color [0] = MIN (((3 * color [0]) / 2), 255);
584       color [1] = MIN (((3 * color [1]) / 2), 255);
585       color [2] = MIN (((3 * color [2]) / 2), 255);
586     } else if (isDark) {
587       color [0] = (2 * color [0]) / 3;
588       color [1] = (2 * color [1]) / 3;
589       color [2] = (2 * color [2]) / 3;
590     }
591   } else {
592     offset = 0;
593     for (i = 0; i < 3; i++) {
594       for (; string[offset] != '\0' && (! IS_DIGIT (string [offset])); offset++) continue;
595       if (string [offset] == '\0') return FALSE;
596       if (sscanf (string + offset, "%u%n", localColor + i, &sscanfOffset) == 0)  return FALSE;
597       offset += sscanfOffset;
598     }
599     color [0] = localColor [0];
600     color [1] = localColor [1];
601     color [2] = localColor [2];
602   }
603   return TRUE;
604 }
605 
606 
607 NLM_EXTERN FilterPtr FindFilterByName (
608   CharPtr name,
609   ViewerConfigsPtr VCP
610 )
611 
612 {
613   Int1  i;
614 
615   if (VCP == NULL || StringHasNoText (name) || (! VCP->ArraysPopulated)) return NULL;
616   i = StringIndexInStringList (name, VCP->FilterNameArray);
617   if (i < VCP->FilterCount && i >= 0) return (VCP->FilterArray[i]);
618   return NULL;
619 }
620 
621 NLM_EXTERN AppearancePtr FindAppearanceByName (
622   CharPtr name,
623   ViewerConfigsPtr VCP
624 )
625 
626 {
627   Int1  i;
628 
629   if (VCP == NULL || StringHasNoText (name) || (! VCP->ArraysPopulated)) return NULL;
630   i = StringIndexInStringList (name, VCP->AppearanceNameArray);
631   if (i < VCP->AppearanceCount && i>= 0) return (VCP->AppearanceArray[i]);
632   return NULL;
633 }
634 
635 NLM_EXTERN LayoutAlgorithm FindLayoutByName (
636   CharPtr name
637 )
638 
639 {
640   Int1  i;
641 
642   if (StringHasNoText (name)) return Layout_Inherit;
643   i = StringIndexInStringList (name, LayoutStrings);
644   if (i >= 0 && i < DIM (LayoutValues)) {
645     return LayoutValues [i];
646   }
647   return Layout_Inherit;
648 }
649 
650 static FilterPtr FindFilterByName_T (
651   CharPtr name,
652   ViewerConfigsPtr VCP
653 )
654 
655 {
656   ValNodePtr  nameVNP, filtVNP;
657 
658   if (VCP == NULL || StringHasNoText (name)) return NULL;
659   for (nameVNP = VCP->FilterNameList, filtVNP = VCP->FilterList;
660        nameVNP != NULL && filtVNP != NULL;
661        nameVNP = nameVNP->next, filtVNP = filtVNP->next) {
662     if (nameVNP->data.ptrvalue != NULL &&
663         StringICmp (name, nameVNP->data.ptrvalue) == 0) {
664       return ((FilterPtr) filtVNP->data.ptrvalue);
665     }
666   }
667   return NULL;
668 }
669 
670 static AppearancePtr FindAppearanceByName_T (
671   CharPtr name,
672   ViewerConfigsPtr VCP
673 )
674 
675 {
676   ValNodePtr  nameVNP, appVNP;
677 
678   if (VCP == NULL || StringHasNoText (name)) return NULL;
679   for (nameVNP = VCP->AppearanceNameList, appVNP = VCP->AppearanceList;
680        nameVNP != NULL && appVNP != NULL;
681        nameVNP = nameVNP->next, appVNP = appVNP->next) {
682     if (nameVNP->data.ptrvalue != NULL &&
683         StringICmp (name, nameVNP->data.ptrvalue) == 0) {
684       return ((AppearancePtr) appVNP->data.ptrvalue);
685     }
686   }
687   return NULL;
688 }
689 
690 static CharPtr FindFeatStrFromFeatDefType (Uint1 type)
691 {
692   CharPtr featName;
693 
694   featName = FindKeyFromFeatDefType (type, FALSE);
695   /* handle locally defined feature types. */
696   if (type > FEATDEF_MAX)
697   {
698     switch (type) {
699     case APPEARANCEITEM_Alignment:
700       featName = "align";
701       break;
702     case APPEARANCEITEM_Segment:
703       featName = "segment";
704       break;
705     case APPEARANCEITEM_Graph:
706       featName = "graph";
707       break;
708     }
709   }
710   return featName;
711 }
712 
713 
714 NLM_EXTERN AppearancePtr CreateAppearance (
715   CharPtr newname,
716   ViewerConfigsPtr VCP
717 )
718 
719 {
720   AppearancePtr   AP;
721 
722   if (VCP == NULL || StringHasNoText (newname)) return NULL;
723   if (FindAppearanceByName_T (newname, VCP) != NULL) return NULL;  /* don't allow duplicate names */
724   AP = (AppearancePtr) MemNew (sizeof (Appearance));
725   if (AP == NULL) return NULL;
726   AP->name = StringSave (newname);
727   VCP->AppearanceCount++;
728   ValNodeAddPointer (&VCP->AppearanceList, VCP->AppearanceCount, AP);
729   ValNodeAddPointer (&VCP->AppearanceNameList, VCP->AppearanceCount, AP->name);
730   return AP;
731 }
732 
733 static BioseqAppearanceItemPtr ParseBioseqAppearanceItem (
734   CharPtr sect,
735   ViewerConfigsPtr VCP
736 )
737 
738 {
739   Char                     buf [128];
740   BioseqAppearanceItemPtr  bioseqAIP;
741   Int2                     i;
742   unsigned                 val; /* "unsigned" to match sscanf("%ud")*/
743 
744   bioseqAIP = MemNew (sizeof (BioseqAppearanceItem));
745   if (bioseqAIP == NULL) return NULL;
746 
747   bioseqAIP->labelLoc = GroupLabelLocationValues [0];
748   if (GetAppParam (config_filename, sect, "label", NULL, buf, sizeof (buf))) {
749     i = StringIndexInStringList (buf, GroupLabelLocations);
750     if (i >= 0 && i < DIM (GroupLabelLocationValues)) {
751       bioseqAIP->labelLoc = GroupLabelLocationValues  [i];
752     }
753   }
754 
755   bioseqAIP->drawScale = TRUE;
756   if (GetAppParam (config_filename, sect, "scale", NULL, buf, sizeof (buf))) {
757     i = StringIndexInStringList (buf, BoolStrings);
758     if (i >= 0 && i < DIM (BoolValues)) {
759       bioseqAIP->drawScale = BoolValues [i];
760     }
761   }
762 
763   if (GetAppParam (config_filename, sect, "labelfont", NULL, buf, sizeof (buf))) {
764     bioseqAIP->labelFont = LocalParseFont (buf);
765   }
766   if (bioseqAIP->labelFont == NULL) {
767     bioseqAIP->labelFont = systemFont;
768   }
769 
770   if (GetAppParam (config_filename, sect, "scalefont", NULL, buf, sizeof (buf))) {
771     bioseqAIP->scaleFont = LocalParseFont (buf);
772   }
773   if (bioseqAIP->scaleFont == NULL) {
774     bioseqAIP->scaleFont = SetSmallFont ();
775   }
776 
777   if (GetAppParam (config_filename, sect, "height", NULL, buf, sizeof (buf))) {
778     if (buf != NULL) {
779       sscanf (buf, "%ud", &val);
780       bioseqAIP->height = MIN (val, 16);
781     }
782   }
783   if (bioseqAIP->height == 0) {
784     bioseqAIP->height = 10;
785   }
786 
787   if (GetAppParam (config_filename, sect, "scaleheight", NULL, buf, sizeof (buf))) {
788     if (buf != NULL) {
789       sscanf (buf, "%ud", &val);
790       bioseqAIP->scaleHeight = MIN (val, 16);
791     }
792   }
793   if (bioseqAIP->scaleHeight == 0) {
794     bioseqAIP->scaleHeight = 10;
795   }
796 
797   if (GetAppParam (config_filename, sect, "color", NULL, buf, sizeof (buf))) {
798     ParseColor (buf, bioseqAIP->bioseqColor);
799   }
800   if (GetAppParam (config_filename, sect, "labelcolor", NULL, buf, sizeof (buf))) {
801     ParseColor (buf, bioseqAIP->labelColor);
802   }
803   if (GetAppParam (config_filename, sect, "scalecolor", NULL, buf, sizeof (buf))) {
804     ParseColor (buf, bioseqAIP->scaleColor);
805   }
806 
807   bioseqAIP->format = BioseqFormatValues [0];
808   if (GetAppParam (config_filename, sect, "format", NULL, buf, sizeof (buf))) {
809     i = StringIndexInStringList (buf, BioseqFormat);
810     if (i >= 0 && i < DIM (BioseqFormatValues)) {
811       bioseqAIP->format = BioseqFormatValues  [i];
812     }
813   }
814 
815   return bioseqAIP;
816 }
817 
818 static AppearanceItemPtr ParseFeatureAppearanceItem (
819   CharPtr sect,
820   AppearanceItemPtr inheritFromMe,
821   Boolean recursing,
822   ViewerConfigsPtr VCP
823 )
824 
825 {
826   Char               buf [128];
827   AppearanceItemPtr  newAIP;
828   Int2               i;
829   Boolean            changed = FALSE;
830   unsigned           val; /* "unsigned" to match sscanf("%ud")*/
831   AppearanceItemPtr  namedAIP;
832 
833   if (! recursing) {
834     if (GetAppParam (config_filename, sect, "usenamedstyle", NULL, buf, sizeof (buf))) {
835       namedAIP = ParseFeatureAppearanceItem (buf, inheritFromMe, TRUE, VCP);
836       if (namedAIP != NULL) {
837         inheritFromMe = namedAIP;
838         changed = TRUE; /* !!! this will use more memory than necessary */
839       }
840     }
841   }
842   newAIP = MemNew (sizeof (AppearanceItem));
843   if (newAIP == NULL) return NULL;
844   MemCopy (newAIP, inheritFromMe, sizeof (AppearanceItem));
845   if (GetAppParam (config_filename, sect, "color", NULL, buf, sizeof (buf))) {
846     changed = ParseColor (buf, newAIP->Color);
847   }
848   if (GetAppParam (config_filename, sect, "labelcolor", NULL, buf, sizeof (buf))) {
849     changed = ParseColor (buf, newAIP->LabelColor);
850   }
851 
852   newAIP->LabelLoc = LabelAbove;
853   if (GetAppParam (config_filename, sect, "label", NULL, buf, sizeof (buf))) {
854     i = StringIndexInStringList (buf, LlocStrings);
855     if (i >= 0 && i < DIM (LlocValues)) {
856       newAIP->LabelLoc = LlocValues [i];
857     }
858   }
859   if (GetAppParam (config_filename, sect, "displaywith", NULL, buf, sizeof (buf))) {
860     i = StringIndexInStringList (buf, RenderStrings);
861     if (i >= 0 && i < DIM (RenderValues)) {
862       newAIP->RenderChoice = RenderValues [i];
863       changed = TRUE;
864     }
865   }
866   if (GetAppParam (config_filename, sect, "showarrow", NULL, buf, sizeof (buf))) {
867     i = StringIndexInStringList (buf, BoolStrings);
868     if (i >= 0 && i < DIM (BoolValues)) {
869       newAIP->ShowArrow = BoolValues [i];
870       changed = TRUE;
871     }
872   }
873   if (GetAppParam (config_filename, sect, "gap", NULL, buf, sizeof (buf))) {
874     i = StringIndexInStringList (buf, GapStrings);
875     if (i >= 0 && i < DIM (RenderValues)) {
876       newAIP->GapChoice = GapValues [i];
877       changed = TRUE;
878     }
879   }
880   if (GetAppParam (config_filename, sect, "showtype", NULL, buf, sizeof (buf))) {
881     i = StringIndexInStringList (buf, BoolStrings);
882     if (i >= 0 && i < DIM (BoolValues)) {
883       newAIP->AddTypeToLabel = BoolValues [i];
884     }
885   }
886   if (GetAppParam (config_filename, sect, "showcontent", NULL, buf, sizeof (buf))) {
887     i = StringIndexInStringList (buf, BoolStrings);
888     if (i >= 0 && i < DIM (BoolValues)) {
889       newAIP->AddDescToLabel = BoolValues [i];
890     }
891   }
892   if (GetAppParam (config_filename, sect, "height", NULL, buf, sizeof (buf))) {
893     if (buf != NULL) {
894       sscanf (buf, "%ud", &val);
895       newAIP->Height = MIN (val, 16);
896       changed = TRUE;
897     }
898   }
899   if (GetAppParam (config_filename, sect, "labelfont", NULL, buf, sizeof (buf))) {
900     newAIP->LabelFont = LocalParseFont (buf);
901   }
902   if (newAIP->LabelFont == NULL) {
903     newAIP->LabelFont = programFont;
904   }
905   if (newAIP->LabelFont != inheritFromMe->LabelFont) {
906     changed = TRUE;
907   }
908   
909   /* this is only meaningful for alignment styles to change the appearance of the alignment's label. */
910   newAIP->format = BioseqFormatValues [0];
911   if (GetAppParam (config_filename, sect, "format", NULL, buf, sizeof (buf))) {
912     i = StringIndexInStringList (buf, BioseqFormat);
913     if (i >= 0 && i < DIM (BioseqFormatValues)) {
914       newAIP->format = BioseqFormatValues  [i];
915     }
916   }
917 
918 
919   if (! changed) {
920     MemFree (newAIP);
921     return NULL;
922   }
923   return newAIP;
924 }
925 
926 static AppearancePtr ParseAppearance (
927   CharPtr appearanceNameInFile,
928   ViewerConfigsPtr VCP
929 )
930 
931 {
932   AppearancePtr      AP;
933   AppearanceItemPtr  AIP, impAIP, newAIP;
934   Char               buf [128];
935   Char               sect [128];
936   Char               outputBuffer [128];
937   AppearanceItem     DefaultAppearanceItem = {
938     {0, 0, 0}, {64, 64, 64}, Render_Box, 0, 5, 0, FALSE, LineGap, TRUE, TRUE, NULL, LabelAbove
939   };
940   Int1               i;
941   unsigned           val;
942 
943   if (appearanceNameInFile == NULL) return NULL;
944   DefaultAppearanceItem.LabelFont = programFont;
945   sprintf (sect, "%s.master", appearanceNameInFile);
946   /* require all styles to have a name, since high-level interface uses the name to identify Filters */
947   if (! GetAppParam (config_filename, sect, "name", NULL, buf, sizeof (buf))) return NULL;
948   if (StringHasNoText (buf)) return NULL;
949   AP = CreateAppearance (buf, VCP);
950   if (AP == NULL) return NULL;
951   AIP = ParseFeatureAppearanceItem (sect, &DefaultAppearanceItem, FALSE, VCP); /*parse xyz.master */
952   if (AIP == NULL) {            /* require a "master" style */
953     DestroyAppearance (AP, VCP);
954     return NULL;
955   }
956   val = VCP->DefaultMaxScaleForArrow;
957   if (GetAppParam (config_filename, sect, "maxarrowscale", NULL, buf, sizeof (buf))) {
958     sscanf (buf, "%ud", &val);
959   }
960   AP->MaxScaleForArrow = val;
961   val = VCP->DefaultMinPixelsForArrow;
962   if (GetAppParam (config_filename, sect, "minarrowpixels", NULL, buf, sizeof (buf))) {
963     sscanf (buf, "%ud", &val);
964   }
965   AP->MinPixelsForArrow = val;
966   AP->ShadeSmears = VCP->DefaultShadeSmears;
967   if (GetAppParam (config_filename, sect, "shadesmears", NULL, buf, sizeof (buf))) {
968     i = StringIndexInStringList (buf, BoolStrings);
969     if (i >= 0 && i < DIM (BoolValues)) {
970       AP->ShadeSmears = BoolValues [i];
971     }
972   }
973 
974   /* group box settings */
975   if (GetAppParam (config_filename, sect, "groupboxcolor", NULL, buf, sizeof (buf))) {
976     ParseColor (buf, AP->GroupBoxColor);
977   }
978 
979   if (GetAppParam (config_filename, sect, "grouplabelfont", NULL, buf, sizeof (buf))) {
980     AP->GroupLabelFont = LocalParseFont (buf);
981     if (AP->GroupLabelFont == NULL) {
982       AP->GroupLabelFont = programFont;
983     }
984   }
985   if (GetAppParam (config_filename, sect, "grouplabelcolor", NULL, buf, sizeof (buf))) {
986     ParseColor (buf, AP->GroupLabelColor);
987   }
988 
989   /* Named Annotation Style settings */
990   if (GetAppParam (config_filename, sect, "annotboxcolor", NULL, buf, sizeof (buf))) {
991     ParseColor (buf, AP->AnnotBoxColor);
992   }
993 
994   if (GetAppParam (config_filename, sect, "annotlabelfont", NULL, buf, sizeof (buf))) {
995     AP->AnnotLabelFont = LocalParseFont (buf);
996     if (AP->AnnotLabelFont == NULL) {
997       AP->AnnotLabelFont = programFont;
998     }
999   }
1000   if (GetAppParam (config_filename, sect, "annotlabelcolor", NULL, buf, sizeof (buf))) {
1001     ParseColor (buf, AP->AnnotLabelColor);
1002   }
1003 
1004 
1005   sprintf (outputBuffer, "%s.bioseq", appearanceNameInFile);
1006   AP->bioseqAIP = ParseBioseqAppearanceItem (outputBuffer, VCP);
1007   sprintf (outputBuffer, "%s.imp", appearanceNameInFile);
1008   impAIP = ParseFeatureAppearanceItem (outputBuffer, AIP, FALSE, VCP);
1009   if (impAIP == NULL) {
1010     sprintf (outputBuffer, "%s.import", appearanceNameInFile);
1011     impAIP = ParseFeatureAppearanceItem (outputBuffer, AIP, FALSE, VCP);
1012   }
1013   if (impAIP == NULL) {
1014     impAIP = AIP;
1015   } else {
1016     AddAppearanceItemToAppearance (impAIP, AP, FEATDEF_IMP, VCP);
1017   }
1018   for (i = 1; i < APPEARANCEITEM_MAX; i++) {
1019     if (i == FEATDEF_IMP) continue;
1020     sprintf (outputBuffer, "%s.%s", appearanceNameInFile, FindFeatStrFromFeatDefType (i));
1021     if (i >= FEATDEF_allele && i <= FEATDEF_35_signal) {        /* is it an imp-feat ? */
1022       newAIP = ParseFeatureAppearanceItem (outputBuffer, impAIP, FALSE, VCP);
1023       newAIP = newAIP ? newAIP : impAIP;
1024     } else {
1025       newAIP = ParseFeatureAppearanceItem (outputBuffer, AIP, FALSE, VCP);
1026       newAIP = newAIP ? newAIP : AIP;
1027     }
1028     if (newAIP != NULL) {
1029       AddAppearanceItemToAppearance (newAIP, AP, i, VCP);
1030     }
1031   }
1032   AddAppearanceItemToAppearance (AIP, AP, FEATDEF_BAD, VCP);
1033   return AP;
1034 }
1035 
1036 NLM_EXTERN FilterPtr CreateFilter (
1037   CharPtr name,
1038   ViewerConfigsPtr VCP
1039 )
1040 
1041 {
1042   FilterPtr  FP;
1043 
1044   if (VCP == NULL || StringHasNoText (name)) return NULL;
1045   if (FindFilterByName_T (name, VCP) != NULL) return NULL;  /* don't allow duplicate names */
1046   FP = MemNew (sizeof (Filter));
1047   if (FP == NULL) return FP;
1048   FP->name = StringSave (name);
1049   VCP->FilterCount++;
1050   ValNodeAddPointer (&VCP->FilterList, VCP->FilterCount, FP);
1051   ValNodeAddPointer (&VCP->FilterNameList, VCP->FilterCount, FP->name);
1052   return FP;
1053 }
1054 
1055 static void ChangeFeatureInFilterItem (
1056   FilterItemPtr FIP,
1057   Uint1 newFeatdef,
1058   Boolean includeMe,
1059   ViewerConfigsPtr VCP
1060 )
1061 
1062 {
1063   Uint1  i;
1064   Uint1  order = 0;
1065   Uint1  orderIncrement = 0;
1066 
1067   if (FIP == NULL) return;
1068 
1069   if (includeMe) {
1070     orderIncrement = 1;
1071     for (i = 0; i < APPEARANCEITEM_MAX; i++) {
1072       order = MAX (order, FIP->IncludeFeature [i]);
1073     }
1074     order++; /* the lowest available order index */
1075   }
1076 
1077   if (newFeatdef == FEATDEF_ANY) {
1078     for (i = 1; i < APPEARANCEITEM_MAX; i++) {  /* start at 1 since we do not want to ever include FEATDEF_BAD */
1079       FIP->IncludeFeature [i] = order;
1080       order += orderIncrement;
1081     }
1082   } else if (newFeatdef == FEATDEF_ANY_RNA) {
1083     for (i = FEATDEF_preRNA; i <= FEATDEF_otherRNA; i++) {
1084       FIP->IncludeFeature [i] = order;
1085       order += orderIncrement;
1086     }
1087     FIP->IncludeFeature [FEATDEF_misc_RNA] = order;
1088     order += orderIncrement;
1089     FIP->IncludeFeature [FEATDEF_precursor_RNA] = order;
1090     order += orderIncrement;
1091     FIP->IncludeFeature [FEATDEF_snoRNA] = order;
1092   } else if (newFeatdef == FEATDEF_ANY_PROT) {
1093     FIP->IncludeFeature [FEATDEF_PROT] = order;
1094     order += orderIncrement;
1095     for (i = FEATDEF_preprotein; i <= FEATDEF_transit_peptide_aa; i++) {
1096       FIP->IncludeFeature [i] = order;
1097       order += orderIncrement;
1098     }
1099   } else if (newFeatdef == FEATDEF_IMP) {
1100     for (i = FEATDEF_allele; i <= FEATDEF_35_signal; i++) {
1101       FIP->IncludeFeature [i] = order;
1102       order += orderIncrement;
1103     }
1104   } else if (newFeatdef < APPEARANCEITEM_MAX) {
1105     FIP->IncludeFeature [newFeatdef] = order;
1106   }
1107 }
1108 
1109 NLM_EXTERN void AddFeatureToFilterItem (
1110   FilterItemPtr FIP,
1111   Uint1 newFeatdef,
1112   ViewerConfigsPtr VCP
1113 )
1114 
1115 {
1116   ChangeFeatureInFilterItem (FIP, newFeatdef, TRUE, VCP);
1117 }
1118 
1119 NLM_EXTERN void RemoveFeatureFromFilterItem (
1120   FilterItemPtr FIP,
1121   Uint1 newFeatdef,
1122   ViewerConfigsPtr VCP
1123 )
1124 
1125 {
1126   ChangeFeatureInFilterItem (FIP, newFeatdef, FALSE, VCP);
1127 }
1128 
1129 static void AddFilterItemToFilter (
1130  FilterItemPtr newFIP,
1131   FilterPtr parent,
1132   ViewerConfigsPtr VCP
1133 )
1134 
1135 {
1136   ValNodePtr  newVNP, lastVNP;
1137 
1138   for (lastVNP = parent->FilterItemList;
1139        lastVNP != NULL && lastVNP->next != NULL;
1140        lastVNP = lastVNP->next) continue;
1141   newVNP = ValNodeAdd (&parent->FilterItemList);
1142   newVNP->data.ptrvalue = newFIP;
1143 }
1144 
1145 NLM_EXTERN FilterItemPtr CreateNewFilterItemInFilter (
1146   CharPtr name,
1147   FilterPtr parent,
1148   ViewerConfigsPtr VCP
1149 )
1150 
1151 {
1152   FilterItemPtr  FIP;
1153 
1154   FIP = MemNew (sizeof (FilterItem));
1155   if (FIP == NULL) return FIP;
1156   FIP->GroupLabel = StringSaveNoNull (name);
1157   AddFilterItemToFilter (FIP, parent, VCP);
1158   return FIP;
1159 }
1160 
1161 static CharPtr SpecialFeatures [] = {
1162 "everything",
1163 "all",
1164 "every",
1165 "any",
1166 "imp",
1167 "rna",
1168 "prot",
1169 "bioseq",
1170 "graph",
1171 "align",
1172 NULL
1173 };
1174 
1175 static FilterItemPtr ParseFilterItem (
1176   CharPtr filterItemName,
1177   Uint2 defaultRowPadding,
1178   Uint2 defaultGroupPadding,
1179   LayoutAlgorithm defaultLayout,
1180   ViewerConfigsPtr VCP
1181 )
1182 
1183 {
1184   Char           sect [128];
1185   Char           featureNum [128];
1186   Char           buf [128];
1187   Uint4          featdeftype;
1188   Uint4          featureCount = 0;
1189   FilterItemPtr  FIP;
1190   Int2           i, j;
1191   unsigned       val;
1192 
1193   FIP = MemNew (sizeof (FilterItem));
1194   if (FIP == NULL) return FIP;
1195   FIP->DrawScale = TristateUnset;
1196   FIP->Type = FeatureFilter; /* this will get changed if a graph or alignment is discovered instead */
1197   sprintf (sect, "filters.%s", filterItemName);
1198   GetAppParam (config_filename, sect, "layout", "inherit", buf, sizeof (buf));
1199   i = StringIndexInStringList (buf, LayoutStrings);
1200   if (i >= 0 && i < DIM (LayoutStrings)) {
1201     FIP->LayoutChoice = LayoutValues [i];
1202   } else {
1203     FIP->LayoutChoice = defaultLayout;
1204   }
1205 
1206   FIP->GroupPadding = defaultGroupPadding;
1207   if (GetAppParam (config_filename, sect, "grouppadding", NULL, buf, sizeof (buf))) {
1208     sscanf (buf, "%ud", &val);
1209     val = MIN (val, 100);
1210     FIP->GroupPadding = val;
1211   }
1212 
1213   FIP->IntraRowPaddingPixels = defaultRowPadding;
1214   if (GetAppParam (config_filename, sect, "rowpadding", NULL, buf, sizeof (buf))) {
1215     sscanf (buf, "%ud", &val);
1216     val = MIN (val, 100);
1217     FIP->IntraRowPaddingPixels = val;
1218   }
1219   FIP->DrawGroupBox = FALSE;
1220   FIP->FillGroupBox = FALSE;
1221   if (GetAppParam (config_filename, sect, "groupbox", NULL, buf, sizeof (buf))) {
1222     i = StringIndexInStringList (buf, BoolStrings);
1223     if (i >= 0 && i < DIM (BoolValues) && i >= 0) {
1224       FIP->DrawGroupBox = BoolValues [i];
1225     }
1226   }
1227   if (FIP->DrawGroupBox) {
1228     if (GetAppParam (config_filename, sect, "groupboxcolor", NULL, buf, sizeof (buf))) {
1229       FIP->GroupBoxColorSet = TRUE;
1230       ParseColor (buf, FIP->GroupBoxColor);
1231     }
1232     if (GetAppParam (config_filename, sect, "fillbox", NULL, buf, sizeof (buf))) {
1233       i = StringIndexInStringList (buf, BoolStrings);
1234       if (i >= 0 && i < DIM (BoolValues)) {
1235         FIP->FillGroupBox = BoolValues[i];
1236       }
1237     }
1238   }
1239 
1240   FIP->GroupLabel = NoLabel;
1241   FIP->GroupLabelFont = programFont;
1242   if (GetAppParam (config_filename, sect, "name", NULL, buf, sizeof (buf))) {
1243     FIP->GroupLabel = StringSaveNoNull (buf);
1244     FIP->GroupLabelLoc = LabelOnTop;
1245     if (GetAppParam (config_filename, sect, "grouplabel", NULL, buf, sizeof (buf))) {
1246       i = StringIndexInStringList (buf, GroupLabelLocations);
1247       if (i >= 0 && i < DIM (GroupLabelLocationValues)) {
1248         FIP->GroupLabelLoc = GroupLabelLocationValues[i];
1249       }
1250     }
1251     if (GetAppParam (config_filename, sect, "grouplabelfont", NULL, buf, sizeof (buf))) {
1252       FIP->GroupLabelFontSet = TRUE;
1253       FIP->GroupLabelFont = LocalParseFont (buf);
1254       if (FIP->GroupLabelFont == NULL) {
1255         FIP->GroupLabelFont = programFont;
1256       }
1257     }
1258     if (GetAppParam (config_filename, sect, "grouplabelcolor", NULL, buf, sizeof (buf))) {
1259       FIP->GroupLabelColorSet = TRUE;
1260       ParseColor (buf, FIP->GroupLabelColor);
1261     }
1262   }
1263 
1264   FIP->LabelLoc = LabelAbove;
1265   if (GetAppParam (config_filename, sect, "label", NULL, buf, sizeof (buf))) {
1266     i = StringIndexInStringList (buf, LlocStrings);
1267     if (i >= 0 && i < DIM (LlocValues)) {
1268       FIP->LabelLoc = LlocValues [i];
1269     }
1270   }
1271 
1272   FIP->AddTypeToLabel = TristateUnset;
1273   if (FIP->LabelLoc != LabelNone && GetAppParam (config_filename, sect, "showtype", NULL, buf, sizeof (buf))) {
1274     i = StringIndexInStringList (buf, BoolStrings);
1275     if (i >= 0 && i < DIM (BoolValues)) {
1276       FIP->AddTypeToLabel = BOOL_TO_TRISTATE (BoolValues [i]);
1277     }
1278   }
1279   FIP->AddDescToLabel = TristateUnset;
1280   if (FIP->LabelLoc != LabelNone && GetAppParam (config_filename, sect, "showcontent", NULL, buf, sizeof (buf))) {
1281     i = StringIndexInStringList (buf, BoolStrings);
1282     if (i >= 0 && i < DIM (BoolValues)) {
1283       FIP->AddDescToLabel = BOOL_TO_TRISTATE (BoolValues [i]);
1284     }
1285   }
1286 
1287   FIP->MatchStrand = StrandValues [0];
1288   if (GetAppParam (config_filename, sect, "strand", NULL, buf, sizeof (buf))) {
1289     i = StringIndexInStringList (buf, StrandStrings);
1290     if (i >= 0 && i < DIM (StrandValues)) {
1291       FIP->MatchStrand = StrandValues [i];
1292     }
1293   }
1294   for (i = 1; i < APPEARANCEITEM_MAX; i++) {
1295     sprintf (featureNum, "feature%d", (unsigned) i);
1296     if (GetAppParam (config_filename, sect, featureNum, NULL, buf, sizeof (buf))) {
1297       featdeftype = FindFeatDefTypeFromKey (buf);
1298       if (featdeftype == FEATDEF_BAD) {
1299         /* special-case checks for types of features not found by FindFeatDefTypeFromKey () */
1300         j = StringIndexInStringList (buf, SpecialFeatures);
1301         /*
1302         if (j >= 0 && j < DIM (SpecialFeatures)) {
1303           switch (j) {
1304             case 1 :
1305             case 2 :
1306             case 3 :
1307             case 4 :
1308               featdeftype = FEATDEF_ANY;
1309               break;
1310             case 5 :
1311               featdeftype = FEATDEF_IMP;
1312               break;
1313             case 6 :
1314               featdeftype = FEATDEF_ANY_RNA;
1315               break;
1316             case 7 :
1317               featdeftype = FEATDEF_ANY_PROT;
1318               break;
1319             case 8 :
1320               FIP->Type = BioseqFilter;
1321               break;
1322             case 9 :
1323               FIP->Type = GraphFilter;
1324               break;
1325             case 10 :
1326               FIP->Type = AlignmentFilter;
1327               break;
1328             default :
1329               break;
1330           }
1331         } else continue; */
1332         /* insert special-case checks for types of features not found by FindFeatDefTypeFromKey () here */
1333         if (StringICmp (buf, "everything") == 0 ||
1334             StringICmp (buf, "all") == 0 ||
1335             StringICmp (buf, "every") == 0 ||
1336             StringICmp (buf, "any") == 0) {
1337           featdeftype = FEATDEF_ANY;
1338         } else if (StringICmp (buf, "imp") == 0) {
1339           featdeftype = FEATDEF_IMP; /* FindFeatDefTypeFromKey matches 'import', but not 'imp' */
1340         } else if (StringICmp (buf, "rna") == 0) {
1341           featdeftype = FEATDEF_ANY_RNA;
1342         } else if (StringICmp (buf, "prot") == 0) {
1343           featdeftype = FEATDEF_ANY_PROT;
1344         } else if (StringICmp (buf, "bioseq") == 0) {
1345           FIP->Type = BioseqFilter;
1346         } else if (StringICmp (buf, "graph") == 0) {
1347           featdeftype = APPEARANCEITEM_Graph;
1348           FIP->Type = GraphFilter;
1349         } else if (StringICmp (buf, "align") == 0) {
1350           featdeftype = APPEARANCEITEM_Alignment;
1351           FIP->Type = AlignmentFilter;
1352         } else continue; /* failed to find a match */
1353       }
1354       AddFeatureToFilterItem (FIP, featdeftype, VCP);
1355       featureCount++;
1356     }
1357   }
1358   if (FIP->Type == BioseqFilter) {
1359     FIP->DrawScale = TristateUnset;
1360     if (GetAppParam (config_filename, sect, "scale", NULL, buf, sizeof (buf))) {
1361       i = StringIndexInStringList (buf, BoolStrings);
1362       if (i >= 0 && i < DIM (BoolValues)) {
1363         FIP->DrawScale = BOOL_TO_TRISTATE (BoolValues [i]);
1364       }
1365     }
1366   }
1367   if (featureCount == 0) {
1368     MemFree (FIP);
1369     return NULL;
1370   }
1371   return FIP;
1372 }
1373 
1374 static FilterPtr ParseFilter (
1375   CharPtr filterNameInFile,
1376   ViewerConfigsPtr VCP
1377 )
1378 
1379 {
1380 
1381   FilterPtr       FP;
1382   FilterItemPtr   FIP;
1383   Int2            i;
1384   Uint1           filterItemCount = 0;
1385   Char            buf [128];     /* for input *from* GetAppParam */
1386   Char            outputBuffer [128];    /* paramater *to* GetAppParam */
1387   Char            sect [128];
1388   Boolean         foundBioseqFilter = FALSE;
1389   Boolean         foundGraphFilter = FALSE;
1390   Boolean         foundAlignmentFilter = FALSE;
1391   ValNodePtr      VNP;
1392   Boolean         createImplicitBioseq = TRUE;
1393   Boolean         createImplicitGraphs = TRUE;
1394   unsigned        val; /* to match sscanf ("%d"...)*/
1395   Uint2           defaultRowPadding;
1396   Uint2           defaultGroupPadding;
1397   LayoutAlgorithm defaultLayout;
1398 
1399   if (filterNameInFile == NULL) return NULL;
1400   sprintf (sect, "%s", filterNameInFile);
1401   /* require all styles to have a name, since high-level interface uses the name to identify Filters */
1402   if (! GetAppParam (config_filename, sect, "name", NULL, buf, sizeof (buf))) return NULL;
1403   FP = CreateFilter (buf, VCP); /* Createfilter will check for duplucate names */
1404   if (FP == NULL) return FP;
1405   val = VCP->DefaultMaxScaleWithLabels;
1406   if (GetAppParam (config_filename, sect, "maxlabelscale", NULL, buf, sizeof (buf))) {
1407     sscanf (buf, "%ud", &val);
1408   }
1409   FP->MaxScaleWithLabels = val;
1410   
1411   /* Group by Named Annotation stuff */
1412   FP->AnnotBoxColorSet = FALSE;
1413   FP->AnnotLabelFontSet = FALSE;
1414   FP->AnnotLabelColorSet = FALSE;
1415   
1416   FP->GroupByAnnot = TRUE;
1417   if (GetAppParam (config_filename, sect, "annotgroup", NULL, buf, sizeof (buf))) {
1418     i = StringIndexInStringList (buf, BoolStrings);
1419     if (i >= 0 && i < DIM (BoolStrings)) {
1420       FP->GroupByAnnot =  (BoolValues [i]);
1421     }
1422   }
1423   if (FP->GroupByAnnot) {
1424     FP->DrawAnnotBox = TRUE;
1425     if (GetAppParam (config_filename, sect, "annotbox", NULL, buf, sizeof (buf))) {
1426       i = StringIndexInStringList (buf, BoolStrings);
1427       if (i >= 0 && i < DIM (BoolStrings)) {
1428         FP->DrawAnnotBox =  (BoolValues [i]);
1429       }
1430     }
1431     if (FP->DrawAnnotBox) {
1432       if (GetAppParam (config_filename, sect, "annotboxcolor", NULL, buf, sizeof (buf))) {
1433         FP->AnnotBoxColorSet = TRUE;
1434         ParseColor (buf, FP->AnnotBoxColor);
1435       }
1436     }
1437     FP->AnnotLabelLoc = LabelOnTop;
1438     if (GetAppParam (config_filename, sect, "annotlabel", NULL, buf, sizeof (buf))) {
1439       i = StringIndexInStringList (buf, GroupLabelLocations);
1440       if (i >= 0 && i < DIM (GroupLabelLocationValues)) {
1441         FP->AnnotLabelLoc = GroupLabelLocationValues[i];
1442       }
1443     }
1444     if (FP->AnnotLabelLoc != NoLabel) {
1445       FP->AnnotLabelFont = programFont;
1446       if (GetAppParam (config_filename, sect, "annotlabelfont", NULL, buf, sizeof (buf))) {
1447         FP->AnnotLabelFont = LocalParseFont (buf);
1448         if (FP->AnnotLabelFont == NULL) {
1449           FP->AnnotLabelFont = programFont;
1450         } else {
1451           FP->AnnotLabelFontSet = TRUE;
1452         }
1453       }
1454       if (GetAppParam (config_filename, sect, "annotlabelcolor", NULL, buf, sizeof (buf))) {
1455         FP->AnnotLabelColorSet = TRUE;
1456         ParseColor (buf, FP->AnnotLabelColor);
1457       }
1458     }
1459   }
1460   
1461   /* other default values */
1462   GetAppParam (config_filename, sect, "layout", NULL, buf, sizeof (buf));
1463   i = StringIndexInStringList (buf, LayoutStrings);
1464   if (i >= 0 && i < DIM (LayoutValues)) {
1465     defaultLayout = LayoutValues [i];
1466   } else {
1467     defaultLayout = Layout_Inherit;
1468   }
1469 
1470   val = VCP->DefaultGroupPadding;
1471   if (GetAppParam (config_filename, sect, "grouppadding", NULL, buf, sizeof (buf))) {
1472     sscanf (buf, "%ud", &val);
1473     val = MIN (val, 100);
1474   }
1475   defaultGroupPadding = val;
1476 
1477   val = VCP->DefaultRowPadding;
1478   if (GetAppParam (config_filename, sect, "rowpadding", NULL, buf, sizeof (buf))) {
1479     sscanf (buf, "%ud", &val);
1480   }
1481   defaultRowPadding = val;
1482 
1483   if (GetAppParam (config_filename, sect, "suppressbioseq", NULL, buf, sizeof (buf))) {
1484     i = StringIndexInStringList (buf, BoolStrings);
1485     if (i >= 0 && i < DIM (BoolStrings)) {
1486       createImplicitBioseq = ! (BoolValues [i]);
1487     }
1488   }
1489   if (GetAppParam (config_filename, sect, "suppressgraphs", NULL, buf, sizeof (buf))) {
1490     i = StringIndexInStringList (buf, BoolStrings);
1491     if (i >= 0 && i < DIM (BoolStrings)) {
1492       createImplicitGraphs = ! (BoolValues [i]);
1493     }
1494   }
1495 
1496   for (i = 1; i < APPEARANCEITEM_MAX; i++) {
1497     sprintf (outputBuffer, "%s%d", "group", (unsigned) i);
1498     if (GetAppParam (config_filename, sect, outputBuffer, NULL, buf, sizeof (buf))) {
1499       FIP = ParseFilterItem (buf, defaultRowPadding, defaultGroupPadding, defaultLayout, VCP);
1500       if (FIP == NULL) continue;
1501       if (FIP->Type == BioseqFilter) {
1502         foundBioseqFilter = TRUE;
1503       }
1504       if (FIP->Type == GraphFilter) {
1505         foundGraphFilter = TRUE;
1506       }
1507       if (FIP->Type == AlignmentFilter) {
1508         foundAlignmentFilter = TRUE;
1509       }
1510       AddFilterItemToFilter (FIP, FP, VCP);
1511       filterItemCount++;
1512     }
1513   }
1514   if (filterItemCount == 0) {
1515     DestroyFilter (FP, VCP);
1516     return NULL;
1517   }
1518 
1519   if (createImplicitBioseq && ! foundBioseqFilter) {
1520     VNP = MemNew (sizeof (ValNode));
1521     FIP = MemNew (sizeof (FilterItem));
1522     if (VNP == NULL || FIP == NULL) {
1523       DestroyFilter (FP, VCP);
1524       return NULL;
1525     }
1526     /* insert a Bioseq filter at the head of the list */
1527     VNP->next = FP->FilterItemList;
1528     VNP->data.ptrvalue = FIP;
1529     FP->FilterItemList = VNP;
1530 
1531     FIP->Type = BioseqFilter;
1532     FIP->IntraRowPaddingPixels = defaultRowPadding;
1533   }
1534 
1535   if (createImplicitGraphs && ! foundAlignmentFilter) {
1536     /* insert an alignment filter at the_end of the list */
1537     FIP = MemNew (sizeof (FilterItem));
1538     VNP = ValNodeAddPointer (&FP->FilterItemList, 0, FIP);
1539     if (FIP == NULL || VNP == NULL ) {
1540       DestroyFilter (FP, VCP);
1541       return NULL;
1542     }
1543     FIP->Type = AlignmentFilter;
1544     FIP->LayoutChoice = defaultLayout;
1545     FIP->GroupPadding = defaultGroupPadding;
1546     FIP->IntraRowPaddingPixels = defaultRowPadding;
1547     FIP->LabelLoc = LabelAbove;
1548     FIP->MatchStrand = StrandValues [0];
1549   }
1550 
1551   if (createImplicitGraphs && ! foundGraphFilter) {
1552     /* insert a Graph filter at the_end of the list */
1553     FIP = MemNew (sizeof (FilterItem));
1554     VNP = ValNodeAddPointer (&FP->FilterItemList, 0, FIP);
1555     if (FIP == NULL || VNP == NULL ) {
1556       DestroyFilter (FP, VCP);
1557       return NULL;
1558     }
1559     FIP->Type = GraphFilter;
1560     FIP->IntraRowPaddingPixels = 5;
1561   }
1562 
1563   return FP;
1564 }
1565 
1566 /* if this will be used by multiple threads in a multi-threaded application, there should be a lock around writing this */
1567 static ViewerConfigsPtr newGraphicViewer_ConfigFileParse_Global = NULL;
1568 
1569 static void InitializeDefaultStyle (
1570   CharPtr configFileName
1571 );
1572 
1573 NLM_EXTERN ViewerConfigsPtr GetGraphicConfigParseResults (
1574   void
1575 )
1576 
1577 {
1578   Uint1             AppearanceCount;
1579   Uint1             FilterCount;
1580   Uint1             i;
1581   void PNTR PNTR    ptr2;
1582   ViewerConfigsPtr  VCP;
1583   ValNodePtr        nameVNP;
1584   ValNodePtr        VNP;
1585 
1586   if (newGraphicViewer_ConfigFileParse_Global != NULL) {
1587     return newGraphicViewer_ConfigFileParse_Global;
1588   }
1589 
1590   InitializeDefaultStyle (config_filename);
1591 
1592   VCP = MemNew (sizeof (ViewerConfigs));
1593   if (VCP == NULL) return NULL;
1594 
1595   if (ParseConfigFile (VCP) == 0) return NULL;
1596   /* this should never happen, because of the static default style*/
1597   if (VCP->AppearanceCount == 0 || VCP->FilterCount == 0) return NULL;
1598 
1599   AppearanceCount = VCP->AppearanceCount;
1600   FilterCount = VCP->FilterCount;
1601   i = (AppearanceCount + FilterCount) * 2 + 2; /* total number of pointers needed (the extra 2 are NULL's to terminate the name lists) */
1602   ptr2 = MemNew (i * sizeof (VoidPtr));
1603   if (ptr2 == NULL) {
1604     MemFree (VCP);
1605     return NULL;
1606   }
1607   VCP->AppearanceArray = (AppearancePtr PNTR) ptr2;
1608   ptr2 += AppearanceCount;
1609   VCP->AppearanceNameArray = (CharPtr PNTR) ptr2;
1610   ptr2 += AppearanceCount + 1; /* add a NULL pointer to terminate the list */
1611   VCP->FilterArray = (FilterPtr PNTR) ptr2;
1612   ptr2 += FilterCount;
1613   VCP->FilterNameArray = (CharPtr PNTR) ptr2;
1614   VNP = VCP->AppearanceList;
1615   nameVNP = VCP->AppearanceNameList;
1616   for (i = 0; i < AppearanceCount; i++) {
1617     VCP->AppearanceArray[i] = VNP->data.ptrvalue;
1618     VCP->AppearanceNameArray[i] = nameVNP->data.ptrvalue;
1619     VNP = VNP->next;
1620     nameVNP = nameVNP->next;
1621   }
1622 
1623   VNP = VCP->FilterList;
1624   nameVNP = VCP->FilterNameList;
1625   for (i = 0; i < FilterCount; i++) {
1626     VCP->FilterArray[i] = VNP->data.ptrvalue;
1627     VCP->FilterNameArray[i] = nameVNP->data.ptrvalue;
1628     VNP = VNP->next;
1629     nameVNP = nameVNP->next;
1630   }
1631   VCP->ArraysPopulated = TRUE;
1632   newGraphicViewer_ConfigFileParse_Global = VCP;
1633   return VCP;
1634 }
1635 
1636 /* returns count of objects successfully parsed -- so 0 on failure */
1637 NLM_EXTERN Uint2 ParseConfigFile (
1638   ViewerConfigsPtr VCP
1639 )
1640 
1641 {
1642   Char     tag [32];
1643   Char     name [128];
1644   Int2     i;
1645   Uint2    fCount = 0, aCount = 0;
1646   VoidPtr  tempPtr;
1647   unsigned val; /* to match scanf("%ud"...) */
1648 
1649   GetAppParam (config_filename, "filters", "maxlabelscale", NULL, tag, sizeof (tag));
1650   if (sscanf (tag, "%ud", &val) != 1) {
1651     val = 200;
1652   }
1653   VCP->DefaultMaxScaleWithLabels = val;
1654 
1655   GetAppParam (config_filename, "filters", "grouppadding", NULL, tag, sizeof (tag));
1656   if (sscanf (tag, "%ud", &val) != 1) {
1657     val = 3;
1658   }
1659   VCP->DefaultGroupPadding = val;
1660 
1661   GetAppParam (config_filename, "filters", "rowpadding", NULL, tag, sizeof (tag));
1662   if (sscanf (tag, "%ud", &val) != 1) {
1663     val = 5;
1664   }
1665   VCP->DefaultRowPadding = val;
1666 
1667   GetAppParam (config_filename, "styles", "maxarrowscale", NULL, tag, sizeof (tag));
1668   if (sscanf (tag, "%ud", &val) != 1) {
1669     val = 5;
1670   }
1671   VCP->DefaultMaxScaleForArrow = val;
1672 
1673   GetAppParam (config_filename, "styles", "minarrowpixels", NULL, tag, sizeof (tag));
1674   if (sscanf (tag, "%ud", &val) != 1) {
1675     val = 5;
1676   }
1677   VCP->DefaultMinPixelsForArrow = val;
1678 
1679   VCP->DefaultShadeSmears = FALSE;
1680   if (GetAppParam (config_filename, "styles", "shadesmears", NULL, tag, sizeof (tag))) {
1681     i = StringIndexInStringList (tag, BoolStrings);
1682     if (i >= 0 && i < DIM (BoolValues)) {
1683       VCP->DefaultShadeSmears = BoolValues [i];
1684     }
1685   }
1686 
1687   for (i = 0; i < 110; i++) {   /* do filters first */
1688     if (i < 10) {
1689       sprintf (tag, "filter0%d", (unsigned) i);
1690     } else {
1691       sprintf (tag, "filter%d", (unsigned) i - 9);
1692     }
1693     if (GetAppParam (config_filename, "filters", tag, NULL, name, sizeof (name))) {
1694       tempPtr = ParseFilter (name, VCP);
1695       if (tempPtr == NULL) continue;
1696       fCount++;
1697     }
1698   }
1699   for (i = 0; i < 110; i++) {
1700     if (i < 10) {
1701       sprintf (tag, "style0%d", (unsigned) i);
1702     } else {
1703       sprintf (tag, "style%d", (unsigned) i - 9);
1704     }
1705     if (GetAppParam (config_filename, "styles", tag, NULL, name, sizeof (name))) {
1706       tempPtr = ParseAppearance (name, VCP);
1707       if (tempPtr == NULL) continue;
1708       aCount++;
1709     }
1710   }
1711   return (aCount + fCount);
1712 }
1713 
1714 NLM_EXTERN FilterPtr DestroyFilter (
1715   FilterPtr FP,
1716   ViewerConfigsPtr VCP
1717 )
1718 
1719 {
1720   FilterItemPtr  FIP;
1721   ValNodePtr     VNP;
1722   Uint1          i;
1723 
1724   if (FP == NULL || VCP == NULL) {
1725     return NULL;
1726   }
1727   for (VNP = FP->FilterItemList; VNP; VNP = VNP->next) { /* free all filterItems, and their labels */
1728     FIP = (FilterItemPtr) VNP->data.ptrvalue;
1729     if (FIP == NULL) {
1730       continue;
1731     }
1732     MemFree (FIP->GroupLabel);
1733     MemFree (FIP);
1734   }
1735   for (VNP = VCP->FilterList; VNP != NULL; VNP = VNP->next) {
1736     if (VNP->data.ptrvalue == FP) {
1737       i = VNP->choice;
1738       VNP = ValNodeExtract (&VCP->FilterList, i);
1739       break;
1740     }
1741   }
1742   if (VNP != NULL) {
1743     MemFree (VNP);
1744     VNP = ValNodeExtract (&VCP->FilterNameList, i);
1745     MemFree (VNP->data.ptrvalue);
1746     MemFree (VNP);
1747   }
1748   --VCP->FilterCount;
1749   MemFree (FP);
1750   return NULL;
1751 }
1752 
1753 NLM_EXTERN void AddAppearanceItemToAppearance (
1754   AppearanceItemPtr AIP,
1755   AppearancePtr AP,
1756   Uint1 newFeatdef,
1757   ViewerConfigsPtr VCP
1758 )
1759 
1760 {
1761   Uint1       i;
1762   ValNodePtr  VNP;
1763 
1764   if (AIP == NULL || AP == NULL || VCP == NULL) return;
1765   if (newFeatdef == FEATDEF_ANY) {
1766     for (i = 0; i < APPEARANCEITEM_MAX; i++) {
1767       AP->FeaturesAppearanceItem [i] = AIP;
1768     }
1769   } else if (newFeatdef == FEATDEF_ANY_RNA) {
1770     for (i = FEATDEF_preRNA; i <= FEATDEF_otherRNA; i++) {
1771       AP->FeaturesAppearanceItem [i] = AIP;
1772     }
1773     AP->FeaturesAppearanceItem [FEATDEF_misc_RNA] = AIP;
1774     AP->FeaturesAppearanceItem [FEATDEF_precursor_RNA] = AIP;
1775     AP->FeaturesAppearanceItem [FEATDEF_snoRNA] = AIP;
1776   } else if (newFeatdef == FEATDEF_ANY_PROT) {
1777     AP->FeaturesAppearanceItem [FEATDEF_PROT] = AIP;
1778     for (i = FEATDEF_preprotein; i <= FEATDEF_transit_peptide_aa; i++) {
1779       AP->FeaturesAppearanceItem [i] = AIP;
1780     }
1781   } else if (newFeatdef == FEATDEF_IMP) {
1782     for (i = FEATDEF_allele; i <= FEATDEF_35_signal; i++) {
1783       AP->FeaturesAppearanceItem [i] = AIP;
1784     }
1785   } else if (newFeatdef < APPEARANCEITEM_MAX) {
1786     AP->FeaturesAppearanceItem [newFeatdef] = AIP;
1787   } else return;
1788   for (VNP = AP->AppearanceItemList; VNP != NULL && VNP->data.ptrvalue != AIP; VNP = VNP->next) continue;
1789   if (! (VNP != NULL && VNP->data.ptrvalue == AIP)) {    /* AIP was not previously in the AppearanceItemList */
1790     ValNodeAddPointer (&AP->AppearanceItemList, 0, AIP);
1791   }
1792 }
1793 
1794 NLM_EXTERN AppearancePtr DestroyAppearance (
1795   AppearancePtr AP,
1796   ViewerConfigsPtr VCP
1797 )
1798 
1799 {
1800   Uint1       i;
1801   ValNodePtr  VNP;
1802 
1803   if (AP == NULL || VCP == NULL) return NULL;
1804   if (AP->AppearanceItemList != NULL) {
1805     ValNodeFreeData (AP->AppearanceItemList);
1806   }
1807   for (VNP = VCP->AppearanceList; VNP != NULL; VNP = VNP->next) {
1808     if (VNP->data.ptrvalue == AP) {
1809       i = VNP->choice;
1810       VNP = ValNodeExtract (&VCP->AppearanceList, i);
1811       break;
1812     }
1813   }
1814   if (VNP != NULL) {
1815     MemFree (VNP);
1816     VNP = ValNodeExtract (&VCP->AppearanceNameList, i);
1817     MemFree (VNP);
1818   }
1819   MemFree (AP);
1820   return NULL;
1821 }
1822 
1823 static void getDim_do_not_render (
1824   RenderInputPtr RIP,
1825   Int4Ptr Start,
1826   Int4Ptr Stop,
1827   Uint2Ptr height,
1828   ViewerContextPtr vContext
1829 )
1830 
1831 {
1832   RelevantFeatureItemPtr RFIP;
1833 
1834   RFIP = RIP->RFIP;
1835 
1836   *Start = *Stop = RFIP->Left;
1837   *height = 1;
1838 }
1839 
1840 static void do_not_render (
1841   RenderInputPtr RIP,
1842   ViewerContextPtr vContext
1843 )
1844 
1845 {
1846   return;
1847 }
1848 
1849 static void getDim_render_with_line (
1850   RenderInputPtr RIP,
1851   Int4Ptr Start,
1852   Int4Ptr Stop,
1853   Uint2Ptr height,
1854   ViewerContextPtr vContext
1855 )
1856 
1857 {
1858   RelevantFeatureItemPtr RFIP;
1859 
1860   RFIP = RIP->RFIP;
1861 
1862   if (vContext->allFeatures) {
1863     *Start = RFIP->Left;
1864     *Stop = RFIP->Right;
1865   } else {
1866     *Start = MAX (RFIP->Left, vContext->from);
1867     *Stop = MAX (RFIP->Right, vContext->to);
1868   }
1869   *height = 1;
1870 }
1871 
1872 static void render_wrap_around_markers(
1873   RenderInputPtr RIP,
1874   ViewerContextPtr vContext
1875 )
1876 {
1877   Uint4      StartY;
1878   RelevantFeatureItemPtr RFIP;
1879   AppearanceItemPtr       AIP;
1880 
1881   RFIP = RIP->RFIP;
1882   AIP = RIP->AIP;
1883   StartY = RIP->yStart - (RIP->featureOffset) - AIP->Height / 2 - 1;
1884 
1885   AddSymbol(RIP->drawSeg, RFIP->Left - 3 * vContext->viewScale,  StartY, LEFT_TRIANGLE_SYMBOL,  FALSE, MIDDLE_LEFT, 0);
1886   AddSymbol(RIP->drawSeg, RFIP->Right + 3 * vContext->viewScale, StartY, RIGHT_TRIANGLE_SYMBOL, FALSE, MIDDLE_RIGHT, 0);
1887 }
1888 
1889 static void render_with_line (
1890   RenderInputPtr RIP,
1891   ViewerContextPtr vContext
1892 )
1893 
1894 {
1895   Uint4      StartY;
1896   PrimitivE  thisPrim;
1897   RelevantFeatureItemPtr RFIP;
1898   AppearanceItemPtr       AIP;
1899   Int4       start, stop;
1900 
1901   RFIP = RIP->RFIP;
1902   AIP = RIP->AIP;
1903 
1904   StartY = RIP->yStart - (RIP->featureOffset) - AIP->Height / 2;
1905   if (vContext->allFeatures) {
1906     start = RFIP->Left;
1907     stop = RFIP->Right;
1908   } else {
1909     start = MAX (RFIP->Left, vContext->from);
1910     stop = MAX (RFIP->Right, vContext->to);
1911   }
1912 
1913   if (!RFIP->circularSpanningOrigin || RFIP->numivals < 2) {
1914     thisPrim = AddLine (RIP->drawSeg, start, StartY, stop, StartY, 0, 0);
1915   SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
1916   } else { 
1917     /* feature spans the origin. draw specially */
1918     if (RFIP->ivals[0] < stop) {
1919       thisPrim = AddLine (RIP->drawSeg, RFIP->ivals[0], StartY, stop, StartY, 0, 0); 
1920       SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
1921     }
1922     if (RFIP->ivals[2* RFIP->numivals - 1] > start) {
1923       thisPrim = AddLine (RIP->drawSeg, start, StartY, RFIP->ivals[2* RFIP->numivals - 1], StartY, 0, 0); 
1924       SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
1925     }
1926     render_wrap_around_markers(RIP, vContext);
1927   }
1928 }
1929 
1930 static void getDim_render_with_capped_line (
1931   RenderInputPtr RIP,
1932   Int4Ptr Start,
1933   Int4Ptr Stop,
1934   Uint2Ptr height,
1935   ViewerContextPtr vContext
1936 )
1937 
1938 {
1939   RelevantFeatureItemPtr RFIP;
1940   AppearanceItemPtr AIP;
1941 
1942   RFIP = RIP->RFIP;
1943   AIP = RIP->AIP;
1944 
1945 
1946   if (vContext->allFeatures) {
1947     *Start = RFIP->Left;
1948     *Stop = RFIP->Right;
1949   } else {
1950     *Start = MAX (RFIP->Left, vContext->from);
1951     *Stop = MAX (RFIP->Right, vContext->to);
1952   }
1953   *height = AIP->Height;
1954 }
1955 
1956 static void render_with_capped_line (
1957   RenderInputPtr RIP,
1958   ViewerContextPtr vContext
1959 )
1960 
1961 {
1962   PrimitivE               thisPrim;
1963   AppearanceItemPtr       AIP;
1964   RelevantFeatureItemPtr  RFIP;
1965   Int4                    StartY;
1966 
1967   StartY = RIP->yStart - (RIP->featureOffset);
1968   render_with_line (RIP, vContext);
1969   RFIP = RIP->RFIP;
1970   AIP = RIP->AIP;
1971 
1972 
1973   if (!RFIP->circularSpanningOrigin || RFIP->numivals < 2) {
1974     thisPrim = AddLine (RIP->drawSeg, RFIP->Left, StartY, RFIP->Left, StartY - AIP->Height, 0, 0);
1975     SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
1976     thisPrim = AddLine (RIP->drawSeg, RFIP->Right, StartY, RFIP->Right, StartY - AIP->Height, 0, 0);
1977     SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
1978   } else {
1979     /* origin spanning feature */
1980     thisPrim = AddLine (RIP->drawSeg, RFIP->ivals[0], StartY, RFIP->ivals[0], StartY - AIP->Height, 0, 0);
1981     SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
1982     thisPrim = AddLine (RIP->drawSeg, RFIP->ivals[2* RFIP->numivals - 1], StartY, RFIP->ivals[2* RFIP->numivals - 1], StartY - AIP->Height, 0, 0);
1983     SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);    
1984   }
1985 }
1986 
1987 static void getDim_render_with_box (
1988   RenderInputPtr RIP,
1989   Int4Ptr Start,
1990   Int4Ptr Stop,
1991   Uint2Ptr height,
1992   ViewerContextPtr vContext
1993 )
1994 {
1995   RelevantFeatureItemPtr RFIP;
1996   AppearanceItemPtr AIP;
1997 
1998   RFIP = RIP->RFIP;
1999   AIP = RIP->AIP;
2000   *Start = RFIP->Left;
2001   *Stop = RFIP->Right;
2002   *height = AIP->Height;
2003   if (IsInsertTic(RFIP))
2004     *height += AIP->Height; 
2005 }
2006 
2007 static Boolean IsInsertTic(RelevantFeatureItemPtr RFIP)
2008 {
2009   int   iival;
2010   
2011   /* Insert Tics only on alignments with more than one segment */
2012   if (RFIP->featdeftype != APPEARANCEITEM_Alignment || RFIP->numivals < 2)
2013     return FALSE;
2014 
2015   for (iival = 0; iival < RFIP->numivals; ++iival) {
2016     /* a segment with beginning and end the same is an insert */
2017     if (RFIP->ivals [2 * iival] == RFIP->ivals [2 * iival + 1] ) {
2018       return TRUE;
2019     }
2020   }
2021   return FALSE;
2022 }
2023 
2024 #if 0 /* TestForSmearOverlap was replaced by PixelsBetweenSeqCoords everwhere it was used */
2025 static Boolean TestForSmearOverlap (
2026   Int4 PrevEnd,
2027   Int4 NewStart,
2028   ViewerContextPtr vContext
2029 )
2030 {
2031   return PixelsBetweenSeqCoords(PrevEnd, NewStart, vContext->viewScale) < 1;
2032 }
2033 #endif
2034 
2035 /* returns TRUE if no visible gap. */
2036 static Boolean NoVisibleGap (
2037   Int4 x1,
2038   Int4 x2,
2039   Uint4 viewScale
2040 )
2041 {
2042   return abs(PixelsBetweenSeqCoords(x1, x2, viewScale)) < 2;
2043 }
2044 
2045 static Boolean TestForSmear (
2046   RelevantFeatureItemPtr RFIP1,
2047   RelevantFeatureItemPtr RFIP2,
2048   Uint4  viewScale
2049 )
2050 {
2051   Uint4  minSeperation;
2052   
2053   minSeperation = 5;  /* do not smear a feature more than 5 pixels wide */
2054 
2055   if (abs(PixelsBetweenSeqCoords(RFIP1->Right, RFIP1->Left, viewScale)) >= minSeperation) return FALSE;
2056   if (abs(PixelsBetweenSeqCoords(RFIP2->Right, RFIP2->Left, viewScale)) >= minSeperation) return FALSE;
2057 
2058   return (NoVisibleGap (RFIP1->Right, RFIP2->Left, viewScale)
2059           || NoVisibleGap (RFIP1->Right, RFIP2->Right, viewScale)
2060           || NoVisibleGap (RFIP1->Left, RFIP2->Right, viewScale)
2061           || NoVisibleGap (RFIP1->Left, RFIP2->Left, viewScale) );
2062 
2063 }
2064 
2065 static Int4 PixelsBetweenSeqCoords(Int4 left, Int4 right, Uint4 viewScale)
2066 {
2067   /* Will be negative if right is really not right of left. */
2068   /* 0 if they fall on the same pixel. */
2069   /* Do NOT change this to (right - left)/viewScale. NOT the same. */
2070   return (right/viewScale - left/viewScale);
2071 }
2072 
2073 static void render_with_box_master (
2074   RenderInputPtr RIP,
2075   Boolean fillBox,
2076   ViewerContextPtr vContext
2077 )
2078 
2079 {
2080   Int4                    StartY;
2081   Uint2                   pieceIValStart;
2082   PrimitivE               thisPrim;
2083   Uint2                   i;
2084   Int4                    mid;
2085   AppearanceItemPtr       AIP;
2086   AppearancePtr           AP;
2087   RelevantFeatureItemPtr  RFIP;
2088   Boolean                 shade_p;
2089   Uint1                   arrow;
2090 
2091   RFIP = RIP->RFIP;
2092   AIP = RIP->AIP;
2093   AP = vContext->AppPtr;
2094   StartY = RIP->yStart - (RIP->featureOffset);
2095   if (RFIP->LeftEnd == EndClipped) {
2096     thisPrim = AddLine (RIP->drawSeg, vContext->from, StartY - AIP->Height / 2, RFIP->Left, StartY - AIP->Height / 2, 0, 0);
2097     SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2098   }
2099   if (RFIP->RightEnd == EndClipped) {
2100     thisPrim = AddLine (RIP->drawSeg, vContext->to, StartY - AIP->Height / 2, RFIP->Right, StartY - AIP->Height / 2, 0, 0);
2101     SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2102   }
2103   if (RFIP->numivals == 1) {
2104     shade_p = (RFIP->entityID == 0 && RFIP->itemID == 0) ? AP->ShadeSmears : FALSE;
2105     if (RFIP->entityID == 0 && RFIP->itemID == 0) { /* is this a multi-feature smear? */
2106       if (shade_p) {
2107         /*        AddAttribute (RIP->drawSeg, SHADING_ATT, 0, 0, MEDIUM_SHADING, 0, 0);*/
2108       }
2109       thisPrim = AddRectangle (RIP->drawSeg, RFIP->Left, StartY, RFIP->Right, StartY - AIP->Height, NO_ARROW, fillBox, 0);
2110       SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2111       if (shade_p) {
2112         /*        AddAttribute (RIP->drawSeg, SHADING_ATT, 0, 0, NO_SHADING, 0, 0);*/
2113       }
2114 
2115     } else { /* nope */
2116       arrow = NO_ARROW;
2117       if (RFIP->featstrand == Seq_strand_plus) {
2118         arrow = RIGHT_ARROW;
2119       } else if (RFIP->featstrand == Seq_strand_minus) {
2120         arrow = LEFT_ARROW;
2121       }
2122       if (! AIP->ShowArrow) {
2123         arrow = NO_ARROW;
2124       }
2125       if (abs(PixelsBetweenSeqCoords( RFIP->Left, RFIP->Right, vContext->viewScale)) < AP->MinPixelsForArrow) {
2126         arrow = NO_ARROW;
2127       }
2128       if (vContext->viewScale > AP->MaxScaleForArrow) {
2129         arrow = NO_ARROW;
2130       }
2131       thisPrim = AddRectangle (RIP->drawSeg, RFIP->Left, StartY, RFIP->Right, StartY - AIP->Height, arrow, fillBox, 0);
2132       SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2133     }
2134     return;
2135   } else {
2136     i = 0;
2137     while (i < RFIP->numivals) {
2138       /* collect a group of interval(s) which do not contain any pixels between them */
2139       pieceIValStart = i;
2140       /* this tests is the i-plus-1th feature is part of the smear, which goes from pieceIValStart to i, inclusive */
2141       /* On alignments only smear away the gaps caused by inserts. */
2142       if (RFIP->featdeftype == APPEARANCEITEM_Alignment) {
2143         while (i + 1 < RFIP->numivals &&
2144                NoVisibleGap (RFIP->ivals [2 * i + 1], RFIP->ivals [2 * i + 2], vContext->viewScale) &&
2145                (RFIP->ivals [2 * i] == RFIP->ivals [2 * i + 1]  ||  RFIP->ivals [2 * i + 2] == RFIP->ivals [2 * i + 3])) {
2146           i++;
2147         }
2148       } else {
2149         while (i + 1 < RFIP->numivals &&
2150                NoVisibleGap (RFIP->ivals [2 * i + 1], RFIP->ivals [2 * i + 2], vContext->viewScale)) {
2151           i++;
2152         }
2153       }
2154 
2155       /* draw the segment and the gap -- drawing the gap first, so that it is overdrawn by the segment */
2156       if (i + 1 < RFIP->numivals) { /* a gap is present if there are more ivals to consider after i*/
2157         if (RFIP->circularSpanningOrigin && RFIP->ivals [2 * i + 1] > RFIP->ivals [2 * i + 2]) {
2158           /* on a circular bioseq, this gap spans the origin and has to be drawn specially */
2159           /* RFIP->Left will be 0, RFIP->Right will be the length of the Bioseq */
2160           if (RFIP->ivals [2 * i + 1] < RFIP->Right - 1) {
2161             /* Draw the part of the gap at the right end. Might not be any. */
2162             thisPrim = AddLine (RIP->drawSeg, RFIP->ivals[2 * i + 1], StartY - AIP->Height / 2, RFIP->Right, StartY - AIP->Height / 2, 0, 0);
2163             SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2164           }
2165           if (RFIP->Left <  RFIP->ivals [2 * i + 2]) {
2166             /* draw the part of the gap at the left end. */
2167             thisPrim = AddLine (RIP->drawSeg, RFIP->Left, StartY - AIP->Height / 2, RFIP->ivals[2 * i + 2], StartY - AIP->Height / 2, 0, 0);
2168             SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2169           }
2170           /* No. I didn't put in code to make those lines 'Angle Gap' style. Figured it wouldn't show up very well. You can if you want. */
2171           
2172           /* draw markers to show that this wraps around */
2173           render_wrap_around_markers(RIP, vContext);
2174 
2175         } else {
2176           /* ordinary gap */
2177           if (AIP->GapChoice == LineGap) {
2178             thisPrim = AddLine (RIP->drawSeg, RFIP->ivals [2 * i + 1],                       StartY - AIP->Height / 2, 
2179                                               RFIP->ivals [2 * i + 2] - vContext->viewScale, StartY - AIP->Height / 2, 0, 0);
2180             SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2181           } else if (AIP->GapChoice == AngleGap) {
2182             mid = (RFIP->ivals [2 * i + 2] + RFIP->ivals [2 * i + 1]) / 2;
2183             thisPrim = AddLine (RIP->drawSeg, RFIP->ivals [2 * i + 1], StartY - AIP->Height / 2, 
2184                                               mid,                     StartY, 0, 0);
2185             SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2186             thisPrim = AddLine (RIP->drawSeg, mid,                                           StartY, 
2187                                               RFIP->ivals [2 * i + 2] - vContext->viewScale, StartY - AIP->Height / 2, 0, 0);
2188             SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2189           }
2190         }
2191       }
2192       arrow = NO_ARROW;
2193       if (i == RFIP->numivals - 1  &&  RFIP->featstrand == Seq_strand_plus) {
2194         arrow = RIGHT_ARROW;
2195       } else if (RFIP->featstrand == Seq_strand_minus) {
2196         /* on minus strands, Alignments put their intervals in coordinate order.
2197           Other features put theirs in biological order (right to  left),
2198           so we have to draw the arrow differently (or we could sort the intervals so they 
2199           ended up the same. */
2200         if ((pieceIValStart == 0 && RFIP->featdeftype == APPEARANCEITEM_Alignment) ||
2201           (i == RFIP->numivals - 1  &&   RFIP->featdeftype != APPEARANCEITEM_Alignment))
2202           arrow = LEFT_ARROW;
2203       }
2204       if (! AIP->ShowArrow) {
2205         arrow = NO_ARROW;
2206       }
2207       if (ABS (RFIP->Right - RFIP->Left) / vContext->viewScale < AP->MinPixelsForArrow) {
2208         arrow = NO_ARROW;
2209       }
2210       if (vContext->viewScale > AP->MaxScaleForArrow) {
2211         arrow = NO_ARROW;
2212       }
2213       thisPrim = AddRectangle (RIP->drawSeg, RFIP->ivals [2 * pieceIValStart], StartY, RFIP->ivals [2 * i + 1], StartY - AIP->Height, arrow, fillBox, 0);
2214       SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2215       i++;
2216     }
2217     if (RFIP->featdeftype == APPEARANCEITEM_Alignment) {
2218       render_insert_tics(RIP);
2219     }
2220   }
2221 }
2222 
2223 
2224 static void render_insert_tics(RenderInputPtr RIP)
2225 {
2226   AppearanceItemPtr       AIP;
2227   RelevantFeatureItemPtr  RFIP;
2228   PrimitivE               thisPrim;
2229   Int4                    StartY;
2230   Int4                    i;
2231 
2232   RFIP = RIP->RFIP;
2233   AIP = RIP->AIP;
2234   StartY = RIP->yStart - (RIP->featureOffset);
2235 
2236   /* if we are drawing an aligment, and the interval is zero length, It is an insert. 
2237      Draw a vertical line the height of the bar under the bar to show this.  */
2238   for (i = 0; i < RFIP->numivals; ++i) {
2239     /* a segment with beginning and end the same is an insert */
2240     if (RFIP->ivals [2 * i] == RFIP->ivals [2 * i + 1] ) {
2241       thisPrim = AddLine(RIP->drawSeg, RFIP->ivals [2 * i], StartY - AIP->Height, 
2242                                        RFIP->ivals [2 * i], StartY - AIP->Height * 2, FALSE, 0);
2243       SetPrimitiveIDs(thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2244     }
2245   }
2246 }
2247 
2248 
2249 static void render_with_box (
2250   RenderInputPtr   RIP,
2251   ViewerContextPtr vContext
2252 )
2253 
2254 {
2255   render_with_box_master (RIP, TRUE, vContext);
2256 
2257 }
2258 
2259 static void render_with_outline_box (
2260   RenderInputPtr   RIP,
2261   ViewerContextPtr vContext
2262 )
2263 
2264 {
2265   render_with_box_master (RIP, FALSE, vContext);
2266 }
2267 
2268 static const RenderClass RenderAlgorithmTable [] = {
2269   {do_not_render, getDim_do_not_render}, /* do_not_render */
2270   {render_with_line, getDim_render_with_line}, /* Render_Line */
2271   {render_with_capped_line, getDim_render_with_capped_line}, /* Render_CappedLine */
2272   {render_with_box, getDim_render_with_box},   /* Render_Box */
2273   {render_with_outline_box, getDim_render_with_box},   /* Render_OutlineBox */
2274   /* !!! these do not exist right now - can they be removed? !!! */
2275   {render_with_line, getDim_render_with_line},
2276   {render_with_box, getDim_render_with_box},
2277   {render_with_line, getDim_render_with_line},
2278   {render_with_line, getDim_render_with_line},
2279   {render_with_line, getDim_render_with_line},
2280   {render_with_line, getDim_render_with_line},
2281   {render_with_line, getDim_render_with_line}
2282 };
2283 
2284 static void DrawFeatureAndLabel (
2285   RenderInputPtr RIP,
2286   ViewerContextPtr vContext
2287 )
2288 
2289 {
2290   AppearanceItemPtr       AIP;
2291   RelevantFeatureItemPtr  RFIP;
2292   FilterItemPtr           FIP;
2293   FilterPtr               FP;
2294   CharPtr                 label;
2295   Char                    tempStringBuffer [256];
2296   Char                    shortLabel [41];
2297   Uint1                   stringFlags;
2298   Uint4                   textWidthBP;
2299   Int4                    textStartX;
2300   Int4                    textStartY;
2301   Uint1                   labelAlign, fitChars;
2302   PrimitivE               thisPrim;
2303   Boolean                 addType;
2304   Boolean                 addDesc;
2305 
2306   FIP = RIP->FIP;
2307   RFIP = RIP->RFIP;
2308   AIP = RIP->AIP;
2309   FP = vContext->FltPtr;
2310   addType = AIP->AddTypeToLabel;
2311   addDesc = AIP->AddDescToLabel;
2312   if (FIP->AddTypeToLabel != TristateUnset) {
2313     addType = BOOL_FROM_SET_TRISTATE (FIP->AddTypeToLabel);
2314   }
2315   if (FIP->AddDescToLabel != TristateUnset) {
2316     addDesc = BOOL_FROM_SET_TRISTATE (FIP->AddDescToLabel);
2317   }
2318 
2319   /*  RIP->drawSeg = CreateSegment (RIP->drawSeg, 0, 0);*/
2320   /* Place each feature in its own segment.  This is not necessary for simple features
2321      but perhaps the inefficiency is acceptable because it simplifies the algorithm. */
2322   (*RenderAlgorithmTable [AIP->RenderChoice].RenderFunc) (RIP, vContext);
2323   if (FIP->LabelLoc == LabelNone) return;
2324   if (vContext->viewScale > FP->MaxScaleWithLabels) return;
2325   stringFlags = 0;
2326   if (addDesc && RFIP->ContentLabel != NULL) {
2327     stringFlags |= 1;
2328   }
2329   if (addType) {
2330     stringFlags |= 2;
2331   }
2332   label = NULL;
2333   switch (stringFlags) {
2334     case 0:                    /* no label */
2335       return;
2336     case 1:                    /*comment but not type */
2337       label = RFIP->ContentLabel;
2338       break;
2339     case 2:                    /*only type */
2340       label = FindFeatStrFromFeatDefType (RFIP->featdeftype);
2341       break;
2342     case 3:                    /*add both */
2343       if (StringCmp (FindFeatStrFromFeatDefType (RFIP->featdeftype), RFIP->ContentLabel) != 0) {
2344         sprintf (tempStringBuffer, "%s: %s", FindFeatStrFromFeatDefType (RFIP->featdeftype), RFIP->ContentLabel);
2345         label = tempStringBuffer;
2346       } else {
2347         label = RFIP->ContentLabel;
2348       }
2349       break;
2350   }
2351   if (StringHasNoText (label)) return;
2352   LabelCopy (shortLabel, label, sizeof (shortLabel)-1);
2353   SelectFont (AIP->LabelFont);
2354   textWidthBP = StringWidth (shortLabel) * vContext->viewScale;
2355   switch (FIP->LabelLoc) {
2356     case LabelAboveClip:
2357       if (textWidthBP + 2 * vContext->viewScale >= (RFIP->Right - RFIP->Left)) {
2358         fitChars = Nlm_FitStringWidth (shortLabel, (RFIP->Right - RFIP->Left) / vContext->viewScale);
2359         if (fitChars <= 2 && StringLen (shortLabel) != fitChars) return;
2360         shortLabel [fitChars] = '>';
2361         shortLabel [fitChars + 1] = '\0';
2362       }
2363       textStartX = (RFIP->Left + RFIP->Right) / 2;
2364       /*      textStartY = RIP->yStart - RIP->rowHeight / 2; -- change "labelInside" to mean "above, but not wider than"*/
2365       textStartY = RIP->yStart;
2366       labelAlign = LOWER_CENTER;
2367       break;
2368     case LabelInside:
2369       if (textWidthBP + 2 * vContext->viewScale >= (RFIP->Right - RFIP->Left)) {
2370         fitChars = Nlm_FitStringWidth (shortLabel, (RFIP->Right - RFIP->Left) / vContext->viewScale);
2371         shortLabel [fitChars + 1] = '\0';
2372       }
2373       textStartX = (RFIP->Left + RFIP->Right) / 2;
2374       textStartY = RIP->yStart - RIP->rowHeight / 2;
2375       labelAlign = MIDDLE_CENTER;
2376       break;
2377     case LabelAboveCull:
2378       if (textWidthBP + 2 * vContext->viewScale >= (RFIP->Right - RFIP->Left)) return;
2379       /* else fall through and display the label above */
2380     case LabelAbove:
2381       textStartX = (RFIP->Left + RFIP->Right) / 2;
2382       textStartY = RIP->yStart;
2383       labelAlign = LOWER_CENTER;
2384       break;
2385     case LabelBelow:
2386       textStartY = RIP->yStart - RIP->Height;
2387       textStartX = (RFIP->Left + RFIP->Right) / 2;
2388       labelAlign = LOWER_CENTER;
2389       break;
2390     case LabelLeft:
2391       textStartX = (RFIP->Left);
2392       textStartY = (RIP->yStart);
2393       labelAlign = LOWER_LEFT;
2394       break;
2395     case LabelRight:
2396       textStartX = (RFIP->Right);
2397       textStartY = (RIP->yStart);
2398       labelAlign = LOWER_RIGHT;
2399       break;
2400     default:
2401       return;
2402   }
2403   thisPrim = AddTextLabel (RIP->labelSeg, textStartX, textStartY, shortLabel, AIP->LabelFont, 1, labelAlign, 0);
2404   SetPrimitiveIDs (thisPrim, RFIP->entityID, RFIP->itemID, RFIP->itemType, 0);
2405 }
2406 
2407 static RelevantFeatureItemPtr BuildClippedRFIP (
2408   RelevantFeatureItemPtr inputRFIP,
2409   FilterProcessStatePtr FPSP,
2410   ViewerContextPtr vContext
2411 )
2412 
2413 {
2414   RelevantFeatureItemPtr newRFIP;
2415   ValNodePtr VNP = NULL;
2416   Uint2 i, newnumivals;
2417   Int4  from, to;
2418   Boolean useThis = TRUE, useLast;
2419   Boolean clippedLeft, clippedRight;
2420   Boolean lastClippedLeft, lastClippedRight;
2421 
2422   if ((newRFIP = MemNew (sizeof (RelevantFeatureItem))) == NULL) return NULL;
2423   if ((VNP = MemNew (sizeof (ValNode))) == NULL) {
2424     MemFree (newRFIP);
2425     return NULL;
2426   }
2427   VNP->data.ptrvalue = newRFIP;
2428   VNP->next = FPSP->needFreeList;
2429   FPSP->needFreeList = VNP;
2430   MemCopy (newRFIP, inputRFIP, sizeof (RelevantFeatureItem));
2431   if ((inputRFIP->Left <= vContext->from && inputRFIP->Right <= vContext->from)
2432       || (inputRFIP->Left >= vContext->to && inputRFIP->Right >= vContext->to)) {
2433     return NULL; /* entire feature removed by clipping */
2434   }
2435   newRFIP->LeftEnd = (inputRFIP->Left >= vContext->from) ? inputRFIP->LeftEnd : EndPartial;
2436   newRFIP->RightEnd  = (inputRFIP->Right <= vContext->to) ?  inputRFIP->RightEnd  : EndPartial;
2437   if (inputRFIP->numivals == 1) {
2438     newRFIP->Left  = MAX (vContext->from, MIN (vContext->to, inputRFIP->Left ));
2439     newRFIP->Right = MAX (vContext->from, MIN (vContext->to, inputRFIP->Right));
2440     newRFIP->LeftEnd = (inputRFIP->Left >= vContext->from) ? inputRFIP->LeftEnd : EndPartial;
2441     newRFIP->RightEnd  = (inputRFIP->Right <= vContext->to) ?  inputRFIP->RightEnd  : EndPartial;
2442   } else {
2443     newRFIP->Left = vContext->to;
2444     newRFIP->Right = vContext->from;
2445     newnumivals = 0;
2446     for (i = 0; i < inputRFIP->numivals; i++) {
2447       from = inputRFIP->ivals [2 * i];
2448       to = inputRFIP->ivals [2 * i + 1];
2449       if (from <= vContext->to && from >= vContext->from) {
2450         newnumivals ++;
2451       } else if (to <=  vContext->to && to >= vContext->from) {
2452         newnumivals ++;
2453       }
2454     }
2455     newRFIP->numivals = newnumivals;
2456     if ((newRFIP->ivals = MemNew (2 * newnumivals * sizeof (Int4))) == NULL) return NULL;
2457     if ((VNP = MemNew (sizeof (ValNode))) == NULL) {
2458       MemFree (newRFIP->ivals);
2459       return NULL;
2460     }
2461     VNP->data.ptrvalue = newRFIP->ivals;
2462     VNP->next = FPSP->needFreeList;
2463     FPSP->needFreeList = VNP;
2464     newnumivals = 0;
2465     for (i = 0; i < inputRFIP->numivals; i++) {
2466       from = inputRFIP->ivals [2 * i];
2467       to = inputRFIP->ivals [2 * i + 1];
2468       if (from <= vContext->to && from >= vContext->from) {
2469         useThis = TRUE;
2470         clippedLeft = clippedRight = FALSE;
2471       } else if (to <=  vContext->to && to >= vContext->from) {
2472         useThis = TRUE;
2473         clippedLeft = clippedRight = FALSE;
2474       } else {
2475         useThis = FALSE;
2476         clippedLeft = ((from + to) < (vContext->from + vContext->to));
2477         clippedRight = !clippedLeft;
2478       }
2479       if (i == 0) {
2480         useLast = useThis;
2481         lastClippedLeft = clippedLeft;
2482         lastClippedRight = clippedRight;
2483       }
2484       if (lastClippedLeft && useThis) {
2485         newRFIP->LeftEnd  = EndClipped;
2486       } else if (lastClippedRight && useThis) {
2487         newRFIP->RightEnd = EndClipped;
2488       } else if (useLast && clippedLeft) {
2489         newRFIP->LeftEnd  = EndClipped;
2490       } else if (useLast && clippedRight) {
2491         newRFIP->RightEnd = EndClipped;
2492       }
2493       if (useThis) {
2494         from = MAX (vContext->from, MIN (vContext->to, from));
2495         to   = MAX (vContext->from, MIN (vContext->to, to  ));
2496         newRFIP->Left = MIN (newRFIP->Left, from);
2497         newRFIP->Left = MIN (newRFIP->Left, to);
2498         newRFIP->Right = MAX (newRFIP->Right, to);
2499         newRFIP->Right = MAX (newRFIP->Right, from);
2500         newRFIP->ivals [newnumivals++] = from;
2501         newRFIP->ivals [newnumivals++] = to;
2502       }
2503       useLast = useThis;
2504     }
2505   }
2506   if (newRFIP->Left > newRFIP->Right) return NULL;
2507   return newRFIP;
2508 }
2509 
2510 
2511 static void GetFeatureAndDecorationDimensions (
2512   RenderInputPtr RIP,
2513   ViewerContextPtr vContext
2514 )
2515 
2516 {
2517   AppearanceItemPtr       AIP;
2518   RelevantFeatureItemPtr  RFIP;
2519   FilterItemPtr           FIP;
2520   FilterPtr               FP;
2521   Int4                    Start, Stop;
2522   Uint2                   Height;
2523   Int2                    lineHeight;
2524   Int4                    textStartX;
2525   Uint4                   textWidthBP;
2526   CharPtr                 label = NULL;
2527   Char                    tempStringBuffer [256];
2528   Uint1                   stringFlags;
2529   Uint2                   featureOffset = 0;
2530   Boolean                 addType;
2531   Boolean                 addDesc;
2532 
2533   RFIP = RIP->RFIP;
2534   AIP = RIP->AIP;
2535   FIP = RIP->FIP;
2536   FP = vContext->FltPtr;
2537   addType = AIP->AddTypeToLabel;
2538   addDesc = AIP->AddDescToLabel;
2539   if (FIP->AddTypeToLabel != TristateUnset) {
2540     addType = BOOL_FROM_SET_TRISTATE (FIP->AddTypeToLabel);
2541   }
2542   if (FIP->AddDescToLabel != TristateUnset) {
2543     addDesc = BOOL_FROM_SET_TRISTATE (FIP->AddDescToLabel);
2544   }
2545 
2546   (*RenderAlgorithmTable [AIP->RenderChoice].GetDimensions) (RIP, &Start, &Stop, &Height, vContext);
2547   RIP->Height = Height;
2548   RIP->decorationHeight = Height;
2549   RIP->decorationLeft = RFIP->Left;
2550   RIP->decorationRight = RFIP->Right;
2551   RIP->featureOffset = 0;
2552 
2553   if (FIP->LabelLoc != LabelNone && vContext->viewScale <= FP->MaxScaleWithLabels) {
2554     stringFlags = 0;
2555     if (addDesc && RFIP->ContentLabel != NULL) {
2556       stringFlags |= 1;
2557     }
2558     if (addType) {
2559       stringFlags |= 2;
2560     }
2561 
2562     switch (stringFlags) {
2563       case 0:                  /* no label */
2564         break;
2565       case 1:                  /*comment but not type */
2566         label = RFIP->ContentLabel;
2567         break;
2568       case 2:                  /*only type */
2569         label = FindFeatStrFromFeatDefType (RFIP->featdeftype);
2570         break;
2571       case 3:                  /*add both */
2572         if (StringCmp (FindFeatStrFromFeatDefType (RFIP->featdeftype), RFIP->ContentLabel) != 0) {
2573           sprintf (tempStringBuffer, "%s: %s", FindFeatStrFromFeatDefType (RFIP->featdeftype), RFIP->ContentLabel);
2574           label = tempStringBuffer;
2575         } else {
2576           label = RFIP->ContentLabel;
2577         }
2578         break;
2579       default:
2580         return;
2581     }
2582   }
2583   if (! StringHasNoText (label)) {
2584     SelectFont (AIP->LabelFont);
2585     textWidthBP = StringWidth (label) * vContext->viewScale;
2586     lineHeight = LineHeight ();
2587     switch (FIP->LabelLoc) {
2588       case LabelInside:
2589         Height = MAX (Height + lineHeight, Height);
2590         featureOffset = lineHeight + 1;
2591         break;
2592       case LabelAbove:
2593         textStartX = (Start + Stop) / 2;
2594         Start = MIN (Start, (signed)(textStartX - textWidthBP / 2));
2595         Stop = MAX (Stop, textStartX + textWidthBP / 2);
2596         featureOffset = lineHeight + 1;
2597         Height += lineHeight + 3;
2598         break;
2599       case LabelAboveClip:
2600         Height += lineHeight + 3;
2601         featureOffset = lineHeight + 1;
2602       case LabelAboveCull:
2603         if (textWidthBP + 2 * vContext->viewScale >= (Stop - Start)) {
2604           Height += lineHeight + 3;
2605           featureOffset = lineHeight + 1;
2606         }
2607       case LabelBelow:
2608         textStartX = (Start + Stop) / 2;
2609         Start = MIN (Start, (signed)(textStartX - textWidthBP / 2));
2610         Stop = MAX (Stop, textStartX + textWidthBP / 2);
2611         Height += lineHeight + 3;
2612         break;
2613       case LabelLeft:
2614         Start -= textWidthBP;
2615         Height = MAX (Height + lineHeight, Height);
2616         break;
2617       case LabelRight:
2618         Stop = RFIP->Right + textWidthBP;
2619         Height = MAX (Height + lineHeight, Height);
2620         break;
2621       default:
2622         return;
2623     }
2624   }
2625   RIP->decorationLeft = Start;
2626   RIP->decorationRight = Stop;
2627   RIP->decorationHeight = Height;
2628   RIP->featureOffset = featureOffset;
2629 }
2630 
2631 
2632 static Boolean BuildRenderInputFromRFIP (
2633   RenderInputPtr target,
2634   RelevantFeatureItemPtr RFIP,
2635   FilterProcessStatePtr FPSP
2636 )
2637 
2638 {
2639   AppearancePtr    AppPtr;
2640   FilterItemPtr    currentFIP;
2641   ViewerContextPtr vContext;
2642   RelevantFeatureItemPtr newRFIP;
2643 
2644   vContext = FPSP->vContext;
2645 
2646   if (target == NULL || RFIP == NULL) return FALSE;
2647   if (!vContext->allFeatures &&
2648       (
2649        (RFIP->Left < vContext->from && RFIP->Right < vContext->from)
2650        || (RFIP->Left > vContext->to && RFIP->Right > vContext->to)
2651        )) {
2652     return FALSE; /* this feature is outside the clipping seqloc */
2653   }
2654   if (! vContext->allFeatures && (
2655       (RFIP->Right >= vContext->from || RFIP->Left <= vContext->to)
2656       || (RFIP->Right < vContext->from && RFIP->Left > vContext->to)
2657       )) {
2658     newRFIP = BuildClippedRFIP (RFIP, FPSP, vContext);
2659     if (newRFIP == NULL) return FALSE;
2660     RFIP = newRFIP;
2661   }
2662   target->RFIP = RFIP;
2663   target->labelSeg = FPSP->labelSegs [RFIP->featdeftype];
2664   target->drawSeg = FPSP->drawSegs [RFIP->featdeftype];
2665   AppPtr = vContext->AppPtr;
2666   currentFIP = FPSP->currentFIP;
2667   target->AIP = AppPtr->FeaturesAppearanceItem [RFIP->featdeftype];
2668   target->FIP = FPSP->currentFIP;
2669   GetFeatureAndDecorationDimensions (target, vContext);
2670   target->rowHeight = MAX (target->Height, target->decorationHeight) + currentFIP->IntraRowPaddingPixels;
2671   return TRUE;
2672 }
2673 
2674 /* If this is a named Seq Annotation, return its name. Otherwise, return NULL */
2675 static CharPtr GetSeqAnnotName(SeqAnnotPtr sap)
2676 {
2677   if (sap != NULL && sap->desc != NULL)
2678   {
2679     AnnotDescrPtr  descPtr;
2680     /* look for a 'title' record */
2681     for (descPtr = sap->desc; descPtr != NULL; descPtr = descPtr->next)
2682     {
2683       if (Annot_descr_title == descPtr->choice) /* title choice */
2684         return descPtr->data.ptrvalue;
2685     }
2686     /* then try for a 'name' record */
2687     for (descPtr = sap->desc; descPtr != NULL; descPtr = descPtr->next)
2688     {
2689       if (Annot_descr_name == descPtr->choice) /* name choice */
2690         return descPtr->data.ptrvalue;
2691     }
2692     /* if an alignment look for a Blast Type or a Hist Seqalign user object */
2693       if(sap->type ==2) /* not an alignment annotation */
2694       {
2695         UserObjectPtr uop;
2696         ObjectIdPtr oip;
2697         UserFieldPtr ufp;
2698  
2699         for (descPtr = sap->desc; descPtr; descPtr = descPtr->next)
2700         {
2701             if (Annot_descr_user == descPtr->choice)
2702             {
2703                 
2704                 for (uop = descPtr->data.ptrvalue; uop; uop = uop->next)
2705                 {
2706                     if (uop->type)
2707                     {
2708                         oip = uop->type;
2709                         
2710                         if (StringCmp(oip->str, "Blast Type") == 0)
2711                         {
2712                             ufp = uop->data;
2713                             if (ufp && ufp->choice == 2)
2714                             {
2715                                 oip = ufp->label;
2716                                 if (oip && oip->str)
2717                                 {
2718                                   return oip->str;
2719                                 }
2720                             }
2721                         }    
2722                     }    
2723                 }
2724             }
2725         }
2726         for (descPtr = sap->desc; descPtr; descPtr = descPtr->next)
2727         {
2728             if (Annot_descr_user == descPtr->choice)
2729             {
2730                 
2731                 for (uop = descPtr->data.ptrvalue; uop; uop = uop->next)
2732                 {
2733                     if (uop->type)
2734                     {
2735                         oip = uop->type;
2736                         
2737                         if (StringCmp(oip->str, "Hist Seqalign") == 0)
2738                         {
2739                             ufp = uop->data;
2740                             if (ufp && ufp->choice == 4 && ufp->data.boolvalue)
2741                             {
2742                                 oip = ufp->label;
2743                                 if (oip && oip->str)
2744                                 {
2745                                     return oip->str;
2746                                 }
2747                             }
2748                         }
2749                     }    
2750                 }
2751             }
2752         }
2753       }
2754   }
2755   return NULL;
2756 }
2757 
2758 static Boolean GetAndCountFeatures (
2759   ViewerContextPtr vContext
2760 )
2761 
2762 {
2763   SeqFeatPtr              sfp;
2764   SeqMgrFeatContext       fContext;
2765   RelevantFeatureItemPtr  rFeats;
2766   ValNodePtr              sapList = NULL, VNP, VNPtail;
2767   SeqAnnotPtr             SAnnP;
2768   Uint2                   i;
2769   Int4                    swap;
2770 
2771   if (vContext == NULL) return FALSE;
2772   vContext->sapCount = 0;
2773   vContext->featureCount = 0;
2774   vContext->featVNP = NULL;
2775   vContext->sapList = NULL;
2776 
2777   /* create list of all named SeqAnnot's in this BioSeq. */
2778   for (SAnnP = vContext->BSP->annot; SAnnP != NULL; SAnnP = SAnnP->next) 
2779   {
2780     if (GetSeqAnnotName(SAnnP)) {
2781       vContext->sapCount++;
2782       ValNodeAddPointer (&sapList, 0, SAnnP);
2783     }
2784   }
2785   if (vContext->sapCount > 0) {
2786     vContext->sapList = MemNew (vContext->sapCount * sizeof (SeqAnnotPtr));
2787     if (vContext->sapList == NULL) {
2788       ValNodeFree (sapList);
2789       return FALSE;
2790     }
2791     for (i = 0, VNP = sapList; VNP != NULL && i < vContext->sapCount; VNP = VNP->next, i++) {
2792       vContext->sapList[i] = VNP->data.ptrvalue;
2793     }
2794     ValNodeFree (sapList);
2795   }
2796   
2797   rFeats = MemNew (RELEVANT_FEATS_PER_CHUNK * sizeof (RelevantFeatureItem));
2798   if (rFeats == NULL) return FALSE;
2799   ValNodeAddPointer (&vContext->featVNP, 0, rFeats);
2800   VNPtail = vContext->featVNP;
2801   i = 0;
2802   sfp = SeqMgrGetNextFeature (vContext->BSP, NULL, 0, 0, &fContext);
2803   while (sfp != NULL) {
2804     vContext->featureCount++;
2805 
2806     rFeats [i].Left = fContext.left;
2807     rFeats [i].Right = fContext.right;
2808     rFeats [i].LeftEnd = fContext.partialL ? EndPartial : EndAbsolute;
2809     rFeats [i].RightEnd  = fContext.partialR ? EndPartial : EndAbsolute;
2810     rFeats [i].ContentLabel = fContext.label;
2811     rFeats [i].featdeftype = fContext.featdeftype;
2812     rFeats [i].entityID = fContext.entityID;
2813     rFeats [i].itemID = fContext.itemID;
2814     rFeats [i].itemType = OBJ_SEQFEAT;
2815     rFeats [i].numivals = fContext.numivals;
2816     rFeats [i].ivals = fContext.ivals;
2817     rFeats [i].featstrand = fContext.strand;
2818     rFeats [i].circularSpanningOrigin = FALSE;
2819     if (rFeats [i].Left < 0 && fContext.bsp != NULL) {
2820       /* !!! for features that span origin JK !!! */
2821       rFeats [i].circularSpanningOrigin = TRUE;
2822       rFeats [i].Left = 0;
2823       rFeats [i].Right = fContext.bsp->length;
2824     }
2825     if (rFeats [i].Right < rFeats [i].Left) {
2826       /* protection against (feature indexing vs. trans-spliced features) */
2827       swap = rFeats [i].Right;
2828       rFeats [i].Right = rFeats [i].Left;
2829       rFeats [i].Left = swap;
2830     }
2831     /* with trans-spliced genes the left and right values might not span all the intervals. */
2832     /* we will fix that */
2833     {
2834       int ivali;
2835       Int4  val;
2836       for (ivali = 0; ivali < rFeats [i].numivals; ++ivali) {
2837         val = rFeats[i].ivals[2*ivali];
2838         rFeats[i].Left  = MIN( rFeats[i].Left,  val );
2839         rFeats[i].Right = MAX( rFeats[i].Right, val );
2840         val = rFeats[i].ivals[2*ivali+1];
2841         rFeats[i].Left  = MIN( rFeats[i].Left,  val );
2842         rFeats[i].Right = MAX( rFeats[i].Right, val );
2843       }
2844     }
2845     if (GetSeqAnnotName(fContext.sap)) { /* save this feature's Annot Ptr if it is a named Seq Annot. */
2846       rFeats [i].sap = fContext.sap;
2847     }
2848     else {
2849       rFeats [i].sap = NULL;
2850     }
2851     i++;
2852     if (i >= RELEVANT_FEATS_PER_CHUNK) {
2853       i = 0;
2854       rFeats = MemNew (RELEVANT_FEATS_PER_CHUNK * sizeof (RelevantFeatureItem));
2855       VNPtail = ValNodeNew (VNPtail);
2856       if (rFeats == NULL || VNPtail == NULL) {
2857         ValNodeFreeData (vContext->featVNP);
2858         MemFree (rFeats);
2859         return FALSE;
2860       }
2861       VNPtail->data.ptrvalue = rFeats;
2862     }
2863 
2864     sfp = SeqMgrGetNextFeature (vContext->BSP, sfp, 0, 0, &fContext);
2865   }
2866   if (vContext->featureCount == 0) {
2867     MemFree (rFeats);
2868     MemFree (vContext->featVNP);
2869     vContext->featVNP = NULL;
2870   }
2871   return TRUE;
2872 }
2873 
2874 NLM_EXTERN RelevantFeaturesPtr CollectFeatures (
2875   BioseqPtr bsp
2876 )
2877 
2878 {
2879   RelevantFeaturesPtr  RFP;
2880   ViewerContext        VC;
2881 
2882   RFP = MemNew (sizeof (RelevantFeatures));
2883   if (RFP == NULL) return NULL;
2884   VC.BSP = bsp;
2885   if (! GetAndCountFeatures (&VC)) return NULL;
2886   RFP->featureCount = VC.featureCount;
2887   RFP->featVNP = VC.featVNP;
2888   RFP->sapCount = VC.sapCount;
2889   RFP->sapList = VC.sapList;
2890   return RFP;
2891 }
2892 
2893 static Boolean EnsureFeatureHasSegment (
2894   FilterProcessStatePtr FPSP,
2895   Uint1 featdeftype,
2896   SegmenT parentSegment
2897 )
2898 
2899 {
2900   AppearancePtr     AppPtr;
2901   AppearanceItemPtr AIP;
2902   ViewerContextPtr vContext;
2903 
2904   vContext = FPSP->vContext;
2905 
2906 
2907   if (parentSegment == NULL) {
2908     parentSegment = vContext->drawOnMe;
2909   }
2910 
2911   AppPtr = vContext->AppPtr;
2912   if (FPSP->drawSegs [featdeftype] == NULL) {
2913     FPSP->drawSegs [featdeftype] = CreateSegment (parentSegment, 0, 0);
2914     FPSP->labelSegs [featdeftype] = CreateSegment (parentSegment, 0, 0);
2915     /* cleaup needed if program is supposed to recover from this !!! */
2916     if (FPSP->drawSegs [featdeftype] == NULL || FPSP->labelSegs [featdeftype] == NULL) return FALSE;
2917     AIP = AppPtr->FeaturesAppearanceItem [featdeftype];
2918     AddAttribute (FPSP->drawSegs [featdeftype],
2919                   COLOR_ATT | SHADING_ATT | STYLE_ATT | WIDTH_ATT,
2920                   AIP->Color, AIP->VibLinestyle, AIP->VibShading, 1, 0);
2921     AddAttribute (FPSP->labelSegs [featdeftype], COLOR_ATT, AIP->LabelColor, 0, 0, 0, 0);
2922   }
2923   return TRUE;
2924 }
2925 
2926 /*
2927   return a string describing the sequences in this alignment,
2928   concatenating strings from all the seqid's of the sequences in this alignment 
2929   except for the one in 'notthisRow' which ordinarly will be the bioseq.
2930 */
2931 
2932 static Boolean SeqAlignContentLabel(SeqAlignPtr sap, SeqIdPtr notThisSID, CharPtr buf, Int4 buflen, Uint1 format)
2933 {
2934   Int4      r, rows, slen;
2935   Char      localbuf[100];
2936   SeqIdPtr  sid;
2937   Char      rowDelim[] = ",";
2938   Int4      rowDelimLen = sizeof(rowDelim) - 1;
2939   Boolean   firstTime = TRUE;
2940   
2941   if (sap == NULL || buf == NULL) return FALSE;
2942   
2943   rows = AlnMgr2GetNumRows(sap);
2944   if (rows < 1)
2945     rows = sap->dim;
2946   if (rows < 1)
2947     return FALSE;
2948  
2949   for (r = 1; r <= rows; ++r)
2950   {
2951     sid = AlnMgr2GetNthSeqIdPtr(sap, r);
2952     if (sid == NULL) {
2953      sid = AlnMgr2GetNthSeqIdPtrStdSeg(sap, r);
2954     }
2955     if (sid == NULL) 
2956       continue;
2957     if (SeqIdIn(sid, notThisSID))
2958       continue;
2959 
2960     SeqIdWrite (sid, localbuf, format, sizeof (localbuf) - 1);
2961     slen = StringLen(localbuf);
2962     if (slen < buflen - rowDelimLen - 1)
2963     {
2964       if (!firstTime) {
2965         StringNCat(buf, rowDelim, buflen);
2966         buflen -= sizeof(rowDelim) - 1;
2967       }
2968       firstTime = FALSE;
2969       StringNCat(buf, localbuf, buflen);
2970       buflen -= slen + 1;
2971      }
2972   }
2973   
2974   if (StringLen(buf) <= 0)
2975     return FALSE;
2976   return TRUE;
2977 }
2978 
2979 static void AccumIvals(Int1* accumulator, Int4 accumBegin, Int4 accumLen, RelevantFeatureItemPtr RFIP);
2980 Int4  CountIvals(Int1* accumulator, Int4 accumLen);
2981 void  MakeIvals(Int1* accumulator, Int4 accumBegin, Int4 accumLen, Int4Ptr ivalsOut, Int4 ivalsLen);
2982 
2983 static RelevantFeatureItemPtr GetNextRFIPinAlignmentFilter (
2984   FilterProcessStatePtr FPSP
2985 )
2986 
2987 {
2988     AlignmentFilterStatePtr alignSP;
2989     SeqAlignSortInfoPtr     alignSorted;
2990     SeqAlignPtr             SAlnP;
2991     Int4                    start, stop;
2992     Uint1                   segType;
2993     Int4                    nsegs, i;
2994     SeqIdPtr                SID;
2995     BioseqPtr               BSP;
2996     RelevantFeatureItem     RFI;       /* holder for intermediate values we will merge together */
2997     RelevantFeatureItemPtr  finalRFIP;  /* Our return value. */
2998     ViewerContextPtr        vContext;
2999     FilterItemPtr           currentFIP;
3000     AppearanceItemPtr       AIP;
3001     Char                    labelbuf[150];
3002     Uint1                   strand;
3003     AlignSegIterator        asi;
3004     ValNodePtr              vnp;
3005     
3006     if (FPSP == NULL) return NULL;
3007     vContext = FPSP->vContext;
3008     currentFIP = FPSP->currentFIP;
3009     if (vContext == NULL || currentFIP == NULL) return NULL;
3010 
3011     BSP = vContext->BSP;
3012     SID = BSP->id;
3013     AIP = vContext->AppPtr->FeaturesAppearanceItem[APPEARANCEITEM_Alignment];
3014 
3015     
3016     alignSP = &FPSP->state.align;
3017     alignSorted = alignSP->alignSorted;
3018 
3019     SAlnP = alignSorted[alignSP->alignIndex].sap;
3020 
3021     /* create new RFIP based on this SeqAlignPtr */
3022     finalRFIP = MemNew (sizeof (RelevantFeatureItem));
3023     if (finalRFIP == NULL) {
3024         MemFree (finalRFIP);
3025         return NULL;
3026     }
3027     vnp = ValNodeAddPointer (&FPSP->lastInFreeList, 0, finalRFIP);
3028     if (vnp == NULL) {
3029         MemFree (finalRFIP);
3030         return NULL;
3031     }
3032     if (FPSP->needFreeList == NULL) {
3033       FPSP->needFreeList = FPSP->lastInFreeList;
3034     }
3035     FPSP->lastInFreeList = vnp;
3036 
3037    /* where does this alignment start & stop in bioseq coords? */
3038     nsegs = AlignSegIteratorCreate(SAlnP, SID, &asi);
3039     strand = asi.strand;
3040     if (strand == Seq_strand_unknown)
3041         strand = Seq_strand_plus;
3042 
3043     if (asi.start < 0 || asi.stop < 0) 
3044       return NULL;
3045     finalRFIP->Left = asi.start;
3046     finalRFIP->Right = asi.stop;
3047     finalRFIP->featstrand = strand;
3048     finalRFIP->numivals = 1;
3049     finalRFIP->ivals = NULL;
3050     finalRFIP->featdeftype = APPEARANCEITEM_Alignment;
3051     finalRFIP->circularSpanningOrigin = FALSE;
3052     finalRFIP->entityID = SAlnP->idx.entityID;
3053     finalRFIP->itemID = SAlnP->idx.itemID;
3054     finalRFIP->itemType = SAlnP->idx.itemtype;
3055     labelbuf[0] = 0;
3056     if (SeqAlignContentLabel(SAlnP, SID, labelbuf, sizeof(labelbuf) - 1, AIP->format))
3057     {
3058         finalRFIP->ContentLabel = StringSave(labelbuf);
3059     }
3060 
3061     if (nsegs > 1) {
3062         finalRFIP->ivals = MemNew (2 * nsegs * sizeof (Int4));
3063         if (finalRFIP->ivals == NULL) 
3064         {
3065             MemFree(finalRFIP->ContentLabel);
3066             return NULL;
3067         } else {
3068             finalRFIP->numivals = 0;
3069             i = 0;
3070             while (AlignSegIteratorNext(&asi, &start, &stop, NULL, &segType))
3071             {
3072                 /* ignore gaps on the bioseq */
3073                 /* hence we may have less than nsegs ivals */
3074                 if (segType == AM_GAP) 
3075                     continue;
3076                 
3077                 if (segType == AM_INSERT) {
3078                   if (i == 0)
3079                     start = stop = finalRFIP->Left;
3080                   else
3081                     start = stop = finalRFIP->ivals[i - 1];
3082                 }
3083                 finalRFIP->ivals[i++] = start;
3084                 finalRFIP->ivals[i++] = stop;
3085                 finalRFIP->numivals++;
3086             }
3087         }
3088     }
3089                 
3090     ++alignSP->alignIndex;
3091     /*
3092         if we are not done with all the alignments and
3093         the next alignment(s) has the same accession and strand as this one does
3094         merge their segments together.
3095     */
3096     if (! AlignmentFilterStateDone(alignSP) && 
3097         alignSorted[alignSP->alignIndex - 1].strand ==  alignSorted[alignSP->alignIndex].strand &&
3098         StrNCmp(alignSorted[alignSP->alignIndex - 1].label, 
3099                 alignSorted[alignSP->alignIndex].label, MAX_ALIGN_SORT_LABEL) == 0) {
3100         Int4                    *newIvals;  /* pointer to merged ivals */
3101         Int4                    newIvalsNum;
3102         Int1                    *accumulator;
3103         Int4                    accumBegin, accumEnd, accumLen;
3104         
3105         /* keep track of where the segments fall in this array */
3106         /* 0 - a gap. 1 - a segment. 2 - an insert */
3107         accumBegin = finalRFIP->Left; 
3108         accumEnd   = vContext->seqLength;
3109         accumLen = accumEnd - accumBegin;
3110         accumulator = MemNew( accumLen * sizeof(Int1) );
3111         if (accumulator == NULL) {
3112             return NULL;
3113         }
3114         
3115         AccumIvals(accumulator, accumBegin, accumLen, finalRFIP);
3116         for ( ;
3117               ! AlignmentFilterStateDone(alignSP) && 
3118               alignSorted[alignSP->alignIndex - 1].strand ==  alignSorted[alignSP->alignIndex].strand &&
3119               StrNCmp(alignSorted[alignSP->alignIndex - 1].label, 
3120                       alignSorted[alignSP->alignIndex].label, MAX_ALIGN_SORT_LABEL) == 0;
3121               ++alignSP->alignIndex ) {
3122         
3123             SAlnP = alignSorted[alignSP->alignIndex].sap;
3124 
3125           /* where does this alignment start & stop in bioseq coords? */
3126             nsegs = AlignSegIteratorCreate(SAlnP, SID, &asi);
3127 
3128             if (asi.start < 0 || asi.stop < 0) 
3129                 continue;
3130             RFI.Left = asi.start;
3131             finalRFIP->Left = MIN(finalRFIP->Left, RFI.Left);
3132             RFI.Right = asi.stop;
3133             finalRFIP->Right = MAX(finalRFIP->Right, RFI.Right);
3134             RFI.numivals = 1;
3135             RFI.ivals =  NULL;
3136             
3137 #ifdef _DEBUG
3138             if (finalRFIP->Right > vContext->seqLength) { 
3139                 printf("finalRFIP->Right too big: %d\n", finalRFIP->Right); /* put a breakpoint here! */
3140                 return NULL;
3141             }
3142 #endif            
3143 
3144             if (nsegs > 1) {
3145                 RFI.ivals = MemNew (2 * nsegs * sizeof (Int4));
3146                 if (RFI.ivals == NULL) 
3147                 {
3148                     MemFree(RFI.ivals);
3149                 } else {
3150                     RFI.numivals = 0;
3151                     i = 0;
3152                     while (AlignSegIteratorNext(&asi, &start, &stop, NULL, &segType))
3153                     {
3154                         /* ignore gaps on the bioseq */
3155                         /* hence we may have less than nsegs ivals */
3156                         if (segType == AM_GAP) 
3157                             continue;
3158                         
3159                         if (segType == AM_INSERT) {
3160                           if (i == 0)
3161                             start = stop = RFI.Left;
3162                           else
3163                             start = stop = RFI.ivals[i - 1];
3164                         }
3165                         RFI.ivals[i++] = start;
3166                         RFI.ivals[i++] = stop;
3167                         RFI.numivals++;
3168                     }
3169                 }
3170             }
3171             AccumIvals(accumulator, accumBegin, accumLen, &RFI);
3172             if (RFI.ivals != NULL)
3173                 MemFree(RFI.ivals);
3174         }
3175         newIvalsNum = CountIvals(accumulator, accumLen);
3176         newIvals = MemNew( 2 * newIvalsNum * sizeof(Int4) );
3177         if (newIvals == NULL) {
3178             MemFree(accumulator);
3179             return NULL;
3180         }
3181         MakeIvals(accumulator, accumBegin, accumLen, newIvals, newIvalsNum);
3182         MemFree(accumulator);
3183         
3184         if (finalRFIP->ivals != NULL)
3185             MemFree(finalRFIP->ivals);
3186         finalRFIP->numivals = newIvalsNum;
3187             
3188         if (newIvalsNum > 1) {
3189             finalRFIP->ivals = newIvals;
3190         } else {
3191             MemFree(newIvals);
3192             finalRFIP->ivals = NULL;
3193         }
3194     }
3195 
3196     /* remember to delete the final ivals array & label space */
3197     if (finalRFIP->numivals > 1)
3198     {
3199         vnp = ValNodeAddPointer(&FPSP->lastInFreeList, 0, finalRFIP->ivals);
3200         if (vnp == NULL) {
3201             MemFree(finalRFIP->ContentLabel);
3202             MemFree(finalRFIP->ivals);
3203             return NULL;
3204         }
3205         if (FPSP->needFreeList == NULL) {
3206           FPSP->needFreeList = FPSP->lastInFreeList;
3207         }
3208         FPSP->lastInFreeList = vnp;
3209     }
3210     vnp = ValNodeAddPointer(&FPSP->lastInFreeList, 0, finalRFIP->ContentLabel);
3211     if (vnp == NULL)
3212     {
3213         MemFree(finalRFIP->ContentLabel);
3214         return NULL;
3215     }
3216     if (FPSP->needFreeList == NULL) {
3217       FPSP->needFreeList = FPSP->lastInFreeList;
3218     }
3219     FPSP->lastInFreeList = vnp;
3220             
3221     return finalRFIP;
3222 }
3223 
3224 
3225 void AccumIvals(Int1* accumulator, Int4 accumBegin, Int4 accumLen, RelevantFeatureItemPtr RFIP)
3226 {
3227     Int4    i, ai;
3228     Int4    blockstart, blockend;
3229     
3230     /* mark everywhere segments spans. 0 in gaps, 1 in blocks, 2 at inserts */
3231     if (RFIP->numivals == 1) {
3232         blockstart = RFIP->Left  - accumBegin;
3233         blockend   = RFIP->Right - accumBegin;
3234     
3235         for (ai = blockstart; ai <= blockend; ++ai)
3236             if (accumulator[ai] == 0)
3237                 accumulator[ai] = 1;
3238     }
3239     else {
3240         for (i = 0; i < RFIP->numivals; ++i) {
3241             blockstart = RFIP->ivals[2*i] - accumBegin;
3242             blockend   = RFIP->ivals[2*i + 1] - accumBegin;
3243             
3244 #ifdef _DEBUG
3245             if (blockstart < 0 || accumLen < blockstart || blockend < 0 || accumLen < blockend) {
3246                 printf("AccumIvals problem!"); /* put a breakpoint here! */
3247                 return;
3248             }
3249 #endif                
3250             if (blockstart == blockend) {
3251                 accumulator[blockstart] = 2;
3252             } else {
3253                 for (ai = blockstart; ai <= blockend; ++ai)
3254                     if (accumulator[ai] == 0)
3255                         accumulator[ai] = 1;
3256             }
3257         }
3258     }
3259 }
3260 
3261 Int4  CountIvals(Int1* accumulator, Int4 accumLen)
3262 {
3263     Int4    numivalsOut;
3264     Int4    ai;
3265     
3266     /* make new ivals */
3267     numivalsOut = 0;
3268     
3269     for (ai = 0; ai < accumLen; ++ai) {
3270         if (accumulator[ai] == 0)
3271             continue;
3272     /* if it is an insert, add it */
3273         if (accumulator[ai] == 2) {
3274             ++numivalsOut;
3275         } else if (accumulator[ai] == 1) { /* beginning of a block */
3276             /* find end of block */
3277             for (; ai < accumLen; ++ai) {
3278                 if (accumulator[ai] != 1)
3279                     break;
3280             }
3281             --ai;
3282             ++numivalsOut;
3283         }
3284     }
3285     return numivalsOut;
3286 }
3287 
3288 void  MakeIvals(Int1* accumulator, Int4 accumBegin, Int4 accumLen, Int4Ptr ivalsOut, Int4 ivalsLen)
3289 {
3290     Int4    numivalsOut;
3291     Int4    ai, blocklen;
3292     
3293     /* make new ivals */
3294     numivalsOut = 0;
3295     
3296     for (ai = 0; ai < accumLen  &&  numivalsOut < ivalsLen; ++ai) {
3297     /* ignore spots in gaps. */
3298         if (accumulator[ai] == 0)
3299             continue;
3300     /* if it is an insert, add it */
3301         if (accumulator[ai] == 2) {
3302             ivalsOut[numivalsOut * 2]     = accumBegin + ai;
3303             ivalsOut[numivalsOut * 2 + 1] = accumBegin + ai;
3304             ++numivalsOut;
3305         } else if (accumulator[ai] == 1) { /* beginning of a block */
3306             /* find end of block */
3307             for (blocklen = 0; ai + blocklen < accumLen; ++blocklen) {
3308                 if (accumulator[ai + blocklen] != 1)
3309                     break;
3310             }
3311             ivalsOut[numivalsOut * 2]     = accumBegin + ai;
3312             if (accumulator[ai + blocklen] == 2)
3313                 ivalsOut[numivalsOut * 2 + 1] = accumBegin + ai + blocklen;
3314             else
3315                 ivalsOut[numivalsOut * 2 + 1] = accumBegin + ai + blocklen - 1;
3316             ++numivalsOut;
3317             ai += blocklen - 1;
3318         }
3319     }
3320 }
3321 
3322 /*
3323   For a given SeqAlign and a given SeqId for a Seq that participates in that SeqAlign,
3324   keep track of where we are in that alignment so we can iterate through all of that 
3325   alignment's segments, returning information about each segment.
3326   Hides the differences between normal/indexable and StdSeg alignments.
3327 */
3328 
3329 /* 
3330   Create an structure to iterate through all of an alignment's segments.
3331   return a pointer to that iterator struct that must be deallocated.
3332    return the number of segments.
3333 */
3334 static 
3335 Int4 AlignSegIteratorCreate(SeqAlignPtr sap, SeqIdPtr sip,  AlignSegIteratorPtr asip)
3336 {
3337   Boolean             stdSeg;
3338   Int4                swap;
3339   
3340   if (sap == NULL || sip == NULL  || asip == NULL)
3341     return 0;
3342       
3343   AlnMgr2IndexSingleChildSeqAlign(sap);
3344   asip->sap = sap;
3345   asip->sip = sip;
3346   
3347   stdSeg = (sap->segtype == SAS_STD);
3348   if ( ! stdSeg) {
3349     asip->nsegs = AlnMgr2GetNumSegs(sap);
3350     asip->alignRow = AlnMgr2GetFirstNForSipList (sap, sip);
3351     if (asip->alignRow == -1) {
3352       return 0;
3353     }
3354     AlnMgr2GetNthSeqRangeInSA(sap, asip->alignRow, &asip->start, &asip->stop);
3355     asip->strand = AlnMgr2GetNthStrand(sap, asip->alignRow);
3356   }
3357   else {
3358     asip->nsegs = AlnMgr2GetNumStdSegs(sap);
3359     asip->alignRow = 0;
3360     AlnMgr2GetSeqRangeForSipInSAStdSeg(sap, sip, &asip->start, &asip->stop, &asip->strand);
3361   }
3362 
3363   if (asip->stop < asip->start) {
3364     swap = asip->stop;
3365     asip->stop = asip->start;
3366     asip->start = swap;
3367   }
3368   
3369   asip->currentStdSeg = NULL;
3370   asip->currentSeg = 0;
3371   
3372   return asip->nsegs;
3373 }
3374 
3375 /*
3376   Given an AlignSegIterator return the information about the current segment
3377   in the pointer arguments, and advance the iterator to the next segment.
3378   Return false if there are no more segments in which case the output arguments
3379   will not be changed.
3380 */
3381 static 
3382 Boolean AlignSegIteratorNext(
3383   AlignSegIteratorPtr asip, 
3384   Int4Ptr       startOut, 
3385   Int4Ptr       stopOut, 
3386   Uint1Ptr      strandOut,
3387   Uint1Ptr      segTypeOut
3388 )
3389 {
3390   Int4    start, stop, swap;
3391   Uint1   strand, segType;
3392   Boolean stdSeg;
3393   Int4    iseg;
3394   
3395   
3396   if (asip == NULL) 
3397     return FALSE; /* bad argument */
3398 
3399   /* iterate */
3400   asip->currentSeg++;  
3401   if (asip->currentSeg > asip->nsegs) /* no more segments */
3402     return FALSE;
3403     
3404   if (asip->strand == Seq_strand_minus)
3405       iseg = asip->nsegs - asip->currentSeg + 1;
3406   else
3407       iseg = asip->currentSeg;
3408   
3409   stdSeg = (asip->sap->segtype == SAS_STD);
3410   if ( ! stdSeg) {
3411     Int4    alignStart, alignStop;
3412     Int4    row2, seq2Start;
3413  
3414     strand = asip->strand; /* all segments have the same strand */
3415     /* get segment location in alignment coordinates */
3416     AlnMgr2GetNthSegmentRange(asip->sap, iseg, &alignStart, &alignStop);
3417     /* convert to bioseq coordinates */
3418     start = AlnMgr2MapSeqAlignToBioseq(asip->sap, alignStart, asip->alignRow);
3419     if (alignStart != alignStop)
3420         stop  = AlnMgr2MapSeqAlignToBioseq(asip->sap, alignStop , asip->alignRow);
3421     else
3422         stop = start;
3423 
3424     /* Is this a insert (a gap on the bioseq)? */
3425     if (start == -2) {
3426         segType = AM_INSERT;
3427     } else {
3428       /* check the other sequence to see if there is a gap there. */
3429       if (asip->alignRow == 1)
3430         row2 = 2;
3431       else 
3432         row2 = 1;
3433       seq2Start = AlnMgr2MapSeqAlignToBioseq(asip->sap, alignStart, row2);
3434 
3435       if (seq2Start == -2 ) 
3436         segType = AM_GAP;
3437        else
3438         segType = AM_SEQ;
3439     }
3440   }
3441   else { /* stdSeg */   
3442     asip->currentStdSeg = AlnMgr2GetNthStdSeg(asip->sap, iseg);
3443     if (asip->currentStdSeg == NULL)
3444         return FALSE;    /* Logic Error. should not happen. */
3445         
3446     AlnMgr2GetSeqRangeForSipInStdSeg(asip->currentStdSeg, asip->sip, &start, &stop, &strand, &segType);
3447   }
3448   if (stop < start) {
3449     swap = start;
3450     start = stop;
3451     stop = swap;
3452   }
3453   
3454   /* take care of output arguments */
3455   if (startOut)   *startOut   = start;
3456   if (stopOut)    *stopOut    = stop;
3457   if (strandOut)  *strandOut  = strand;
3458   if (segTypeOut) *segTypeOut = segType;
3459   
3460   return TRUE;
3461 }
3462 
3463  
3464 static RelevantFeatureItemPtr GetNextRFIPinFeatureFilter (
3465   FilterProcessStatePtr FPSP
3466 )
3467 
3468 {
3469   ValNodePtr              currentRFIPblockVNP;
3470   FeatureFilterStatePtr   featSP;
3471   RelevantFeatureItemPtr  RFIP = NULL;
3472   ViewerContextPtr        vContext;
3473   FilterItemPtr           currentFIP;
3474   FilterPtr               FP;
3475 
3476   if (FPSP == NULL) return NULL;
3477   vContext = FPSP->vContext;
3478   currentFIP = FPSP->currentFIP;
3479   if (vContext == NULL || currentFIP == NULL) return NULL;
3480   FP = vContext->FltPtr;
3481   if (FP == NULL) return NULL;
3482   
3483   featSP = &FPSP->state.feat;
3484   for (; featSP->featureBlockOffset + featSP->indexInBlock < vContext->featureCount; featSP->indexInBlock++) {
3485     if (featSP->indexInBlock >= RELEVANT_FEATS_PER_CHUNK) {
3486       featSP->indexInBlock = 0;
3487       featSP->featureBlockOffset += RELEVANT_FEATS_PER_CHUNK;
3488       featSP->currentRFIPblockVNP = featSP->currentRFIPblockVNP->next;
3489       if (featSP->currentRFIPblockVNP == NULL) return NULL;
3490     }
3491     currentRFIPblockVNP = featSP->currentRFIPblockVNP;
3492     RFIP = (RelevantFeatureItemPtr) (currentRFIPblockVNP->data.ptrvalue) + featSP->indexInBlock;
3493     
3494     if (FP->GroupByAnnot && RFIP->sap != vContext->currentSAP) continue;
3495     
3496     if (! vContext->allFeatures
3497         && (RFIP->Right < vContext->from || RFIP->Left > vContext->to)) {
3498       continue;
3499     }
3500     if (FPSP->featuresProcessed [featSP->featureBlockOffset + featSP->indexInBlock]
3501         || (! currentFIP->IncludeFeature [RFIP->featdeftype])) continue;
3502     if (currentFIP->MatchStrand != BothStrands) {
3503       if (currentFIP->MatchStrand == MinusStrand && RFIP->featstrand != Seq_strand_minus) continue;
3504       if (currentFIP->MatchStrand == PlusStrand  && RFIP->featstrand != Seq_strand_plus) continue;
3505     }
3506     FPSP->featuresProcessed [featSP->featureBlockOffset + featSP->indexInBlock] = TRUE;
3507     break;
3508   }
3509   if (featSP->featureBlockOffset + featSP->indexInBlock >= vContext->featureCount) return NULL;
3510   return RFIP;
3511 }
3512 
3513 
3514 
3515 static RelevantFeatureItemPtr GetNextRFIPinFilterItem (
3516   FilterProcessStatePtr FPSP
3517 )
3518 
3519 {
3520   AlignmentFilterStatePtr alignSP;
3521   RelevantFeatureItemPtr  RFIP = NULL;
3522   ViewerContextPtr        vContext;
3523   FilterItemPtr           currentFIP;
3524 
3525   /* called as an iterator by the rendering functions -- builds & returns the next feature (in an RFIP), or returns NULL if no more left in this Filter */
3526   if (FPSP == NULL) return NULL;
3527   vContext = FPSP->vContext;
3528   currentFIP = FPSP->currentFIP;
3529   if (vContext == NULL || currentFIP == NULL) return NULL;
3530   switch (currentFIP->Type) {
3531   case InvalidFilter:
3532   case GraphFilter:
3533   case BioseqFilter:
3534     return NULL;
3535   case AlignmentFilter:
3536     alignSP = &FPSP->state.align;
3537     while (!AlignmentFilterStateDone(alignSP)) {
3538       RFIP = GetNextRFIPinAlignmentFilter (FPSP);
3539       if (RFIP != NULL) 
3540         return RFIP;
3541     } 
3542     /* note: if control reaches here, then RFIP == NULL */
3543     break;
3544   case FeatureFilter:
3545     RFIP = GetNextRFIPinFeatureFilter (FPSP);
3546     break;
3547   }
3548   if (RFIP != NULL) {
3549     FPSP->featuresProcessedCount++;
3550   }
3551   return RFIP;
3552 }
3553 
3554 static Boolean AddFeatureToRow (
3555   InternalRowPtr row,
3556   RelevantFeatureItemPtr RFIP,
3557   Boolean SkipSmearTest,
3558   FilterProcessStatePtr FPSP
3559 )
3560 
3561 {
3562   ValNodePtr             VNP;
3563   RelevantFeatureItemPtr newRFIP; /* for representing a multi-feature smear */
3564   RelevantFeatureItemPtr oldRFIP;
3565   ViewerContextPtr       vContext;
3566 
3567   vContext = FPSP->vContext;
3568 
3569   if (row == NULL || RFIP == NULL) return FALSE;
3570 
3571   if (!SkipSmearTest && row->rowFeatureCount &&
3572       TestForSmear (RFIP, (RelevantFeatureItemPtr) row->feats->data.ptrvalue, vContext->viewScale)) {
3573     /* if the last feature was not a smear-in-progress, allocate newRFIP, else re-use the current one */
3574     oldRFIP = (RelevantFeatureItemPtr) row->feats->data.ptrvalue;
3575     if (oldRFIP->entityID == 0 && oldRFIP->itemID == 0) {
3576       /* oldRFIP is already a smear-in-prorgess, just extend it */
3577       newRFIP = oldRFIP;
3578     } else {
3579       /* need to create a new smear */
3580       newRFIP = MemNew (sizeof (RelevantFeatureItem));
3581       VNP = MemNew (sizeof (ValNode));
3582       if (newRFIP == NULL || VNP == NULL) return FALSE;
3583       VNP->data.ptrvalue = newRFIP;
3584       VNP->next = FPSP->needFreeList;
3585       FPSP->needFreeList = VNP;
3586       row->feats->data.ptrvalue = newRFIP;
3587       newRFIP->featdeftype = oldRFIP->featdeftype;
3588       newRFIP->numivals = 1;
3589     }
3590 
3591     newRFIP->Left = MIN (oldRFIP->Left, RFIP->Left);
3592     newRFIP->Right = MAX (oldRFIP->Right, RFIP->Right);
3593   } else {
3594 
3595     VNP = MemNew (sizeof (ValNode));
3596     if (VNP == NULL) return FALSE;
3597     VNP->data.ptrvalue = RFIP;
3598     VNP->next = row->feats;
3599     row->feats = VNP;
3600     row->rowFeatureCount++;
3601   }
3602   return TRUE;
3603 }
3604 
3605 static InternalRowPtr AddARow (
3606   InternalRowPtr sourceRow
3607 )
3608 
3609 {
3610   InternalRowPtr  IRP;
3611 
3612   if (sourceRow == NULL) return NULL;
3613   IRP = MemNew (sizeof (InternalRow));
3614   if (IRP == NULL) return NULL;
3615   sourceRow->next = IRP;
3616   return IRP;
3617 }
3618 
3619 static Uint2 SimpleDiagonalLayout (
3620   InternalRowPtr firstRow,
3621   FilterProcessStatePtr FPSP
3622 )
3623 
3624 {
3625   Uint2                   rows = 0;
3626   InternalRowPtr          thisRow;
3627   RelevantFeatureItemPtr  RFIP;
3628 
3629   thisRow = firstRow;
3630   if (thisRow == NULL) return 0;
3631   thisRow->rowFeatureCount = 0;
3632   while ((RFIP = GetNextRFIPinFilterItem (FPSP)) != NULL) {
3633     AddFeatureToRow (thisRow, RFIP, TRUE, FPSP);
3634     thisRow = AddARow (thisRow);
3635     rows++;
3636   }
3637   return rows;
3638 }
3639 
3640 /* this was copied from seqmgr.c (6.181, 27-feb-2002) */
3641 static Boolean CheckInternalExonBoundaries (Int2 numivalsCDS, Int4Ptr ivalsCDS, Int2 numivalsMRNA, Int4Ptr ivalsMRNA)
3642 
3643 {
3644   Int2  i;
3645   Int2  j;
3646 
3647   if (numivalsCDS > numivalsMRNA) return FALSE;
3648   if (ivalsCDS == NULL || ivalsMRNA == NULL) return TRUE;
3649 
3650   /* scan first exon-intron boundary against candidate start positions */
3651 
3652   for (i = 0; i <= numivalsMRNA - numivalsCDS; i++) {
3653     if (ivalsCDS [1] == ivalsMRNA [2 * i + 1]) break;
3654   }
3655   if (i > numivalsMRNA - numivalsCDS) return FALSE;
3656 
3657   /* Addition by Eric: the first interval in the CDS must not be larger than the corresponding interval in the mRNA */
3658   if (ABS (ivalsCDS [0] - ivalsCDS [1]) > ABS (ivalsMRNA [2 * i] - ivalsMRNA [2 * i + 1])) return FALSE;
3659 
3660   /* scan subsequent exon-intron and intron-exon boundaries */
3661 
3662   for (j = 2; j <= 2 * numivalsCDS - 2; j++) {
3663     if (ivalsCDS [j] != ivalsMRNA [2 * i + j]) return FALSE;
3664   }
3665 
3666   /* Addition by Eric: the last interval in the CDS must not be larger than the corresponding interval in the mRNA */
3667   if (ABS (ivalsCDS [j - 1] - ivalsCDS [j]) > ABS (ivalsMRNA [2 * i + j - 1] - ivalsMRNA [2 * i + j])) return FALSE;
3668 
3669   return TRUE;
3670 }
3671 
3672 static Boolean mRNAmatchesCDS (
3673   RelevantFeatureItemPtr mRNA,
3674   RelevantFeatureItemPtr CDS
3675 )
3676 
3677 {
3678   /* the mRNA must be the larger feature */
3679   if (CDS->Left < mRNA->Left || CDS->Right > mRNA->Right) return FALSE;
3680   /* check strands (anything not equal to strand minus is equal to each other) */
3681   if (CDS->featstrand != mRNA->featstrand && 
3682       (CDS->featstrand == Seq_strand_minus || mRNA->featstrand == Seq_strand_minus)) 
3683     return FALSE;
3684   /* trivial case */
3685   if (CDS->numivals == 1 && mRNA->numivals == 1) return TRUE;
3686   /* . . . and the intervals must line up */
3687   return CheckInternalExonBoundaries (CDS->numivals, CDS->ivals, mRNA->numivals, mRNA->ivals);
3688 }
3689 
3690 typedef struct rFIPentry {
3691   RelevantFeatureItemPtr RFIP;
3692   Int4                   decorationLeft;
3693   Int4                   decorationRight;
3694   Int4                   Right;
3695   Int4                   Left;
3696   Boolean                used;
3697 } RFIPentry, PNTR RFIPentryPtr;
3698 
3699 typedef struct rFIPgroup {
3700   Uint4       memberCount;
3701   Int4        decorationLeft;
3702   Int4        decorationRight;
3703   Int4        Left;
3704   Int4        Right;
3705   ValNodePtr  members; /* data.ptrvalue = RFIPentryPtr */
3706 } RFIPgroup, PNTR RFIPgroupPtr;
3707 
3708 
3709 static Uint2 GeneProductsLayoutInternal (
3710   InternalRowPtr firstRow,
3711   FilterProcessStatePtr FPSP,
3712   Boolean MultiRender
3713 )
3714 
3715 {
3716   Uint2                   rows = 1, i;
3717   RelevantFeatureItemPtr  RFIP, RFIPcds, RFIPmrna, tRFIP;
3718   InternalRowPtr          thisRow, thisRow2, lastRow;
3719   ValNodePtr              fifoHead = NULL, fifoTail = NULL;
3720   ValNodePtr              bumpedListHead = NULL, bumpedListTail = NULL;
3721   ValNodePtr              potCDSvnp, potRNAvnp;
3722   ValNodePtr              GroupsHead = NULL, GroupsTailVNP, MemberTailVNP;
3723   ValNodePtr              groupVNP, featVNP;
3724   RFIPentryPtr            tRFIPentry;
3725   RFIPentryPtr            bumpedListEntry;
3726   RFIPentryPtr            potCDS, potRNA, GroupsTailMemberTail;
3727   Uint2                   bumpedCount;
3728   Boolean                 foundRows, doneCollecting = FALSE;
3729   Int4                    newLeft, featureStart, rowMaxRight;
3730   RenderInput             dummyRI;
3731   RFIPgroupPtr            currentGroup;
3732   Uint4                   viewScale;
3733   
3734   viewScale =  FPSP->vContext->viewScale;
3735 
3736   if (firstRow == NULL) return 0;
3737 
3738   lastRow = firstRow;
3739   firstRow->layoutData.intvalue = -2000000000; /* the first feature will _always_ fit in the first row */
3740 
3741   while (1) {
3742     if (doneCollecting) {
3743       break;
3744     }
3745     RFIP = GetNextRFIPinFilterItem (FPSP);
3746     if (RFIP == NULL) {
3747       doneCollecting = TRUE;
3748       bumpedListHead = fifoHead; /* no more incoming features, bump all features in the queue*/
3749       bumpedListTail = fifoTail;
3750       fifoHead = NULL;
3751     } else {
3752       if (! BuildRenderInputFromRFIP (&dummyRI, RFIP, FPSP)) {
3753         continue; /* either we're out of memory or this feature doesn't overlap the clipping SeqLoc */
3754       }
3755 
3756       if ((tRFIPentry = MemNew (sizeof (RFIPentry))) == NULL) goto bail_out;
3757       if (fifoHead == NULL) {
3758         fifoTail = ValNodeAddPointer (&fifoHead, 0, tRFIPentry);
3759       } else {
3760         fifoTail = ValNodeAddPointer (&fifoTail, 0, tRFIPentry);
3761       }
3762       if (fifoTail == NULL) goto bail_out;
3763       tRFIPentry->RFIP = RFIP;
3764       tRFIPentry->Left = RFIP->Left;
3765       tRFIPentry->Right = RFIP->Right;
3766       tRFIPentry->decorationLeft = dummyRI.decorationLeft;
3767       tRFIPentry->decorationRight = dummyRI.decorationRight;
3768 
3769     /*
3770       now find all features which can not overlap the next feature (in the sequence; decoration overlap doesn't matter)
3771     */
3772       newLeft = RFIP->Left;
3773 
3774       bumpedCount = 0;
3775       bumpedListHead = fifoHead;
3776       for (featVNP = fifoHead;
3777            featVNP != NULL;
3778            featVNP = featVNP->next) {
3779         bumpedListEntry = featVNP->data.ptrvalue;
3780         RFIP = bumpedListEntry->RFIP;
3781         if (RFIP->Right > newLeft) break;
3782         bumpedListTail = featVNP;
3783         fifoHead = featVNP->next;
3784         bumpedCount++;
3785       }
3786       if (bumpedCount == 0) continue;
3787     }
3788     if (bumpedListTail != NULL) {
3789       bumpedListTail->next = NULL;
3790     }
3791     GroupsHead = GroupsTailVNP = NULL;
3792 
3793     for (potCDSvnp = bumpedListHead; potCDSvnp != NULL; potCDSvnp = potCDSvnp->next) {
3794       /* this pass is only searching for CDS features (and correspondins mRNAs) */
3795       potCDS = potCDSvnp->data.ptrvalue;
3796       RFIPcds = potCDS->RFIP;
3797       if (RFIPcds->featdeftype != FEATDEF_CDS) continue;
3798       potCDS->used = TRUE;
3799 
3800       if ((currentGroup = MemNew (sizeof (RFIPgroup))) == NULL) goto bail_out;
3801       if (GroupsHead == NULL) {
3802         GroupsTailVNP = ValNodeAddPointer (&GroupsHead, 0, currentGroup);
3803       } else {
3804         GroupsTailVNP = ValNodeAddPointer (&GroupsTailVNP, 0, currentGroup);
3805       }
3806       if (GroupsTailVNP == NULL) goto bail_out;
3807 
3808       /* do not add the CDS to currentGroup->members yet, b/c we want mRNA features to appear first
3809          (but remember to add it after!) */
3810 
3811       currentGroup->memberCount = 1;
3812       currentGroup->members = NULL;
3813 
3814       currentGroup->Left = potCDS->Left;
3815       currentGroup->Right = potCDS->Right;
3816       currentGroup->decorationLeft = potCDS->decorationLeft;
3817       currentGroup->decorationRight = potCDS->decorationRight;
3818 
3819       for (potRNAvnp = bumpedListHead; potRNAvnp != NULL; potRNAvnp = potRNAvnp->next) {
3820         potRNA = potRNAvnp->data.ptrvalue;
3821         RFIPmrna = potRNA->RFIP;
3822         if (RFIPmrna->featdeftype != FEATDEF_mRNA) continue;
3823         if (!MultiRender && potRNA->used) continue;
3824 
3825         if (mRNAmatchesCDS (RFIPmrna, RFIPcds)) {
3826           potRNA->used = TRUE;
3827 
3828           if ((tRFIPentry = MemNew (sizeof (RFIPentry))) == NULL) goto bail_out;
3829           if (ValNodeAddPointer (&currentGroup->members, 0, tRFIPentry) == NULL) goto bail_out;
3830 
3831           MemCopy (tRFIPentry, potRNA, sizeof (RFIPentry));
3832 
3833           currentGroup->memberCount++;
3834           currentGroup->Left = MIN (currentGroup->Left, RFIPmrna->Left);
3835           currentGroup->Right = MAX (currentGroup->Right, RFIPmrna->Right);
3836           currentGroup->decorationLeft = MIN (currentGroup->decorationLeft, potRNA->decorationLeft);
3837           currentGroup->decorationRight = MAX (currentGroup->decorationRight, potRNA->decorationRight);
3838         }
3839       }
3840 
3841       if ((GroupsTailMemberTail = MemNew (sizeof (RFIPentry))) == NULL) goto bail_out;
3842       if (ValNodeAddPointer (&currentGroup->members, 0, GroupsTailMemberTail) == NULL) goto bail_out;
3843       MemCopy (GroupsTailMemberTail, potCDS, sizeof (RFIPentry));
3844 
3845     }
3846     /*
3847       append all non-matched elements to the Groups list
3848     */
3849     for (featVNP = bumpedListHead; featVNP != NULL; featVNP = featVNP->next) {
3850       tRFIPentry = featVNP->data.ptrvalue;
3851       /* skip feature if it's been matched already */
3852       if (tRFIPentry->used) {
3853         continue;
3854       } else {
3855         /* add another singleton entry to Groups */
3856         tRFIP = tRFIPentry->RFIP;
3857 
3858         if ((currentGroup = MemNew (sizeof (RFIPgroup))) == NULL) goto bail_out;
3859         if (GroupsHead == NULL) {
3860           GroupsTailVNP = ValNodeAddPointer (&GroupsHead, 0, currentGroup);
3861         } else {
3862           GroupsTailVNP = ValNodeAddPointer (&GroupsTailVNP, 0 ,currentGroup);
3863         }
3864         if (GroupsTailVNP == NULL) goto bail_out;
3865 
3866         if ((GroupsTailMemberTail = MemNew (sizeof (RFIPentry))) == NULL) goto bail_out;
3867         if ((MemberTailVNP = ValNodeAddPointer (&currentGroup->members, 0, GroupsTailMemberTail)) == NULL) goto bail_out;
3868 
3869         currentGroup->memberCount = 1;
3870         MemCopy (GroupsTailMemberTail, tRFIPentry, sizeof (RFIPentry));
3871 
3872         currentGroup->Left = tRFIP->Left;
3873         currentGroup->Right = tRFIP->Right;
3874         currentGroup->decorationLeft = tRFIPentry->decorationLeft;
3875         currentGroup->decorationRight = tRFIPentry->decorationRight;
3876 
3877       }
3878     }
3879 
3880     /*
3881       now, assign each element in Groups to a row.  algorithm is the same as in BubbleUpLayout,
3882       except that instead of looking for the first matching row, need to find (RFIPgroup->members)
3883       consecutive free rows.
3884     */
3885     for (groupVNP = GroupsHead; groupVNP != NULL; groupVNP = groupVNP->next) {
3886       currentGroup = groupVNP->data.ptrvalue;
3887 
3888       featureStart = currentGroup->decorationLeft;
3889       for (thisRow = firstRow; thisRow != NULL; thisRow = thisRow->next) {
3890         foundRows = TRUE;
3891         for (i = 0, thisRow2 = thisRow, featVNP = currentGroup->members;
3892              featVNP != NULL && i < currentGroup->memberCount;
3893              thisRow2 = thisRow2->next, featVNP = featVNP->next, i++) {
3894           tRFIPentry = featVNP->data.ptrvalue;
3895 /*
3896   note: if thisRow2 ends up being NULL then it meas thisRow was a good starting point and we just need to add some rows
3897 */
3898           if (thisRow2 == NULL) {
3899             foundRows = TRUE;
3900             break;
3901           }
3902           rowMaxRight = thisRow2->layoutData.intvalue;
3903           if ( tRFIPentry->decorationLeft <= rowMaxRight  ||   
3904                 NoVisibleGap(tRFIPentry->decorationLeft, rowMaxRight, viewScale) ) {
3905             foundRows = FALSE;
3906             if (thisRow2->next == NULL) {
3907               rows++;
3908               if ((lastRow = AddARow (lastRow)) == NULL) goto bail_out;
3909               lastRow->layoutData.intvalue = -2000000000;
3910             }
3911             break;
3912           }
3913         }
3914         if (foundRows) {
3915           for (i = 0,
3916                  thisRow2 = thisRow,
3917                  featVNP = currentGroup->members;
3918                tRFIPentry != NULL
3919                  && i < currentGroup->memberCount;
3920                featVNP = featVNP->next,
3921                  thisRow2 = thisRow2->next,
3922                  i++) {
3923             tRFIPentry = featVNP->data.ptrvalue;
3924             RFIP = tRFIPentry->RFIP;
3925             if (thisRow2 == NULL) {
3926               rows++;
3927               if ((lastRow = AddARow (lastRow)) == NULL) goto bail_out;
3928               thisRow2 = lastRow;
3929             }
3930             AddFeatureToRow (thisRow2, RFIP, FALSE, FPSP);
3931             rowMaxRight = tRFIPentry->decorationRight;
3932             thisRow2->layoutData.intvalue = rowMaxRight;
3933           }
3934           break;
3935         }
3936       }
3937     }
3938     bumpedListHead = ValNodeFreeData (bumpedListHead);
3939     for (groupVNP = GroupsHead; groupVNP != NULL; groupVNP = groupVNP->next) {
3940       currentGroup = groupVNP->data.ptrvalue;
3941       ValNodeFreeData (currentGroup->members);
3942     }
3943     GroupsHead = ValNodeFreeData (GroupsHead);
3944   }
3945 bail_out:
3946   ValNodeFreeData (bumpedListHead);
3947   ValNodeFreeData (fifoHead);
3948   for (groupVNP = GroupsHead; groupVNP != NULL; groupVNP = groupVNP->next) {
3949     currentGroup = groupVNP->data.ptrvalue;
3950     ValNodeFreeData (currentGroup->members);
3951   }
3952   GroupsHead = ValNodeFreeData (GroupsHead);
3953   return rows;
3954 }
3955 
3956 static Uint2 GeneProductsLayout (
3957   InternalRowPtr firstRow,
3958   FilterProcessStatePtr FPSP
3959 )
3960 
3961 {
3962   return GeneProductsLayoutInternal (firstRow, FPSP, FALSE);
3963 }
3964 
3965 static Uint2 GeneProductsLayoutX (
3966   InternalRowPtr firstRow,
3967   FilterProcessStatePtr FPSP
3968 )
3969 
3970 {
3971   return GeneProductsLayoutInternal (firstRow, FPSP, TRUE);
3972 }
3973 
3974 static Uint2 BubbleUpLayout (
3975   InternalRowPtr firstRow,
3976   FilterProcessStatePtr FPSP
3977 )
3978 
3979 {
3980   Uint2                   rows = 1;
3981   Int4                    featureStart, rowMaxRight;
3982   RelevantFeatureItemPtr  RFIP;
3983   InternalRowPtr          thisRow, lastRow;
3984   RenderInput             dummyRI;
3985   Uint4                   viewScale;
3986   
3987   viewScale =  FPSP->vContext->viewScale;
3988   /*
3989      This uses InternalRow.layoutData as an Int4, which stores the (x) offset of the last used pixel.
3990      so a feature starting at (internalRow.layoutData + 1) or greater can be placed in the same row
3991      without a collision.
3992    */
3993 
3994   /* add the 1st feature to the 1st row */
3995   do {
3996     RFIP = GetNextRFIPinFilterItem (FPSP);
3997     if (RFIP == NULL) return 0;
3998   } while (! BuildRenderInputFromRFIP (&dummyRI, RFIP, FPSP));
3999 
4000   AddFeatureToRow (firstRow, RFIP, FALSE, FPSP);
4001   lastRow = firstRow;
4002   firstRow->layoutData.intvalue = dummyRI.decorationRight;
4003 
4004   while ((RFIP = GetNextRFIPinFilterItem (FPSP)) != NULL) {
4005     if (! BuildRenderInputFromRFIP (&dummyRI, RFIP, FPSP)) {
4006       continue;
4007     }
4008     featureStart = dummyRI.decorationLeft;
4009     for (thisRow = firstRow; thisRow != NULL; thisRow = thisRow->next) {
4010       rowMaxRight = thisRow->layoutData.intvalue;
4011       if (featureStart >= rowMaxRight  &&  
4012             ! NoVisibleGap(featureStart, rowMaxRight, viewScale) ) {
4013         AddFeatureToRow (thisRow, RFIP, FALSE, FPSP);
4014         rowMaxRight = MAX (rowMaxRight, dummyRI.decorationRight);
4015         thisRow->layoutData.intvalue = rowMaxRight;
4016         RFIP = NULL;
4017         break;
4018       }
4019     }
4020     if (RFIP != NULL) {
4021       rows++;
4022       lastRow = AddARow (lastRow);
4023       AddFeatureToRow (lastRow, RFIP, FALSE, FPSP);
4024       rowMaxRight = dummyRI.decorationRight;
4025       lastRow->layoutData.intvalue = rowMaxRight;
4026     }
4027   }
4028   return rows;
4029 }
4030 
4031 static Uint2 SingleRowLayout (
4032   InternalRowPtr firstRow,
4033   FilterProcessStatePtr FPSP
4034 )
4035 
4036 {
4037   RelevantFeatureItemPtr RFIP, lastRFIP;
4038   ValNodePtr       freeVNP, vnp;
4039   Uint4            smearStart, smearStop;
4040   Uint1            smearFeatdeftype;
4041   ViewerContextPtr vContext;
4042   Uint4            viewScale;
4043   Boolean          adjacentNoGap;
4044   Boolean          tooNarrow;
4045   Boolean          smearing = FALSE, justSmeared = FALSE;
4046 
4047   vContext = FPSP->vContext;
4048   viewScale = vContext->viewScale;
4049   lastRFIP = GetNextRFIPinFilterItem (FPSP);
4050   if (lastRFIP == NULL)
4051     return 0;
4052   smearFeatdeftype = lastRFIP->featdeftype;
4053 
4054   while ((RFIP = GetNextRFIPinFilterItem (FPSP)) != NULL) {
4055     adjacentNoGap =  PixelsBetweenSeqCoords(lastRFIP->Right, RFIP->Left, viewScale) < 2; /* was 3 */
4056     /* TestForSmearOverlap (lastRFIP->Right + 2 * vContext->viewScale, RFIP->Left, vContext); */
4057     tooNarrow = PixelsBetweenSeqCoords(RFIP->Left,  RFIP->Right, viewScale) < 5;
4058     /* TestForSmearOverlap (RFIP->Left + 4 * vContext->viewScale, RFIP->Right, vContext); */
4059     if (adjacentNoGap && tooNarrow) {
4060       if (smearing == FALSE) {
4061         tooNarrow = PixelsBetweenSeqCoords(lastRFIP->Left, lastRFIP->Right, viewScale) < 5;
4062         /* TestForSmearOverlap (lastRFIP->Left + 4 * vContext->viewScale, lastRFIP->Right, vContext); */
4063         if (tooNarrow) {
4064           smearStart = lastRFIP->Left;
4065         } else {
4066           smearStart = RFIP->Left;
4067         }
4068         smearing = TRUE;
4069       }
4070       justSmeared = TRUE;
4071       if (smearFeatdeftype != RFIP->featdeftype) {
4072         smearFeatdeftype = FEATDEF_ANY;
4073       } else if (smearFeatdeftype == 0) {
4074         smearFeatdeftype = RFIP->featdeftype;
4075       }
4076       smearStop = RFIP->Right;
4077       continue;
4078     }
4079     /* VNP is non-NULL during a smear; this tests for regular features*/
4080     if (! justSmeared) {
4081       AddFeatureToRow (firstRow, lastRFIP, TRUE, FPSP);
4082       lastRFIP = RFIP;
4083     } else { /* we just finished a smear group */
4084       lastRFIP = MemNew (sizeof (RelevantFeatureItem));
4085       if (lastRFIP == NULL) return 1;
4086       lastRFIP->Left = smearStart;
4087       lastRFIP->Right = smearStop;
4088       lastRFIP->featstrand = Seq_strand_unknown;
4089       lastRFIP->circularSpanningOrigin = FALSE;
4090       lastRFIP->LeftEnd = lastRFIP->RightEnd = EndAbsolute;
4091       lastRFIP->circularSpanningOrigin = FALSE;
4092       lastRFIP->featdeftype = smearFeatdeftype;
4093       lastRFIP->numivals = 1;
4094       vnp = ValNodeAddPointer (&FPSP->lastInFreeList, 0, lastRFIP);
4095       if (FPSP->needFreeList == NULL) {
4096         FPSP->needFreeList = FPSP->lastInFreeList;
4097       }
4098       FPSP->lastInFreeList = vnp;
4099       smearing = FALSE;
4100       AddFeatureToRow (firstRow, lastRFIP, TRUE, FPSP);
4101       justSmeared = FALSE;
4102       lastRFIP = RFIP;
4103     }
4104   }
4105   if (! justSmeared) { /* the last feature was not in a smear group */
4106     AddFeatureToRow (firstRow, lastRFIP, TRUE, FPSP);
4107   } else {
4108     RFIP = MemNew (sizeof (RelevantFeatureItem));
4109     if (RFIP == NULL) return 1;
4110     RFIP->Left = smearStart;
4111     RFIP->Right = smearStop;
4112     RFIP->featstrand = Seq_strand_unknown;
4113     RFIP->circularSpanningOrigin = FALSE;
4114     
4115     RFIP->LeftEnd = RFIP->RightEnd = EndAbsolute;
4116     RFIP->featdeftype = smearFeatdeftype;
4117     RFIP->numivals = 1;
4118 
4119     if ((freeVNP = MemNew (sizeof (ValNode))) == NULL) {
4120       MemFree (RFIP);
4121       return 1;
4122     }
4123     freeVNP->data.ptrvalue = RFIP;
4124     freeVNP->next = FPSP->needFreeList;
4125     FPSP->needFreeList = freeVNP;
4126 
4127     AddFeatureToRow (firstRow, RFIP, TRUE, FPSP);
4128   }
4129   return 1;
4130 }
4131 
4132 
4133 /* 
4134   Call DrawNameAnnotbox() in a sub-function of FilterAndLayout, when you know that something
4135   will be drawn in as a result of this call to FilterAndLayout in the current sanLevelSeg.
4136   return FALSE if there is an error. Return true whether we draw anything or not.
4137 */
4138 static 
4139 Boolean DrawNamedAnnotBox(FilterProcessStatePtr FPSP)
4140 {
4141   ViewerContextPtr vContext;
4142   CharPtr          annotName;
4143   FilterPtr        FP;
4144   
4145   if (FPSP == NULL) /* something wrong !? */
4146     return FALSE;
4147   vContext = FPSP->vContext;
4148   if (vContext == NULL)
4149     return FALSE;
4150   if (vContext->sanLevelSeg == NULL)
4151     return FALSE;
4152   if ((FP = vContext->FltPtr) == NULL)
4153     return FALSE;
4154 
4155   if (!FP->GroupByAnnot) /* we are not grouping by named annotations */
4156     return TRUE;
4157     
4158   annotName = GetSeqAnnotName(vContext->currentSAP);
4159   if (annotName == NULL)  /* we are currently not working on a named Seq Annot. */
4160     return TRUE;
4161     
4162   if (FP->DrawAnnotBox && !FPSP->annotBoxDrawn) /* We should draw the box and it hasn't been done yet. */
4163   {
4164     AddAttribute (vContext->sanLevelSeg, COLOR_ATT, FP->AnnotBoxColor, 0, 0, 0, 0);
4165     AddSilentSegRect (vContext->sanLevelSeg, FALSE, 0);
4166     FPSP->ceiling -= 5;
4167     FPSP->annotBoxDrawn = TRUE;
4168   }
4169   
4170   /* Have a label that should be drawn at the top of the box? */
4171   if (!FPSP->annotLabelDrawn && !StringHasNoText (annotName))
4172   {
4173     AddAttribute (vContext->sanLevelSeg, COLOR_ATT, FP->AnnotLabelColor, 0, 0, 0, 0);
4174     switch (FP->AnnotLabelLoc) {
4175     case LabelOnTop:
4176       AddTextLabel (vContext->sanLevelSeg, (vContext->from + vContext->to) / 2, FPSP->ceiling,
4177                     annotName, FP->AnnotLabelFont, 1, LOWER_CENTER, 0);
4178       SelectFont(FP->AnnotLabelFont);
4179       FPSP->ceiling -=  LineHeight () + 4;
4180       break;
4181     case LabelOnSide:
4182       AddTextLabel (vContext->sanLevelSeg, 0, FPSP->ceiling, 
4183                     annotName, FP->AnnotLabelFont, 1, LOWER_RIGHT, 0);
4184       break;
4185     default:
4186       break;
4187     }
4188     FPSP->annotLabelDrawn = TRUE;
4189   }
4190   return TRUE;
4191 }
4192 
4193 /* 
4194   Call DrawFilterItemBoxLabel() in a sub-function of FilterAndLayout, when you know that something
4195   will be drawn in as a result of this call to FilterAndLayout in the current filterSeg (vc->drawOnMe).
4196   return FALSE if there is an error. Return true whether we draw anything or not.
4197 */
4198 static Boolean DrawFilterItemBoxLabel(
4199   FilterProcessStatePtr FPSP,
4200   FilterItemPtr FIP
4201 )
4202 {
4203   ViewerContextPtr    vContext;
4204   SegmenT             invisibleSeg;
4205 
4206   if (FPSP == NULL) /* something wrong !? */
4207     return FALSE;
4208   vContext = FPSP->vContext;
4209   if (vContext == NULL)
4210     return FALSE;
4211   if (vContext->drawOnMe == NULL)
4212     return FALSE;
4213   
4214   if (FIP->DrawGroupBox && !FPSP->groupBoxDrawn) /* want to draw it, or already drawn. */
4215   {   
4216     AddAttribute (vContext->drawOnMe, COLOR_ATT, FIP->GroupBoxColor, 0, 0, 0, 0);
4217     AddSilentSegRect (vContext->drawOnMe, FALSE, 0);
4218     FPSP->ceiling -= 5;
4219     /* add invisible line to force width of SegRect */
4220     invisibleSeg = CreateSegment (vContext->drawOnMe, 0, 0);
4221     SetSegmentVisibleFlag (invisibleSeg, FALSE);
4222     AddLine (invisibleSeg, vContext->from - 1 * vContext->viewScale ,  FPSP->ceiling , 
4223                            vContext->to + 1 * vContext->viewScale ,  FPSP->ceiling, FALSE, 0);
4224     FPSP->groupBoxDrawn = TRUE;
4225   }
4226     
4227   /* need to draw a label on top? */
4228   if (FIP->GroupLabelLoc == LabelOnTop && !FPSP->groupLabelDrawn && !StringHasNoText (FIP->GroupLabel)) {
4229     AddAttribute (vContext->drawOnMe, COLOR_ATT, FIP->GroupLabelColor, 0, 0, 0, 0);
4230     AddTextLabel (vContext->drawOnMe, (vContext->from + vContext->to) / 2, FPSP->ceiling,
4231                   FIP->GroupLabel, FIP->GroupLabelFont, 1, LOWER_CENTER, 0);
4232     SelectFont (FIP->GroupLabelFont);
4233     FPSP->ceiling -= LineHeight () + 4;
4234     FPSP->groupLabelDrawn = TRUE;
4235   }
4236 
4237   return TRUE;
4238 }
4239 
4240 
4241 
4242 static const LayoutFunction LayoutAlgorithmTable [] = {
4243   BubbleUpLayout,             /* placeholder for Layout_Inherit */
4244   SimpleDiagonalLayout,       /* Layout_Diagonal */
4245   /*  SimpleDiagonalLayout,*/ /* Layout_DiagonalSawtooth (to be implemented) */
4246   SingleRowLayout,            /* Layout_FeatTypePerLine (same as single-row, but features are grouped by type before processing) */
4247   BubbleUpLayout,             /* Layout_FeatTypePerLineGroup (same as bubble-up, but features are grouped by type before processing) */
4248   /*SingleRowLayout,*/        /* Layout_AllInOneLine (which isn't working currently, and may be less useful than expected*/
4249   BubbleUpLayout,             /* Layout_PackUpward */
4250   GeneProductsLayout,         /* Layout_GroupCorrespondingFeats (?working?) */
4251   GeneProductsLayoutX         /* Layout_GroupCorrespondingFeatsRepeat (?working?) */
4252 };
4253 
4254 /* non-leaf segments contain SEGMENT_TREE_BASE other segments */
4255 #define SEGMENT_TREE_BASE 16
4256 #define SEGMENT_TREE_BASE2 (SEGMENT_TREE_BASE * SEGMENT_TREE_BASE)
4257 #define SEGMENT_TREE_BASE3 (SEGMENT_TREE_BASE * SEGMENT_TREE_BASE * SEGMENT_TREE_BASE)
4258 
4259 static Uint2 ProcessRows (
4260   LayoutAlgorithm layoutC,
4261   FilterProcessStatePtr FPSP,
4262   ViewerContextPtr vContext
4263 )
4264 
4265 {
4266   Uint2                   I, J, K, lastI, lastJ, lastK; /* for keeping track of which segment in the tree we're in*/
4267   Uint1                   featdeftype;
4268   Int4                    featMidPoint;
4269   Uint2                   row, rowCount, rowHeight, totalHeight;
4270   Uint4                   feat;
4271   InternalRowPtr          firstRow, thisRow;
4272   ValNodePtr              VNP;
4273   RelevantFeatureItemPtr  RFIP;
4274   RenderInput             RI;           /* dummy used while finding height of each row */
4275   SegmenT                 SegmentTreeTop;
4276   SegmenT                 SegmentTreeMid;
4277   SegmenT                 SegmentTreeBot;
4278   Boolean                 SegmentChanged = TRUE;
4279   Boolean                 emptyRow = TRUE;
4280   Boolean                 allEmpty = TRUE;
4281   FilterItemPtr           FIP;
4282 
4283   firstRow = MemNew (sizeof (InternalRow));
4284   if (firstRow == NULL) return 0;
4285   firstRow->feats = NULL;
4286   VNP = FPSP->currentFilterVNP;
4287   if (VNP == NULL) return 0;
4288   FIP = (FilterItemPtr) VNP->data.ptrvalue;
4289 
4290   rowCount = (*LayoutAlgorithmTable [layoutC]) (firstRow, FPSP);
4291 
4292   thisRow = firstRow;
4293   totalHeight = 0;
4294 
4295   for (row = 0; row < rowCount; row++) {
4296     if (thisRow == NULL) continue;
4297     /*First iterate through features to find the row's height */
4298     VNP = thisRow->feats;
4299     rowHeight = 0;
4300     if (thisRow->rowFeatureCount > 0) {
4301       allEmpty = FALSE;
4302     }
4303     for (feat = 0; feat < thisRow->rowFeatureCount; feat++) {
4304       RFIP = VNP->data.ptrvalue;
4305       if (VNP == NULL) break;
4306       VNP = VNP->next;
4307       if (RFIP == NULL) return 0;
4308       if (! BuildRenderInputFromRFIP (&RI, RFIP, FPSP)) {
4309         continue;
4310       }
4311       rowHeight = MAX (rowHeight, RI.decorationHeight);
4312     }
4313     if (!allEmpty)
4314     {
4315       DrawNamedAnnotBox(FPSP);
4316       DrawFilterItemBoxLabel(FPSP, FIP);
4317     }
4318 
4319     /*Repeat, but actually draw them this time */
4320     VNP = thisRow->feats;
4321     RI.rowHeight = rowHeight;
4322     RI.yStart = FPSP->ceiling - totalHeight;
4323     if (thisRow->rowFeatureCount > 0) {
4324       emptyRow = FALSE;
4325     } else {
4326       emptyRow = TRUE;
4327     }
4328     I = J = K = 0;
4329     SegmentChanged = TRUE;
4330     for (feat = 0; feat < thisRow->rowFeatureCount; feat++) {
4331       if (VNP == NULL) break;
4332       if ((RFIP = VNP->data.ptrvalue) == NULL) return 0;
4333       VNP = VNP->next;
4334       featdeftype = RFIP->featdeftype;
4335       featMidPoint = (RFIP->Left + RFIP->Right) / 2;
4336       lastI = I;
4337       lastJ = J;
4338       lastK = K;
4339       I = (SEGMENT_TREE_BASE * featMidPoint) / vContext->seqLength;
4340       J = ((SEGMENT_TREE_BASE2 * featMidPoint) / vContext->seqLength) % SEGMENT_TREE_BASE;
4341       K = ((SEGMENT_TREE_BASE3 * featMidPoint) / vContext->seqLength) % SEGMENT_TREE_BASE;
4342 
4343       if (I != lastI || SegmentChanged) {
4344         SegmentTreeTop  = CreateSegment (vContext->drawOnMe, 0, 0);
4345         SegmentChanged = TRUE;
4346       }
4347       if (J != lastJ || SegmentChanged) {
4348         SegmentTreeMid = CreateSegment (SegmentTreeTop , 0, 0);
4349         SegmentChanged = TRUE;
4350       }
4351       if (K != lastK || SegmentChanged) {
4352         SegmentTreeBot = CreateSegment (SegmentTreeMid, 0, 0);
4353         SegmentChanged = TRUE;
4354       }
4355       if (SegmentChanged) {
4356         MemSet (FPSP->drawSegs, 0, sizeof (FPSP->drawSegs));
4357         MemSet (FPSP->labelSegs, 0, sizeof (FPSP->labelSegs));
4358         SegmentChanged = FALSE;
4359       }
4360       if (! EnsureFeatureHasSegment (FPSP, featdeftype, SegmentTreeBot)) return 0;
4361       if (! BuildRenderInputFromRFIP (&RI, RFIP, FPSP)) {
4362         continue;
4363       }
4364 
4365       DrawFeatureAndLabel (&RI, vContext);
4366     }
4367     if (! emptyRow) {
4368       totalHeight += rowHeight + FPSP->currentFIP->IntraRowPaddingPixels;
4369     }
4370     if (thisRow->next == NULL) break;
4371     thisRow = thisRow->next;
4372   }
4373   if (allEmpty) {
4374     return 0;
4375   }
4376   return totalHeight;
4377 }
4378 
4379 typedef struct scalesntdata {
4380   SegmenT                   seg;
4381   PrimitivE                 snt;
4382   BoxInfo                   box;
4383   Int4                      length;
4384   Int4                      labelOffset;
4385   BioseqAppearanceItemPtr   bioseqAIP;
4386   Boolean                   disableLastLabel;
4387   Boolean                   disableFirstLabel;
4388   Int4                      offset;
4389 } ScaleSntData, PNTR ScaleSntPtr;
4390 
4391 static void ScaleSntDrawProc (
4392   BigScalar calldata,
4393   PrimDrawContext pdc
4394 )
4395 
4396 {
4397   Int4                     curScale;
4398   DrawInfoPtr              dInfoPtr;
4399   Int4                     i, j;
4400   RecT                     r;
4401   PntInfo                  pnt;
4402   PoinT                    pt;
4403   ScaleSntPtr              ssp;
4404   BoxInfo                  tmpBox;
4405   Uint1                    tickCount;
4406   Char                     buffer[16];
4407   BioseqAppearanceItemPtr  bioseqAIP;
4408   Uint1                    scaleHeight;
4409   Int4                     from, to;
4410 
4411   ssp = (ScaleSntPtr) calldata;
4412   if (ssp == NULL) return;
4413 
4414   dInfoPtr = (DrawInfoPtr) pdc;
4415   tmpBox = ssp->box;
4416   bioseqAIP = ssp->bioseqAIP;
4417   scaleHeight = bioseqAIP->scaleHeight;
4418 
4419   from = tmpBox.left;
4420   to   = tmpBox.right;
4421 
4422   curScale = dInfoPtr->scale.scaleX;
4423   r.left   = (Int2) ((dInfoPtr->scale.offsetX + tmpBox.left)  / curScale);
4424   r.right  = (Int2) ((dInfoPtr->scale.offsetX + tmpBox.right)  / curScale);
4425   SelectFont (bioseqAIP->scaleFont);
4426   SelectColor (bioseqAIP->scaleColor[0], bioseqAIP->scaleColor[1], bioseqAIP->scaleColor[2]);
4427 
4428   /* if the right-most edge is visible, draw the final tick mark and a right-justified total length label */
4429   if (dInfoPtr->scale.worldWindow.right >= to - 100 * curScale) {
4430     pnt.x = to;
4431     pnt.y = tmpBox.top;
4432     MapWorldPointToPixel (&pt, &pnt, &dInfoPtr->scale);
4433     MoveTo (pt.x, pt.y);
4434     pnt.y = tmpBox.top - scaleHeight;
4435     MapWorldPointToPixel (&pt, &pnt, &dInfoPtr->scale);
4436     LineTo (pt.x, pt.y);
4437     sprintf (buffer, "%ld", (long)to + ssp->labelOffset + 1);
4438     pt.x += 3 - StringWidth (buffer);
4439     pt.y += 1 + Ascent ();
4440     PaintStringEx (buffer, pt.x, pt.y);
4441   }
4442 
4443   /* if the left-most edge is visible, draw the first label, left-justified */
4444   if (dInfoPtr->scale.worldWindow.left - 400 * curScale <= from) {
4445     pnt.x = from;
4446     pnt.y = tmpBox.top - scaleHeight;
4447     sprintf (buffer, "%ld", (long)((from == 0) ? 1 : from)  + ssp->labelOffset);
4448     MapWorldPointToPixel (&pt, &pnt, &dInfoPtr->scale);
4449     pt.y += 1 + Ascent ();
4450     PaintStringEx (buffer, pt.x, pt.y);
4451   }
4452 
4453   if (tmpBox.left > dInfoPtr->scale.worldWindow.right ||
4454       tmpBox.right < dInfoPtr->scale.worldWindow.left ||
4455       tmpBox.top < dInfoPtr->scale.worldWindow.bottom ||
4456       tmpBox.bottom > dInfoPtr->scale.worldWindow.top)
4457     return;
4458 
4459   if (dInfoPtr->checked == FALSE ) {
4460     if (tmpBox.left < dInfoPtr->scale.worldWindow16.left)
4461       tmpBox.left = dInfoPtr->scale.worldWindow16.left;
4462     if (tmpBox.right > dInfoPtr->scale.worldWindow16.right)
4463       tmpBox.right = dInfoPtr->scale.worldWindow16.right;
4464     if (tmpBox.top > dInfoPtr->scale.worldWindow16.top)
4465       tmpBox.top = dInfoPtr->scale.worldWindow16.top;
4466     if (tmpBox.bottom < dInfoPtr->scale.worldWindow16.bottom)
4467       tmpBox.bottom = dInfoPtr->scale.worldWindow16.bottom;
4468   }
4469 
4470   i = MAX (dInfoPtr->scale.worldWindow.left, tmpBox.left);
4471   /*  i = i + 10 * curScale - (i % (10 * curScale)) - 1;*/
4472   while (i % (10 * curScale) != 0) {
4473     i++; /* !!! do this the right way */
4474   }
4475   for (tickCount = (i / (10 * curScale)) % 10;
4476        i < MIN (dInfoPtr->scale.worldWindow.right, to);
4477        i += curScale * 10) {
4478     pnt.x = i;
4479     pnt.y = tmpBox.top;
4480     MapWorldPointToPixel (&pt, &pnt, &dInfoPtr->scale);
4481     MoveTo (pt.x, pt.y);
4482 
4483     if (tickCount == 0 || tickCount == 5) {
4484       pnt.y = tmpBox.top - scaleHeight; /* draw full-height tick */
4485       MapWorldPointToPixel (&pt, &pnt, &dInfoPtr->scale);
4486       LineTo (pt.x, pt.y);
4487       if (tickCount == 0
4488           /* if i + 100curScale > the right boundary, this is the last label */
4489           && (!((i + 100 * curScale >= to) && ssp->disableLastLabel))
4490           && (!((i - 100 * curScale <= from) && ssp->disableFirstLabel))
4491           && (i != from)) {
4492         sprintf (buffer, "%ld", (long)((i == 0) ? 1 : i) + ssp->labelOffset); /* put the origin at 1 instead of 0 */
4493         pt.x -= StringWidth (buffer) / 2; /* center text around the tick mark*/
4494         pt.y += 1 + Ascent ();
4495         PaintStringEx (buffer, pt.x, pt.y);
4496       }
4497       /* disable the right-end one in the case of an overlap */
4498     } else {
4499       pnt.y = tmpBox.top - scaleHeight / 2;
4500       MapWorldPointToPixel (&pt, &pnt, &dInfoPtr->scale);
4501       LineTo (pt.x, pt.y);
4502     }
4503     tickCount = (tickCount + 1) % 10;
4504   }
4505 
4506   /* also, we might need to redraw the labels just to the left or to the right of the update region. */
4507   i = MAX (dInfoPtr->scale.worldWindow.left, tmpBox.left);
4508   j = i; /* j = leftmost visible pixel in world coordinates */
4509   while (i % (100 * curScale) != 0) {
4510     i--;
4511   }
4512   pnt.x = i;
4513   sprintf (buffer, "%ld", (long)((i == 0) ? 1 : i) + ssp->labelOffset);
4514   i += curScale * ((StringWidth (buffer) / 2) + 1);
4515   if (i >= j
4516       && i > from
4517       && (!((pnt.x + 100 * curScale >= to) && ssp->disableLastLabel))
4518       && (!((pnt.x - 100 * curScale <= from) && ssp->disableFirstLabel))
4519       && pnt.x > from) {
4520     pnt.y = tmpBox.top - scaleHeight;
4521     MapWorldPointToPixel (&pt, &pnt, &dInfoPtr->scale);
4522     pt.x -= StringWidth (buffer) / 2; /* center text around the tick mark*/
4523     pt.y += 1 + Ascent ();
4524     PaintStringEx (buffer, pt.x, pt.y);
4525   }
4526   /* now check the right side*/
4527 
4528   i = MIN (dInfoPtr->scale.worldWindow.right, to);
4529   j = i; /* j = rightmost visible pixel in world coordinates */
4530   while (i % (100 * curScale) != 0) {
4531     i++;
4532   }
4533   pnt.x = i;
4534   sprintf (buffer, "%ld", (long)i + ssp->labelOffset);
4535   i -= curScale * ((StringWidth (buffer) / 2) + 1);
4536   if (i <= j
4537       && (!((pnt.x + 100 * curScale >= to) && ssp->disableLastLabel))
4538       && (!((pnt.x - 100 * curScale <= from) && ssp->disableFirstLabel))
4539       && pnt.x < to) {
4540     pnt.y = tmpBox.top - scaleHeight;
4541     MapWorldPointToPixel (&pt, &pnt, &dInfoPtr->scale);
4542     pt.x -= StringWidth (buffer) / 2; /* center text around the tick mark*/
4543     pt.y += 1 + Ascent ();
4544     PaintStringEx (buffer, pt.x, pt.y);
4545   }
4546 }
4547 
4548 static void ScaleSntCleanupProc (
4549   BigScalar calldata
4550 )
4551 
4552 {
4553   ScaleSntPtr  ssp;
4554 
4555   ssp = (ScaleSntPtr) calldata;
4556   MemFree (ssp);
4557 }
4558 
4559 static Boolean LIBCALLBACK Asn2gphSegmentExploreProc (
4560   SeqLocPtr slp,
4561   SeqMgrSegmentContextPtr context
4562 )
4563 
4564 {
4565   /* context->userdata a pointer to vContext */
4566   RelevantFeatureItemPtr RFIP;
4567   SeqIdPtr           sip, newid = NULL;
4568   Int4               gi;
4569   Char               labelBuf [100];
4570   Int4               left, right;
4571   ViewerContextPtr   vContext;
4572   AppearancePtr      AP;
4573   BioseqAppearanceItemPtr BioAIP;
4574   BioseqPtr          bsp;
4575 
4576   if ((RFIP = MemNew (sizeof (RelevantFeatureItem))) == NULL) return FALSE;
4577   vContext = context->userdata;
4578   bsp = vContext->BSP;
4579   ValNodeAddPointer (&vContext->BSPsegmentVNP, 0, RFIP);
4580   sip = SeqLocId (slp);
4581   if (sip != NULL && sip->choice == SEQID_GI /* && BioseqFindCore (sip) != NULL */) {
4582     gi = sip->data.intvalue;
4583     newid = SeqIdStripLocus (GetSeqIdForGI (gi));
4584     sip = newid;
4585     if (newid != NULL) {
4586       ValNodeAddInt (&newid, SEQID_GI, gi);
4587     }
4588   }
4589   
4590   AP = vContext->AppPtr;
4591   BioAIP = AP->bioseqAIP;
4592 
4593   left = context->from;
4594   right = context->to - left + context->cumOffset;
4595   left = context->cumOffset;
4596   if (sip != NULL) {
4597     if (BioAIP->format != PRINTID_FASTA_LONG) { 
4598       sip = SeqIdFindWorst (sip);
4599     }
4600     SeqIdWrite (sip, labelBuf, BioAIP->format, sizeof (labelBuf) - 1);
4601     RFIP->ContentLabel = StringSave (labelBuf); /* record this in a list of things to be freed so that we dont leak memory!!!!! */
4602   }
4603   SeqIdSetFree (newid);
4604   RFIP->Left = MIN (left, right);
4605   RFIP->Right = MAX (left, right);
4606   RFIP->entityID = context->entityID;
4607   RFIP->itemID = context->itemID;
4608   if (bsp->repr == Seq_repr_seg) {
4609     RFIP->itemType = OBJ_BIOSEQ_SEG;
4610   } else if (bsp->repr == Seq_repr_delta) {
4611     RFIP->itemType = OBJ_BIOSEQ_DELTA;
4612   } else {
4613     RFIP->itemType = OBJ_BIOSEQ_SEG;
4614   }
4615   RFIP->numivals = 1;
4616   RFIP->featdeftype = 0;
4617   RFIP->featstrand = context->strand;
4618   RFIP->circularSpanningOrigin = FALSE;
4619   return TRUE;
4620 }
4621 
4622 static Int4 DrawBioseqSegments (
4623   Int4 initialYOffset,
4624   FilterItemPtr FIP,
4625   ViewerContextPtr vContext
4626 )
4627 
4628 {
4629   ValNodePtr   listHead = NULL, vnpSegment = NULL;
4630   Int4         segmentCount;
4631   Uint2         rowHeight, row = 0; /* row is either 0 or 1 */
4632   AppearanceItem segmentAI;
4633   AppearanceItemPtr oldAIPzero;
4634   AppearancePtr AP;
4635   BioseqAppearanceItemPtr BioAIP;
4636   RelevantFeatureItemPtr RFIP;
4637   FilterProcessState FPS;
4638   Uint2         oldMaxScale;
4639 
4640   vContext->BSPsegmentVNP = NULL;
4641   SeqMgrExploreSegments (vContext->BSP, vContext, Asn2gphSegmentExploreProc);
4642   listHead = vContext->BSPsegmentVNP;
4643   segmentCount = 0;
4644   for (vnpSegment = listHead; vnpSegment != NULL; vnpSegment = vnpSegment->next) {
4645     segmentCount++;
4646   }
4647 
4648   if (segmentCount <= 1) {
4649     ValNodeFreeData (listHead);
4650     return 0;
4651   }
4652 
4653   oldMaxScale = vContext->FltPtr->MaxScaleWithLabels;
4654   vContext->FltPtr->MaxScaleWithLabels = 10000;
4655   AP = vContext->AppPtr;
4656   BioAIP = AP->bioseqAIP;
4657   MemCpy (segmentAI.LabelColor, BioAIP->labelColor, sizeof (segmentAI.LabelColor));
4658   MemCpy (segmentAI.Color, BioAIP->bioseqColor, sizeof (segmentAI.Color));
4659   segmentAI.RenderChoice = Render_Box;
4660   segmentAI.Height = BioAIP->height;
4661   segmentAI.AddDescToLabel = TRUE;
4662   segmentAI.AddTypeToLabel = FALSE;
4663   segmentAI.LabelFont = BioAIP->labelFont;
4664   segmentAI.LabelLoc = FIP->LabelLoc;
4665   FIP->LabelLoc = LabelAboveClip;
4666 
4667   segmentAI.ShowArrow = FALSE;
4668   segmentAI.VibLinestyle = SOLID_LINE;
4669   segmentAI.VibShading = SOLID_SHADING;
4670   oldAIPzero = vContext->AppPtr->FeaturesAppearanceItem[0];
4671   vContext->AppPtr->FeaturesAppearanceItem[0] = &segmentAI;
4672 
4673   MemSet (&FPS, 0, sizeof (FPS));
4674   FPS.currentFIP = FIP;
4675   FPS.ceiling = initialYOffset;
4676   FPS.vContext = vContext;
4677 
4678   FPS.renderParm.AIP = &segmentAI;
4679   FPS.renderParm.FIP = FIP;
4680 
4681   SelectFont (segmentAI.LabelFont);
4682   rowHeight = LineHeight () + segmentAI.Height + 3 + FIP->IntraRowPaddingPixels;
4683   row = 0;
4684 
4685   for (vnpSegment = listHead; vnpSegment != NULL; vnpSegment = vnpSegment->next) {
4686 
4687     RFIP = vnpSegment->data.ptrvalue;
4688     EnsureFeatureHasSegment (&FPS, 0, vContext->drawOnMe);
4689     if (!BuildRenderInputFromRFIP (&FPS.renderParm, RFIP, &FPS)) {
4690       continue;
4691     }
4692     row = 1 - row; /* alternate between 0 and 1 */
4693     FPS.renderParm.rowHeight += 2;
4694     FPS.renderParm.yStart = initialYOffset - (row * rowHeight);
4695     DrawFeatureAndLabel (&FPS.renderParm, vContext);
4696   }
4697   if (FPS.needFreeList != NULL) {
4698     FPS.needFreeList = ValNodeFreeData (FPS.needFreeList);
4699     FPS.lastInFreeList = NULL;
4700   }
4701   ValNodeFreeData (listHead);
4702   vContext->BSPsegmentVNP = NULL;
4703   vContext->AppPtr->FeaturesAppearanceItem[0] = oldAIPzero;
4704   vContext->FltPtr->MaxScaleWithLabels = oldMaxScale;
4705   return 2 * rowHeight + 7 + FIP->IntraRowPaddingPixels + FIP->GroupPadding;
4706 }
4707 
4708 static Int4 DrawBioseq (
4709   Int4 initialYOffset,
4710   FilterItemPtr FIP,
4711   ViewerContextPtr vContext
4712 )
4713 
4714 {
4715   PrimitivE               thisPrim;
4716   Char                    labelbuf[128];
4717   BioseqPtr               bsp;
4718   ScaleSntPtr             ssp;
4719   Uint2                   labelLineHeight, lastLabelWidth, firstLabelWidth;
4720   SegmenT                 seg;
4721   SeqIdPtr                sip;
4722   BioseqAppearanceItemPtr bioseqAIP;
4723   Uint1                   scaleHeight;
4724   Uint1                   height;
4725   Int4                    yOffset;
4726   Uint4                   i, j;
4727   Boolean                 drawScale;
4728   AppearancePtr           AP;
4729 
4730   AP = vContext->AppPtr;
4731   bioseqAIP = AP->bioseqAIP;
4732   yOffset = initialYOffset;
4733   bsp = vContext->BSP;
4734 
4735   drawScale = bioseqAIP->drawScale;
4736   if (FIP->DrawScale != TristateUnset) {
4737     drawScale = BOOL_FROM_SET_TRISTATE(FIP->DrawScale);
4738   }
4739 
4740   scaleHeight = bioseqAIP->scaleHeight;
4741   if (bioseqAIP->format != PRINTID_FASTA_LONG) {
4742     sip = SeqIdFindWorst (bsp->id);
4743   } else {
4744     sip = bsp->id;
4745   }
4746   SeqIdWrite (sip, labelbuf, bioseqAIP->format, sizeof (labelbuf) - 1);
4747   seg = CreateSegment (vContext->topLevelSeg, 0, 0);
4748 
4749   if (bioseqAIP->labelLoc == LabelOnTop) {
4750     AddAttribute (seg, COLOR_ATT, bioseqAIP->labelColor, 0, 0, 1, 0);
4751     thisPrim = AddTextLabel (seg, vContext->from, yOffset, labelbuf, bioseqAIP->labelFont, 0, LOWER_RIGHT, 0);
4752     SetPrimitiveIDs (thisPrim, bsp->idx.entityID, bsp->idx.itemID, OBJ_BIOSEQ, 0);
4753     SelectFont (bioseqAIP->labelFont);
4754     height = LineHeight() + 1;
4755     yOffset = initialYOffset - height;
4756   } else {
4757     height = 0;
4758   }
4759 
4760   AddAttribute (seg, COLOR_ATT, bioseqAIP->bioseqColor, 0, 0, 1, 0);
4761   thisPrim = AddRectangle (seg, vContext->from, yOffset, vContext->to, yOffset - bioseqAIP->height, NO_ARROW, TRUE, 0);
4762   SetPrimitiveIDs (thisPrim, bsp->idx.entityID, bsp->idx.itemID, OBJ_BIOSEQ, 0);
4763   AddAttribute (seg, COLOR_ATT, bioseqAIP->labelColor, 0, 0, 1, 0);
4764 
4765   height += bioseqAIP->height + 2;
4766   yOffset = initialYOffset - height;
4767 
4768   if (bioseqAIP->labelLoc == LabelOnSide) {
4769     AddAttribute (seg, COLOR_ATT, bioseqAIP->labelColor, 0, 0, 1, 0);
4770     thisPrim = AddTextLabel (seg, vContext->from, yOffset, labelbuf, bioseqAIP->labelFont, 1, UPPER_LEFT, 0);
4771     SetPrimitiveIDs (thisPrim, bsp->idx.entityID, bsp->idx.itemID, OBJ_BIOSEQ, 0);
4772   } else if (bioseqAIP->labelLoc == LabelOnBottom) {
4773     AddAttribute (seg, COLOR_ATT, bioseqAIP->labelColor, 0, 0, 1, 0);
4774     thisPrim = AddTextLabel (seg, 0, yOffset, labelbuf, bioseqAIP->labelFont, 1, LOWER_RIGHT, 0);
4775     SetPrimitiveIDs (thisPrim, bsp->idx.entityID, bsp->idx.itemID, OBJ_BIOSEQ, 0);
4776     SelectFont (bioseqAIP->labelFont);
4777     height += LineHeight() + 2;
4778     yOffset = initialYOffset - height;
4779   }
4780 
4781   if (drawScale) {
4782 
4783     ssp = (ScaleSntPtr) MemNew (sizeof (ScaleSntData));
4784     if (ssp == NULL) return height;
4785     SelectFont (bioseqAIP->scaleFont);
4786     labelLineHeight = LineHeight();
4787     ssp->seg = seg;
4788     ssp->labelOffset = 0;
4789     ssp->length = vContext->to - vContext->from;
4790     ssp->box.left = vContext->from;
4791     ssp->box.right = vContext->to;
4792     ssp->box.top = yOffset;
4793     ssp->box.bottom = yOffset - 2 - scaleHeight - labelLineHeight;
4794     ssp->bioseqAIP = bioseqAIP;
4795     
4796     i = vContext->to - vContext->to % (100 * vContext->viewScale); /* the last labelled tick-mark */
4797     
4798     sprintf (labelbuf, "%ld", (long)i + ssp->labelOffset);
4799     j = StringWidth (labelbuf); /* j is the width (pixels) of the last tick mark's label */
4800     
4801     sprintf (labelbuf, "%ld", (long)vContext->to + ssp->labelOffset);
4802     lastLabelWidth = (StringWidth (labelbuf) + 2) * vContext->viewScale;
4803     
4804     if (vContext->from > 0) {
4805       sprintf (labelbuf, "%ld", (long)vContext->from + ssp->labelOffset);
4806     } else {
4807       sprintf (labelbuf, "1");
4808     }
4809     firstLabelWidth = StringWidth (labelbuf) * vContext->viewScale;
4810     
4811     i += vContext->viewScale * j / 2; /* right-most edge of the centered label */
4812     j = MAX (j, 10); /* always leave at least 10 pixels between the last 2 labels */
4813     if (i + j * vContext->viewScale >= vContext->to - lastLabelWidth) {
4814       ssp->disableLastLabel = TRUE;
4815     } else {
4816       ssp->disableLastLabel = FALSE;
4817     }
4818     
4819     i = vContext->from + (100 * vContext->viewScale) - vContext->from % (100 * vContext->viewScale); /* the second label (first is at the origin) */
4820     sprintf (labelbuf, "%ld", (long)vContext->from + ssp->labelOffset);
4821     if (i - ((StringWidth (labelbuf) / 2 + 5) * vContext->viewScale) <= vContext->from + firstLabelWidth) {
4822       ssp->disableFirstLabel = TRUE;
4823     } else {
4824       ssp->disableFirstLabel = FALSE;
4825     }
4826     
4827     ssp->snt = AddSntRectangle (seg, vContext->from, yOffset,
4828                                 vContext->to + lastLabelWidth, yOffset - scaleHeight - labelLineHeight,
4829                                 ScaleSntDrawProc, (BigScalar) ssp, ScaleSntCleanupProc, 0);
4830     /*  height += scaleHeight + labelLineHeight + 4;*/
4831     yOffset = ssp->box.bottom - 1 - 15;
4832   } else {
4833     yOffset++;
4834   }
4835   yOffset -= DrawBioseqSegments (yOffset, FIP, vContext);
4836   return initialYOffset - yOffset;
4837 }
4838 
4839 /* -=-=-=-==-=-==-=-= GRAPH STUFF =-=-=-=-=-=-=-=-=-*/
4840 
4841 typedef struct gphsentdata {
4842   Boolean        flagIsGUI;
4843   SegmenT        seg;
4844   PrimitivE      snt;
4845   BoxInfo        box;
4846   Int4           min;
4847   Int4           max;
4848   Int4           axis;
4849   Int4           bottom;
4850   FloatHi        a;
4851   FloatHi        b;
4852   SeqGraphPtr    sgp;
4853   Uint1          red, green, blue;
4854 } GphSentData, PNTR GphSentPtr;
4855 
4856 static void PlotTheWorkingArray (Int4Ptr xp, Int4 len, Int4 scaleX,
4857                                  Int4 gmin, RecT r, Uint1 uR, Uint1 uG,
4858                                  Uint1 uB, AttPData PNTR curattrib)
4859 {
4860   PoinT          pt;
4861   Int4           i;
4862   Int4           j;
4863   Int4           max;
4864   Int4           min;
4865   Int4           val;
4866   Uint1          curR, curG, curB;
4867 
4868   if (curattrib != NULL)
4869   {
4870     curR = curattrib->color[0];
4871     curG = curattrib->color[1];
4872     curB = curattrib->color[2];
4873   }
4874   else
4875   {
4876     curR = 0;
4877     curG = 0;
4878     curB = 0;
4879   }
4880 
4881   SelectColor (uR, uG, uB);
4882   pt.x = r.left;
4883   i = 0;
4884   val = (Int4) *xp;
4885   pt.y = (Int2) (r.bottom - (Int2) (val - gmin));
4886   if (pt.y < r.top) {
4887     pt.y = r.top;
4888   }
4889   MoveTo (pt.x, pt.y);
4890   for (; i < len - scaleX; i += scaleX) {
4891     val = (Int4) *xp;
4892     min = (Int4) val;
4893     max = (Int4) val;
4894     for (j = 1; j < scaleX; j++) {
4895       xp++;
4896       val = (Int4) *xp;
4897       min = MIN (min, (Int4) val);
4898       max = MAX (max, (Int4) val);
4899     }
4900     xp++;
4901     pt.y = (Int2) (r.bottom - (Int2) (min - gmin));
4902     if (pt.y < r.top) {
4903       pt.y = r.top;
4904     }
4905     LineTo (pt.x, pt.y);
4906     pt.y = (Int2) (r.bottom - (Int2) (max - gmin));
4907     if (pt.y < r.top) {
4908       pt.y = r.top;
4909     }
4910     LineTo (pt.x, pt.y);
4911     (pt.x)++;
4912   }
4913   SelectColor (curR, curG, curB);
4914   return;
4915 }
4916 
4917 static Int4Ptr MakeWorkingSeqGraphInt4Array (Pointer data, Uint1 type,
4918                                              Int4 pos, Int4 len,
4919                                              Boolean usescaleflags,
4920                                              FloatHi a, FloatHi b)
4921 {
4922   Int4          i;
4923   Int4Ptr       xpoints, xp;
4924   FloatHi       fval;
4925   Int4          ival;
4926   Byte          bval;
4927   ByteStorePtr  bs;
4928   BytePtr       bp;
4929 
4930   xpoints = (Int4Ptr) MemNew ((size_t) (sizeof (Int4) * (len + 10)));
4931 
4932   if (xpoints == NULL)
4933     return xpoints;
4934 
4935   xp = xpoints;
4936   switch (type) {
4937    default:
4938    case 1:
4939      for (i = 0; i < len; i++, pos++, xp++) {
4940       if (usescaleflags) {
4941         fval = a * (FloatHi) (((FloatHiPtr) data)[pos]) + b;
4942       } else {
4943         fval = (FloatHi) (((FloatHiPtr) data)[pos]);
4944       }
4945       *xp = (Int4) fval;
4946     }
4947     break;
4948    case 2:
4949     for (i = 0; i < len; i++, pos++, xp++) {
4950       if (usescaleflags) {
4951         ival = (Int4) (a * (FloatHi) (((Int4Ptr) data)[pos]) + b);
4952       } else {
4953         ival = (Int4) (((Int4Ptr) data)[pos]);
4954       }
4955       *xp = (Int4) ival;
4956     }
4957     break;
4958    case 3:
4959     bp = MemNew (sizeof (Byte) * (len + 10));
4960     bs = (ByteStorePtr) data;
4961     BSSeek (bs, pos, SEEK_SET);
4962     len = BSRead (bs, (Pointer) bp, len * sizeof (Byte));
4963     for (i = 0; i < len; i++, pos++, xp++) {
4964       if (usescaleflags) {
4965         bval = (Byte) (a * (FloatHi) (bp [i]) + b);
4966       } else {
4967         bval = (Byte) (bp [i]);
4968       }
4969       *xp = (Int4) bval;
4970     }
4971     MemFree (bp);
4972     break;
4973   }
4974   return xpoints;
4975 }
4976 
4977 static void GphSentProc (BigScalar calldata, PrimDrawContext pdc)
4978 {
4979   BioseqPtr    bsp;
4980   Int4         curScale;
4981   DrawInfoPtr  dInfoPtr;
4982   GphSentPtr   gsp;
4983   Int4         left;
4984   Int4         len, pos;
4985   RecT         r;
4986   Int4         scaleX;
4987   Int4         scaleY;
4988   SeqGraphPtr  sgp;
4989   BoxInfo      tmpBox;
4990   Int4Ptr      xpoints;
4991   Int4         tmpscaleX;
4992 
4993   gsp = (GphSentPtr) calldata;
4994   if (gsp == NULL) return;
4995   sgp = gsp->sgp;
4996   if (sgp == NULL || sgp->values == NULL) return;
4997 
4998   dInfoPtr = (DrawInfoPtr) pdc;
4999   tmpBox = gsp->box;
5000   if ( (tmpBox.left   > dInfoPtr->scale.worldWindow.right ) ||
5001        (tmpBox.right  < dInfoPtr->scale.worldWindow.left  ) ||
5002        (tmpBox.top    < dInfoPtr->scale.worldWindow.bottom) ||
5003        (tmpBox.bottom > dInfoPtr->scale.worldWindow.top   ) )
5004     return;
5005 
5006   if ( dInfoPtr->checked == FALSE ) {
5007     if ( tmpBox.left < dInfoPtr->scale.worldWindow16.left )
5008       tmpBox.left = dInfoPtr->scale.worldWindow16.left;
5009     if ( tmpBox.right > dInfoPtr->scale.worldWindow16.right )
5010       tmpBox.right = dInfoPtr->scale.worldWindow16.right;
5011     if ( tmpBox.top > dInfoPtr->scale.worldWindow16.top )
5012       tmpBox.top = dInfoPtr->scale.worldWindow16.top;
5013     if ( tmpBox.bottom < dInfoPtr->scale.worldWindow16.bottom )
5014       tmpBox.bottom = dInfoPtr->scale.worldWindow16.bottom;
5015   }
5016   scaleX = dInfoPtr->scale.scaleX;
5017   scaleY = dInfoPtr->scale.scaleY;
5018 
5019   curScale = dInfoPtr->scale.scaleX;
5020   r.left   = (Int2)((dInfoPtr->scale.offsetX + tmpBox.left )  / curScale);
5021   r.right  = (Int2)((dInfoPtr->scale.offsetX + tmpBox.right)  / curScale);
5022   curScale = dInfoPtr->scale.scaleY;
5023   r.top    = (Int2)((dInfoPtr->scale.offsetY - tmpBox.top   ) / curScale);
5024   r.bottom = (Int2)((dInfoPtr->scale.offsetY - tmpBox.bottom) / curScale);
5025 
5026   bsp = BioseqFind (SeqLocId (sgp->loc));
5027   left = GetOffsetInBioseq (sgp->loc, bsp, SEQLOC_LEFT_END);
5028 
5029   if (gsp->flagIsGUI)
5030   {
5031     len = tmpBox.right - tmpBox.left;
5032     pos = tmpBox.left - left;
5033   }
5034   else
5035   {
5036 /* for non-GUI, plot all the sgp values */
5037     len = sgp->numval;
5038     pos = 0;
5039   }
5040 
5041   xpoints = MakeWorkingSeqGraphInt4Array (sgp->values, sgp->flags[2],
5042                                           pos, len,
5043                                           (Boolean) (sgp->flags[1] != 0),
5044                                           gsp->a, gsp->b);
5045   if (!gsp->flagIsGUI)
5046   {
5047     tmpscaleX = sgp->numval / (Int4) (tmpBox.right - tmpBox.left);
5048     if (tmpscaleX > scaleX)
5049       scaleX = tmpscaleX;
5050   }
5051 
5052   PlotTheWorkingArray (xpoints, len, scaleX, gsp->min, r,
5053                        gsp->red, gsp->green, gsp->blue,
5054                        &(dInfoPtr->curattrib));
5055 
5056   MemFree (xpoints);
5057 }
5058 
5059 static void CleanGSP (BigScalar calldata)
5060 {
5061   GphSentPtr  gsp;
5062 
5063   gsp = (GphSentPtr) calldata;
5064   MemFree (gsp);
5065 }
5066 
5067 static GphSentPtr AddGphSentinelToPicture (SeqGraphPtr sgp, BioseqPtr bsp,
5068                                            SegmenT pict, Int4 scaleX,
5069                                            Int4 top, Int2 start,
5070                                            Uint1Ptr uRGB, Boolean drawScale)
5071 {
5072   Int4        axis;
5073   GphSentPtr  gsp;
5074   Int4        i;
5075   Boolean     is_phrap;
5076   Int4        max;
5077   Int4        min;
5078   SegmenT     seg;
5079   Char        str [32];
5080   Int4        leftoff, rightoff;
5081 
5082   if (sgp == NULL || bsp == NULL || pict == NULL)
5083     return NULL;
5084   gsp = MemNew (sizeof (GphSentData));
5085   if (gsp == NULL)
5086     return NULL;
5087   gsp->flagIsGUI = VibrantIsGUI ();
5088   if (uRGB != NULL)
5089   {
5090     gsp->red   = uRGB[0];
5091     gsp->green = uRGB[1];
5092     gsp->blue  = uRGB[2];
5093   }
5094   else
5095   {
5096     gsp->red   = 0;
5097     gsp->green = 0;
5098     gsp->blue  = 0;
5099   }
5100 
5101   leftoff = GetOffsetInBioseq (sgp->loc, bsp, SEQLOC_LEFT_END);
5102   rightoff = GetOffsetInBioseq (sgp->loc, bsp, SEQLOC_RIGHT_END);
5103   if (!gsp->flagIsGUI)
5104   {
5105     leftoff /= scaleX;
5106     rightoff /= scaleX;
5107   }
5108   gsp->box.left = leftoff + start;
5109   gsp->box.right = rightoff - 1 + start;
5110 
5111   gsp->sgp = sgp;
5112   gsp->a = sgp->a;
5113   gsp->b = sgp->b;
5114   is_phrap = (Boolean) (StringICmp (sgp->title, "Phrap Quality") == 0 ||
5115                         StringICmp (sgp->title, "Phred Quality") == 0);
5116   switch (sgp->flags [2]) {
5117     case 1 :
5118       min = (Int4) sgp->min.realvalue;
5119       max = (Int4) sgp->max.realvalue;
5120       axis = (Int4) sgp->axis.realvalue;
5121       if (sgp->flags [1] != 0) {
5122         min = (Int4) (sgp->a * ((FloatHi) min) + sgp->b);
5123         max = (Int4) (sgp->a * ((FloatHi) max) + sgp->b);
5124       }
5125       break;
5126     case 2 :
5127       min = (Int4) sgp->min.intvalue;
5128       max = (Int4) sgp->max.intvalue;
5129       axis = (Int4) sgp->axis.intvalue;
5130       if (sgp->flags [1] != 0) {
5131         min = (Int4) (sgp->a * ((FloatHi) min) + sgp->b);
5132         max = (Int4) (sgp->a * ((FloatHi) max) + sgp->b);
5133       }
5134       break;
5135     case 3 :
5136       min = (Int4) sgp->min.intvalue;
5137       max = (Int4) sgp->max.intvalue;
5138       if (is_phrap) {
5139         min = MIN (0, min);
5140         max = MAX (100, max);
5141       }
5142       axis = (Int4) sgp->axis.intvalue;
5143       if (sgp->flags [1] != 0) {
5144         min = (Int4) (sgp->a * ((FloatHi) min) + sgp->b);
5145         max = (Int4) (sgp->a * ((FloatHi) max) + sgp->b);
5146       }
5147       break;
5148     default :
5149       min = (Int4) 0;
5150       max = (Int4) 100;
5151       axis = (Int4) 0;
5152       break;
5153   }
5154   gsp->seg = seg = CreateSegment (pict, 0, 0);
5155   gsp->bottom = top - (max - min) - 20;
5156 
5157   if (drawScale && sgp->title != NULL)  /* StringHasNoText -- vibforms */
5158   {
5159     if (StringLen (sgp->title) > 0)
5160     {
5161       AddLabel (seg, 
5162                 /* (gsp->box.left + gsp->box.right) / 2, */
5163                 (start + bsp->length) / 2, 
5164                 top,
5165                 sgp->title, SMALL_TEXT, 0, MIDDLE_CENTER, 0);
5166     }
5167   }
5168 
5169   top -= 10;
5170   gsp->box.top = top;
5171   gsp->box.bottom = gsp->bottom;
5172   gsp->min = min;
5173   gsp->max = max;
5174   gsp->axis = axis;
5175   gsp->bottom += 10;
5176 
5177   if (drawScale)
5178   {
5179       if (is_phrap)
5180       {
5181         for (i = 0; i <=100; i += 20) {
5182           sprintf (str, "%ld", (long) i);
5183           AddLabel (seg, gsp->box.left, gsp->bottom + i, str,
5184                     SMALL_TEXT, 5, MIDDLE_LEFT, 0);
5185         }
5186       }
5187       else
5188       {
5189         sprintf (str, "%ld", (long) max);
5190         AddLabel (seg, gsp->box.left, top-10, str,
5191                   SMALL_TEXT, 5, MIDDLE_LEFT, 0);
5192         sprintf (str, "%ld", (long) min);
5193         AddLabel (seg, gsp->box.left, gsp->bottom-10, str,
5194                   SMALL_TEXT, 5, MIDDLE_LEFT, 0);
5195         if (min < 0 && max > 0)
5196         {
5197           sprintf (str, "%ld", 0L);
5198           AddLabel (seg, gsp->box.left, gsp->bottom-min-10, str,
5199                     SMALL_TEXT, 5, MIDDLE_LEFT, 0);
5200         }
5201      }
5202   }
5203 
5204   gsp->snt = AddSntRectangle (seg, gsp->box.left, gsp->box.top,
5205                               gsp->box.right, gsp->box.bottom,
5206                               GphSentProc, (BigScalar) gsp, CleanGSP, 0);
5207   return gsp;
5208 }
5209 
5210 static void VisitAndListGraphs (SeqGraphPtr sgp, Pointer userdata)
5211 
5212 {
5213   GphSentPtr        gsp;
5214   ViewerContextPtr  vContext;
5215   Boolean           drawScale = FALSE;
5216   
5217   vContext = (ViewerContextPtr) userdata;
5218   if (vContext == NULL) return;
5219 
5220   if (vContext->gphseg == NULL) {
5221     vContext->gphseg = CreateSegment (vContext->drawOnMe, 0, 0);
5222     
5223     /* 
5224        this first time 'drawScale' stuff here and in AddGphSentinelToPicture
5225        assumes that all the graphs on this bioseq are the same type
5226        use the same legend and have the same label. 
5227     */
5228     AddSegRect (vContext->gphseg, FALSE, 0); /* draw box around all the graphs, not each one individually */
5229     drawScale = TRUE;   /* only draw scale on the first graph */
5230   }
5231 
5232   gsp = AddGphSentinelToPicture (sgp, vContext->BSP, vContext->gphseg,
5233                                  vContext->viewScale, vContext->gphyOffset,
5234                                  0, NULL, drawScale);
5235   if (gsp == NULL) return;
5236   vContext->gphheight = MAX (vContext->gphheight, gsp->box.top - gsp->box.bottom);
5237 }
5238 
5239 static Int4 DrawGraphs (
5240   Int4 yOffset,
5241   ViewerContextPtr vContext
5242 )
5243 
5244 {
5245   vContext->gphheight = 0;
5246   vContext->gphseg = NULL;
5247   vContext->gphyOffset = yOffset - 16; /* (workaround) drawing the graphs touches some pixels above gphyOffset  */
5248 
5249   VisitGraphsOnBsp (vContext->BSP, (Pointer) vContext, VisitAndListGraphs);
5250 
5251   return vContext->gphheight + 16;
5252 }
5253 
5254 static void ResetFilterState (
5255   FilterProcessStatePtr FPSP
5256 )
5257 
5258 {
5259   FilterItemPtr FIP;
5260   ViewerContextPtr vContext;
5261 
5262 
5263   if (FPSP == NULL) return;
5264   vContext = FPSP->vContext;
5265   FIP = FPSP->currentFIP;
5266   MemSet (&FPSP->state, 0, sizeof (FilterState));
5267   if (FIP->Type == FeatureFilter) {
5268     FPSP->state.feat.currentRFIPblockVNP = vContext->featVNP;
5269   }
5270 }
5271 
5272 
5273 /*
5274   Order by accession (label), strand and location
5275   for sorting.
5276 */
5277 static int LIBCALLBACK CompareAlignNameLoc(VoidPtr ptr1, VoidPtr ptr2)
5278 {
5279   SeqAlignSortInfoPtr saip1, saip2;
5280   Int2                  labelcomp;
5281   
5282   if (ptr1 != NULL && ptr2 != NULL) {
5283     saip1 = (SeqAlignSortInfoPtr) ptr1;
5284     saip2 = (SeqAlignSortInfoPtr) ptr2;
5285 
5286     labelcomp = StrNCmp(saip1->label, saip2->label, MAX_ALIGN_SORT_LABEL);
5287     if (labelcomp != 0)
5288       return labelcomp;
5289       
5290     if (saip1->strand < saip2->strand)
5291       return -1;
5292     if (saip1->strand > saip2->strand)
5293       return 1;
5294       
5295     if (saip1->start < saip2->start)
5296       return -1;
5297     if (saip1->start > saip2->start)
5298       return 1;
5299     if (saip1->stop < saip2->stop)
5300       return -1;
5301     if (saip1->stop > saip2->stop)
5302       return 1;
5303   }
5304   return 0; 
5305 }
5306 
5307 static int LIBCALLBACK ScoreCompare( VoidPtr p1, VoidPtr p2)
5308 {
5309   if (p1 != NULL  &&  p2 != NULL) {
5310     Int4 i1 = * (Int4Ptr) p1;
5311     Int4 i2 = * (Int4Ptr) p2;
5312     
5313     return i1 - i2;
5314   }
5315   return 0;
5316 }
5317 
5318 /*
5319   Take a number (rawscore) between min and max (inclusive) 
5320   and linearly scale it into a number from 1 to ACCUMVALUE_MAX/256.
5321   We divide by 256 since we will be adding these numbers together when smearing.
5322   Hopefully we won't have more than 256 with large scores at any one point  :)
5323   Do not call if min >= max.
5324 */
5325 AccumValue_t NormaliseScore(Int4 rawscore, Int4 min, Int4 max)
5326 {
5327     AccumValue_t retval;
5328     FloatHi fscore = rawscore - min;
5329     fscore *=  ACCUMVALUE_MAX / 256 - 1;
5330     fscore /= (max - min);
5331     retval = fscore + 1; 
5332     return retval;
5333 }
5334 
5335 
5336 static void FindCutoffScore(SeqAlignPtr sap, Int4 alignCnt, AlignmentFilterStatePtr afsp)
5337 {
5338   SeqAlignPtr sapIter;
5339   Int4Ptr     allScores;
5340   Int4        i;
5341   /* Int4        cutoffScore; */  /* not normalized */
5342   
5343   /* make array to hold all the scores. */
5344   allScores = MemNew(alignCnt * sizeof(Int4));
5345   if (allScores == NULL)
5346       return;
5347 
5348   /* get all the scores */
5349   i = 0;
5350   for (sapIter = sap; sapIter != NULL; sapIter = sapIter->next) {
5351     allScores[i++] = WeightFromAlignScore(sapIter, afsp->scoreType);
5352   }
5353     
5354   /* sort them */
5355   HeapSort ( allScores, alignCnt, sizeof(Int4), ScoreCompare);
5356   
5357   /* find, normalise and return the cutoffPercent score 
5358      and the min & max scores so we can normalise the rest of the scores later.
5359   */
5360   afsp->minScore = allScores[0];
5361   afsp->maxScore = allScores[alignCnt - 1];
5362   afsp->cutoffScoreHi = ACCUMVALUE_MAX; /* could add other controls to set this. */
5363 /* Cutoff by actual score, not by percentage.
5364   if (afsp->cutoffPercent == 100  ||  
5365       afsp->scoreType == NLM_SCORE_COUNT ||
5366       afsp->minScore >= afsp->maxScore ) {
5367     afsp->cutoffScore = 0;
5368   }
5369   else {
5370     cutoffScore = allScores[ (100 - afsp->cutoffPercent) * alignCnt/100 ];
5371     afsp->cutoffScore = NormaliseScore(cutoffScore, afsp->minScore, afsp->maxScore);
5372   }
5373 */  
5374   MemFree(allScores);
5375 }
5376 
5377 
5378 /* Gather all the SeqAligns whose normalised score is less than the cutoff score */
5379 static void GatherAlignInfo(
5380   SeqAlignPtr sap, 
5381   Int4        alignCnt,
5382   SeqIdPtr    bioSeqId,
5383   AlignmentFilterStatePtr afsp
5384 )
5385 {
5386   SeqAlignSortInfoPtr  infoArray = NULL;
5387   Int4        infoIndex;
5388   SeqAlignPtr sapIter;
5389   Int4        nrows, alignRow;
5390   Int4        start, stop, swap;
5391   Uint1       strand;
5392   Int4        rawScore;
5393   AccumValue_t  normScore;
5394   
5395   infoArray = MemNew(alignCnt * sizeof(SeqAlignSortInfo));
5396   if (infoArray == NULL)
5397     return;
5398   
5399   infoIndex = 0;
5400   for (sapIter = sap; sapIter != NULL; sapIter = sapIter->next) 
5401   {
5402     /* is this alignment in the top percentile? */
5403     if (afsp->scoreType != NLM_SCORE_COUNT  &&  afsp->minScore < afsp->maxScore ) {
5404       rawScore = WeightFromAlignScore(sapIter, afsp->scoreType);
5405       normScore = NormaliseScore( rawScore, afsp->minScore, afsp->maxScore);
5406   /*    if (normScore < afsp->cutoffScore ||  afsp->cutoffScoreHi < normScore) */
5407       if (rawScore < afsp->cutoffScore ||  afsp->cutoffScoreHi < rawScore)
5408       {
5409         continue;
5410       }
5411     } else {
5412       normScore = 1;
5413     }
5414     
5415     if (!AlnMgr2IndexSingleChildSeqAlign (sapIter)) continue; /* make sure we are indexed */ 
5416 
5417     /* get dimensions of this alignment (number of sequences aligned) */   
5418     nrows = AlnMgr2GetNumRows(sapIter);
5419     if (nrows < 1) {
5420       nrows = sapIter->dim; 
5421     }     
5422     if (nrows != 2) {  /* can't handle 3+ dimension alignments */
5423       continue;
5424     }
5425     
5426     /* Get the beginning and end points of this alignment in Bioseq coords. */
5427     if (sapIter->segtype != SAS_STD) {  
5428       /* not Std Seg alignment, use indexed functions */
5429       alignRow = AlnMgr2GetFirstNForSipList (sapIter, bioSeqId);
5430       if (alignRow == -1) {
5431         continue;
5432       }
5433       AlnMgr2GetNthSeqRangeInSA(sapIter, alignRow, &start, &stop);
5434       strand = AlnMgr2GetNthStrand(sapIter, alignRow);
5435     } else { 
5436       /* Std Seg alignment. Use special function */
5437       AlnMgr2GetSeqRangeForSipInSAStdSeg(sapIter, bioSeqId, &start, &stop, &strand);
5438     }
5439     if (start < 0) {
5440       continue;
5441     }
5442     if (stop < start) {
5443       swap = stop;
5444       stop = start;
5445       start = swap;
5446     }
5447 
5448     /* populate the structure we will use to sort on. */
5449     infoArray[infoIndex].start = start;
5450     infoArray[infoIndex].stop  = stop;
5451     infoArray[infoIndex].strand = strand;
5452     infoArray[infoIndex].sap   = sapIter;
5453     infoArray[infoIndex].normScore = normScore;
5454     if (!SeqAlignContentLabel(sapIter, bioSeqId, infoArray[infoIndex].label, MAX_ALIGN_SORT_LABEL, PRINTID_TEXTID_ACC_VER)) {
5455       infoArray[infoIndex].label[0] = 0;
5456     }
5457     ++infoIndex;
5458   }
5459   
5460   afsp->alignSortedLen = infoIndex;
5461     
5462   if (infoIndex == 0) {
5463     MemFree(infoArray);
5464     infoArray = NULL;
5465   }
5466   afsp->alignSorted = infoArray;
5467 }
5468 
5469 static Boolean AlignmentFilterStateInit(
5470     SeqAlignPtr sap,                /* the head of hte chain of sequence alignments from the bioseq */
5471     SeqIdPtr sid,                   /* the id of the bioseq so we know how we are viewing the alignments */
5472     AlignmentFilterStatePtr afsp,   /* What we are initializing. */
5473     GraphicViewExtrasPtr extras     /* contains the score cuttoff percentage and the kind of scores we use */
5474 )
5475 {  
5476   SeqAlignPtr   sapIter;
5477   Int4  alignCnt;
5478   Uint1 scoreType = NLM_SCORE_COUNT;
5479   Int2  i = 0;
5480   
5481   if (sap == NULL ||  sid == NULL  ||  afsp == NULL)
5482     return FALSE;
5483     
5484   /* what kind of score will we use to weight the alignments? */
5485   if (extras  &&  extras->alignScoreName) {
5486     scoreType = StringIndexInStringList (extras->alignScoreName, AlnScoreStrings);
5487     if (scoreType >= NLM_SCORE_TOOBIG)
5488         scoreType = NLM_SCORE_COUNT;
5489   }
5490   afsp->scoreType = scoreType;
5491   
5492   /* what percentile does an alignments score have to be to be displayed? */
5493   if (extras  && extras->alignScoreCutoff) {
5494     i = StringIndexInStringList (extras->alignScoreCutoff, AlnScoreCutoffStrings);
5495     if (i < 0  ||  DIM (AlnScoreCutoffValues) <= i )
5496         i = 0;
5497   }
5498     /*  afsp->cutoffPercent = AlnScoreCutoffValues[i]; */
5499   if (AlnScoreCutoffValues[i] < 0) {
5500     afsp->scoreType = NLM_SCORE_COUNT;
5501   } else {
5502     afsp->scoreType = NLM_SCORE_BIT;
5503     afsp->cutoffScore = AlnScoreCutoffValues[i];
5504   }
5505   
5506   /* how many alignments? Count one time now */
5507   alignCnt = 0;
5508   for (sapIter = sap; sapIter != NULL; sapIter = sapIter->next) {
5509     ++alignCnt;
5510   }
5511 
5512   FindCutoffScore(sap, alignCnt, afsp);
5513   
5514   GatherAlignInfo(sap, alignCnt, sid, afsp);
5515   if (afsp->alignSorted == NULL || afsp->alignSortedLen == 0) {
5516     /* couldn't allocate memory, or no alignments - nothing to do. */
5517     return FALSE;
5518   }
5519   /* sort the alignments, first by accession, then by location on the bioseq. */
5520   HeapSort(afsp->alignSorted, afsp->alignSortedLen, sizeof(SeqAlignSortInfo), CompareAlignNameLoc);
5521   afsp->alignIndex = 0;
5522   
5523   return TRUE;
5524 }
5525 
5526 static Boolean AlignmentFilterStateDone(AlignmentFilterStatePtr afsp)
5527 {
5528   if (afsp->alignSorted != NULL &&
5529       afsp->alignIndex < afsp->alignSortedLen)
5530     return FALSE;
5531   return TRUE;
5532 }
5533 
5534 
5535 static void AlignmentFilterStateFree( AlignmentFilterStatePtr afsp)
5536 {
5537   if (afsp->alignSorted != NULL)
5538     MemFree(afsp->alignSorted);
5539   afsp->alignSorted = NULL;
5540   afsp->alignSortedLen = 0;
5541 }
5542 
5543 
5544   
5545 static Int4 WeightFromAlignScore(SeqAlignPtr sap, Uint1 scoreType)
5546 {
5547   Int4        weight = 1;
5548   Int4        score, number;
5549   Nlm_FloatHi bit_score, evalue;
5550   
5551   if (GetScoreAndEvalue(sap, &score, &bit_score, &evalue, &number)) {
5552     /* evaluate scores and get weight */
5553     switch (scoreType) {
5554     case NLM_SCORE_SCORE :
5555       weight = score;
5556       break;
5557     case NLM_SCORE_BIT :
5558       weight = bit_score;
5559       break;
5560     case NLM_SCORE_EVALUE :
5561       if (evalue > 0) {
5562         weight = -log(evalue);
5563       }
5564       else {  /* 0 is the best value. */
5565         /*
5566             We can't use the maxScore here since we do not know it yet
5567             since this is where we collect the values.
5568             Our sample data has -log(evalue) ranging from 25 - 429
5569             so 500 just has to be bigger than anything else we will encounter.
5570             It will get normalised latter with it as the maxScore.
5571             
5572             We could put a special sentinel value (very large or negative) here 
5573             that gets replaced with the real maxScore later. 
5574             (But then make sure it gets skipped when calculating the max and min).
5575         */
5576         weight = 500;
5577       }
5578       break;
5579     case NLM_SCORE_NUMBER :
5580       weight = number;
5581       break;
5582     case NLM_SCORE_COUNT :
5583     default:
5584       weight = 1;
5585     }
5586   }
5587   
5588   return weight;
5589 }
5590 
5591 
5592 /*
5593   Interval Accumulator
5594   An object which can add or in some way (specified by accumop) intervals on a sequence,
5595   then from which can be extracted the resulting intervals.
5596 */
5597 
5598 #define ACCUMVALUE_GAP  -1
5599 
5600 typedef enum AccumlatorOp {
5601  NLM_SUM_WEIGHT = 0,
5602  NLM_MAX_WEIGHT
5603 } AccumulatorOp;
5604 
5605 
5606 typedef struct AccumNode_s {
5607   Uint4         coord;
5608   AccumValue_t  weight;
5609   struct AccumNode_s * next;  /* makes a single-linked list. */
5610 } AccumNode, *AccumNodePtr;
5611 
5612 
5613 static AccumNodePtr
5614 InsertNodeAfter(AccumNodePtr node, Uint4 coord, AccumValue_t weight)
5615 {
5616   AccumNodePtr new_node = (AccumNodePtr) Nlm_MemNew(sizeof(AccumNode));
5617   if (new_node == NULL) return NULL;
5618   
5619   new_node->coord = coord;
5620   new_node->weight = weight;
5621   if (node != NULL) {
5622     new_node->next = node->next;
5623     node->next = new_node;
5624   }
5625   return new_node;
5626 }
5627 
5628 static AccumNodePtr
5629 AppendNode(AccumNodePtr head, Uint4 coord, AccumValue_t weight)
5630 {
5631   while (head->next != NULL)
5632     head = head->next;
5633   return InsertNodeAfter(head, coord, weight);
5634 }
5635 
5636 static void 
5637 FreeAccumNodes(AccumNodePtr node)
5638 {
5639   AccumNodePtr del_me;
5640   while (node) {
5641     del_me = node;
5642     node = node->next;
5643     Nlm_MemFree(del_me);
5644   }
5645 }
5646 
5647 static void
5648 AccumulateWeight(AccumNodePtr node, AccumValue_t w2, AccumulatorOp op)
5649 {
5650   AccumValue_t w1;
5651   
5652   if (w2 == 0)
5653     return;
5654   
5655   w1 = node->weight;
5656   
5657   /* gap trumps 0, any weight trumps a gap. */
5658   if ((w1 == ACCUMVALUE_GAP  &&  w2 <= 0)  ||
5659       (w2 == ACCUMVALUE_GAP  &&  w1 <= 0)) {
5660     node->weight = ACCUMVALUE_GAP;
5661   } else if (w1 == ACCUMVALUE_GAP){
5662     node->weight = w2; 
5663   } else if (w2 == ACCUMVALUE_GAP){
5664     node->weight = w1; 
5665   } else if (op  == NLM_MAX_WEIGHT) {
5666     /* max of the weights. */
5667     if (w2 > w1)
5668       node->weight = w2;
5669   } else {
5670     /* NLM_SUM_WEIGHT */
5671     if (node->weight < ACCUMVALUE_MAX - w2) /* avoid overflow. */
5672       node->weight += w2;
5673     else
5674       node->weight = ACCUMVALUE_MAX;
5675   }
5676 }
5677 
5678 
5679 typedef struct IntervalAccumulator {
5680   AccumNodePtr        nodes;
5681   AccumulatorOp       accumOp;
5682   AccumValue_t        maxWeight;
5683 } IntervalAccumulator, PNTR IntervalAccumulatorPtr;
5684 
5685 
5686 static IntervalAccumulatorPtr
5687 NewIntervalAccumulator(Uint4 len, AccumulatorOp op)
5688 {
5689   AccumNodePtr tail_node;
5690   IntervalAccumulatorPtr IAP;
5691 
5692   IAP = (IntervalAccumulatorPtr) Nlm_MemNew(sizeof(IntervalAccumulator));
5693   if (IAP == NULL) return NULL;
5694   IAP->nodes = InsertNodeAfter(NULL, 0, 0); /* header node. coord 0. */
5695   if (IAP->nodes == NULL) {
5696     Nlm_MemFree(IAP);
5697     return NULL;
5698   }
5699   tail_node = AppendNode(IAP->nodes, len, 0); /* tail node. max coord. */
5700   if (tail_node == NULL) {
5701     FreeAccumNodes(IAP->nodes);
5702     Nlm_MemFree(IAP);
5703     return NULL;
5704   }
5705   IAP->accumOp = op;
5706   return IAP;
5707 }
5708 
5709 static void
5710 FreeIntervalAccumulator(IntervalAccumulatorPtr *iapp)
5711 {
5712   IntervalAccumulatorPtr IAP;
5713 
5714   if (iapp == NULL || *iapp == NULL) return;
5715   IAP = *iapp;
5716   FreeAccumNodes(IAP->nodes);
5717   Nlm_MemFree(IAP);
5718   *iapp = NULL;
5719 }
5720 
5721 
5722 /* merge in a list of segments. */
5723 static void
5724 AccumSegments(IntervalAccumulatorPtr accum, AccumNodePtr new_nodes)
5725 {
5726 
5727     AccumNodePtr prev_node = accum->nodes;     /* add our node right before this one. */
5728   AccumValue_t old_weight = 0;    /* the weight of the last old node passed. */
5729     AccumValue_t in_weight = 0;         /* the weight of the last new node entered. */
5730     
5731     AccumNodePtr in_node;
5732     
5733     in_node = new_nodes;
5734   /* skip header node in the input, if we have one. */
5735     if (in_node->weight == 0  &&  in_node->coord == 0)
5736       in_node = in_node->next;
5737         
5738   for (; in_node != NULL; in_node = in_node->next) {
5739     
5740         /* we are adding in_node now. */
5741         
5742         /* Find where to insert in_node.  */
5743         /* while extending the range of the last node added. */
5744         while (prev_node->next != NULL  && 
5745                in_node->coord > prev_node->next->coord) {
5746             prev_node = prev_node->next;
5747             old_weight = prev_node->weight;
5748             AccumulateWeight(prev_node, in_weight, accum->accumOp); 
5749         }
5750         in_weight = in_node->weight;
5751 
5752         /* insert at node. */
5753         if (in_node->coord == prev_node->next->coord) {
5754           prev_node = prev_node->next;
5755             old_weight = prev_node->weight;
5756             AccumulateWeight(prev_node, in_weight, accum->accumOp); 
5757         } else {
5758           AccumulateWeight(in_node, old_weight, accum->accumOp);
5759           InsertNodeAfter(prev_node, in_node->coord, in_node->weight);
5760           prev_node = prev_node->next; /* skip the one we just inserted. */
5761         } 
5762     }
5763     
5764     ASSERT(in_weight > 0);
5765   /* extend last range to the end of the accumulator. 
5766     if (in_weight > 0) {
5767         while (prev_node->next != NULL) {
5768             prev_node = prev_node->next;
5769             AccumulateWeight(prev_node, in_weight, accum->accumOp); 
5770         }
5771     }
5772     */
5773 }
5774 
5775 
5776 
5777 
5778 /* Retrieve information. */
5779 
5780 static AccumValue_t
5781 GetMaxWeight(const IntervalAccumulatorPtr IAP) 
5782 {
5783   AccumNodePtr node; 
5784   AccumValue_t max_weight = 0;
5785 
5786     for (node = IAP->nodes->next; /* skip the header node. */
5787          node->next != NULL;  /* skip the last (dummy) node. */
5788          node = node->next) { 
5789     if (max_weight < node->weight)
5790       max_weight = node->weight;
5791     }
5792     return max_weight;
5793 }
5794 
5795 
5796 static AccumNodePtr
5797 GetIntervalFromAccumulator(
5798   IntervalAccumulatorPtr iap, 
5799   AccumNodePtr nextPos, 
5800   Uint4Ptr startp, 
5801   Uint4Ptr stopp, 
5802   AccumValuePtr_t weightp
5803 )
5804 {
5805   Uint4             start, stop;
5806   AccumValue_t      weight = 0;
5807   
5808   if (NULL == iap  &&  nextPos == NULL) return NULL;
5809 
5810   if (iap  &&  nextPos == NULL)
5811     nextPos = iap->nodes->next;  /* skip leading 0,0 node. */
5812   
5813   /* are we at the end? */
5814   if (nextPos == NULL  ||  nextPos->next == NULL)  /* nextPos == NULL would be an error. */
5815     return NULL;
5816   
5817   start = nextPos->coord;
5818   weight = nextPos->weight;
5819   
5820   /* go to the next node. */
5821   nextPos = nextPos->next;
5822   /* skip redundant nodes. Those with the same weight as the preceding one. */
5823   while (nextPos->next  &&  nextPos->weight == weight)
5824     nextPos = nextPos->next;
5825   stop = nextPos->coord;
5826   
5827   if (startp) *startp = start;
5828   if (stopp)  *stopp  = stop;
5829   if (weightp) *weightp = weight;
5830   
5831   return nextPos;
5832 }
5833 
5834 
5835 
5836 static Uint2 SmearAlignments (
5837   FilterProcessStatePtr FPSP,
5838   ViewerContextPtr vContext  
5839 )
5840 
5841 {
5842   SeqAlignPtr              SAP;
5843   BioseqPtr                BSP;
5844   SeqIdPtr                 SID;
5845   AppearanceItemPtr        AIP;
5846   FilterItemPtr            FIP;
5847   
5848   AlignmentFilterStatePtr  alignSP;
5849   SeqAlignSortInfoPtr      alignSorted;
5850   Int4                     alignSortedLen;
5851   Int4                     alignIndex;
5852   Int4                     start, stop, maxDensity;
5853   Uint1                    color[3], col;
5854   Uint1                    minCol = 224; /* density == min -> light gray */
5855   Uint1                    maxCol = 0;   /* density == max -> black */
5856   
5857   IntervalAccumulatorPtr   plusIAP = NULL, minusIAP = NULL;  /* Smear accumulators */
5858   AccumNodePtr             thisAlignSegs = NULL;  /* input to the accumulators per alignment */
5859   AccumNodePtr             accumPos;              /* output iterator on the accumulators. */
5860   Uint4                    begin, end;
5861   Int4                     space_pixs;
5862   AccumValue_t             weight;
5863   AccumValue_t             density;
5864 
5865   Boolean                  smearedAlignsPlus = FALSE, smearedAlignsMinus = FALSE;
5866   AccumulatorOp            sumOrMax = NLM_SUM_WEIGHT;
5867   Boolean                  separateStrands;
5868   const Uint1              minDensity = 1;  /* minimum density we will display. */
5869   SegmenT                  seg;
5870   CharPtr                  annotName;
5871   Int4                     height = 0;
5872   Uint2                    space_line_height = 2;
5873   
5874   if (FPSP == NULL || vContext == NULL) return 0;
5875   alignSP = &FPSP->state.align;
5876   alignSorted  = alignSP->alignSorted;
5877   alignSortedLen = alignSP->alignSortedLen;
5878  
5879    /* get the Appearance item for alignments */
5880   AIP = vContext->AppPtr->FeaturesAppearanceItem[APPEARANCEITEM_Alignment];
5881 
5882   /* get the bioseq's id for picking out the right part of the alignments */
5883   BSP = vContext->BSP;
5884   SID = BSP->id;
5885    
5886   /* Get this annotation's name to decide how to display this. */ 
5887   annotName = GetSeqAnnotName(vContext->currentSAP);
5888   separateStrands = FALSE;
5889   if (StringStr(annotName, "BLASTX - swissprot"))
5890     separateStrands = TRUE;
5891   else if (StringStr(annotName, "BLASTN - mrna"))
5892     separateStrands = TRUE;
5893   else if (StringStr(annotName, "BLASTN - est"))
5894     separateStrands = FALSE;
5895   else if (StringStr(annotName, "BLASTN - nr"))
5896     separateStrands = FALSE;
5897 
5898   plusIAP =  NewIntervalAccumulator(vContext->seqLength, sumOrMax);
5899   if (plusIAP == NULL)  goto smearAlignmentsDone;
5900   minusIAP = NewIntervalAccumulator(vContext->seqLength, sumOrMax);
5901   if (minusIAP == NULL)  goto smearAlignmentsDone;
5902     
5903   /* 
5904     for all the sorted alignments (from a single annotation in this BioSeq)
5905     treat all alignments with the same accession as one alignment
5906   */   
5907   for (alignIndex = 0; 
5908        alignIndex < alignSortedLen; 
5909        ++alignIndex) 
5910   {
5911     AlignSegIterator asi;
5912     Int4  nsegs;
5913     Uint1 segType;
5914     Uint1 strand;
5915     
5916     SAP = alignSorted[alignIndex].sap;
5917     
5918     /* sanity checks */
5919     if (alignSorted[alignIndex].start < 0 || alignSorted[alignIndex].stop < 0)
5920       continue;
5921       
5922     weight = alignSorted[alignIndex].normScore;
5923     /* if (weight == 0)
5924       continue;
5925     */  
5926     /*
5927       at the begining of an alignment
5928       if there was another alignment before this one, 
5929       with the same accession, strand and that alignment ended before this one begins, 
5930       treat that space as a gap.
5931     */
5932     if (alignIndex > 0 && 
5933         StrNCmp(alignSorted[alignIndex - 1].label, alignSorted[alignIndex].label, MAX_ALIGN_SORT_LABEL) == 0 &&
5934         alignSorted[alignIndex - 1].strand == alignSorted[alignIndex].strand &&
5935         alignSorted[alignIndex - 1].stop < alignSorted[alignIndex].start)
5936     {
5937       start = alignSorted[alignIndex - 1].stop;
5938       AppendNode(thisAlignSegs, start, ACCUMVALUE_GAP );
5939     } else {
5940       /* we are starting a new alignment.  */
5941       /* Smear the last alignment's segments. */
5942       if (alignIndex > 0) {
5943         AppendNode(thisAlignSegs, alignSorted[alignIndex - 1].stop, 0);
5944         if (alignSorted[alignIndex - 1].strand != Seq_strand_minus || !separateStrands )
5945           AccumSegments(plusIAP, thisAlignSegs);
5946         else
5947           AccumSegments(minusIAP, thisAlignSegs);
5948       }
5949       
5950       /* Prepare space for the new alignment's segment info.  */
5951       FreeAccumNodes(thisAlignSegs);
5952       thisAlignSegs = InsertNodeAfter(NULL, 0, 0);
5953     }
5954         
5955     /*
5956       for each segment in an alignment
5957       if the segment is block or a gap map it appropriately in the density arrays.
5958     */ 
5959     nsegs = AlignSegIteratorCreate(SAP, SID, &asi);
5960     if (nsegs == 0)
5961       continue;
5962     while (AlignSegIteratorNext(&asi, &start, &stop, &strand, &segType))
5963     {
5964       if (segType == AM_INSERT || start < 0 || stop < 0)
5965         continue;
5966         
5967       if (segType == AM_GAP ) {  /* a gap */
5968         AppendNode(thisAlignSegs, start, ACCUMVALUE_GAP );
5969       }
5970       else if (segType == AM_SEQ) { /* a real alignment. */
5971         AppendNode(thisAlignSegs, start, weight );
5972         if (strand != Seq_strand_minus || !separateStrands) {
5973           smearedAlignsPlus = TRUE;
5974         } else {
5975           smearedAlignsMinus = TRUE;
5976         }
5977       }
5978   
5979     } /* for all segments in an alignment */
5980   } /* for all alignments in an annotation */
5981   
5982     /* Smear the last alignment's segments. */
5983    if (alignIndex > 0) {
5984     AppendNode(thisAlignSegs, alignSorted[alignIndex - 1].stop, 0);
5985     if (alignSorted[alignIndex - 1].strand != Seq_strand_minus || !separateStrands )
5986       AccumSegments(plusIAP, thisAlignSegs);
5987     else
5988       AccumSegments(minusIAP, thisAlignSegs);
5989   }
5990 
5991   if (!smearedAlignsPlus && !smearedAlignsMinus) 
5992     goto smearAlignmentsDone; /* there was nothing to show. */
5993   
5994   DrawNamedAnnotBox(FPSP);
5995   FIP = (FilterItemPtr) FPSP->currentFilterVNP->data.ptrvalue;
5996   DrawFilterItemBoxLabel(FPSP, FIP);
5997   
5998   seg = CreateSegment ( vContext->drawOnMe, 0, 0);
5999 
6000   if (smearedAlignsPlus || !separateStrands) {
6001     height += space_line_height;
6002     maxDensity = GetMaxWeight(plusIAP);
6003 
6004     accumPos = GetIntervalFromAccumulator(plusIAP, NULL, &begin, &end, &density);
6005     while (accumPos != NULL) {
6006     
6007       if (density > 0  && density != ACCUMVALUE_GAP) {
6008           /* convert  (1 - maxDensity) to "color" number (224 - 0) */
6009         if (maxDensity == minDensity) {
6010           col = minCol;
6011         } else {
6012           col = (Uint1) (minCol + (density - minDensity)*(maxCol - minCol)/(maxDensity - minDensity));
6013         }
6014         color [2] = color [1] = color [0] = col;  /* set to shade of grey. (minCol = 224) */
6015         AddAttribute (seg, COLOR_ATT, color, 0, 0, 1, 0);
6016         AddRectangle (seg, begin, FPSP->ceiling - height, 
6017                            end,   FPSP->ceiling - height - (AIP->Height), NO_ARROW, TRUE, 0);
6018       }
6019       else if (density == ACCUMVALUE_GAP) {
6020         AddAttribute (seg, COLOR_ATT, NULL, 0, 0, 1, 0);
6021         AddLine (seg, begin, FPSP->ceiling - height - (AIP->Height)/2, 
6022                       end,   FPSP->ceiling - height - (AIP->Height)/2, NO_ARROW, 0);
6023       } else { /* density == 0 */
6024         /* put a small line above spaces between blocks we draw */
6025         if (0 < begin  &&  end < vContext->seqLength) { /* ignore gaps at beginning and end of bioseq */
6026           ASSERT(density == 0);
6027           space_pixs = (end - begin)/vContext->viewScale;
6028           /* if (3 <= space_pixs  &&  space_pixs <= 10  &&  end < vContext->seqLength) { */
6029           if (space_pixs <= 20) { /* don't draw this line if it is more than 20 pixels long. */
6030             if (space_pixs < 3) { /* make sure bar is always at least 3 pixels long */
6031               int mid_point = end + begin;
6032               end = (mid_point + 3 * vContext->viewScale) /2;
6033               begin = (mid_point - 3 * vContext->viewScale) /2;
6034             }
6035             AddAttribute (seg, COLOR_ATT, NULL, 0, 0, 1, 0);
6036             AddLine (seg, begin,                     FPSP->ceiling + space_line_height, 
6037                           end - vContext->viewScale, FPSP->ceiling + space_line_height, NO_ARROW, 0);
6038           }
6039         }
6040       }
6041       accumPos = GetIntervalFromAccumulator(NULL, accumPos, &begin, &end, &density);
6042     }
6043     /* put a little arrow to show this is the plus strand */
6044     if (separateStrands) {
6045       AddAttribute (seg, COLOR_ATT, NULL, 0, 0, 1, 0);
6046       AddTextLabel (seg, 0 * vContext->viewScale, FPSP->ceiling - height - (AIP->Height)/2, 
6047                    ">", FIP->GroupLabelFont, 1, MIDDLE_LEFT, 0);
6048     }
6049     height += AIP->Height + FIP->IntraRowPaddingPixels;
6050   }
6051   
6052   if (smearedAlignsMinus) {
6053     maxDensity = GetMaxWeight(minusIAP);
6054 
6055     accumPos = GetIntervalFromAccumulator(minusIAP, NULL, &begin, &end, &density);
6056     while (accumPos != NULL) {
6057     
6058       if (density > 0  && density != ACCUMVALUE_GAP) {
6059           /* convert  (1 - maxDensity) to "color" number (224 - 0) */
6060         if (maxDensity == minDensity) {
6061           col = minCol;
6062         } else {
6063           col = (Uint1) (minCol + (density - minDensity)*(maxCol - minCol)/(maxDensity - minDensity));
6064         }
6065         color [2] = color [1] = color [0] = col;  /* set to shade of grey. (minCol = 224) */
6066         AddAttribute (seg, COLOR_ATT, color, 0, 0, 1, 0);
6067         AddRectangle (seg, begin, FPSP->ceiling - height, 
6068                            end,   FPSP->ceiling - height - (AIP->Height), NO_ARROW, TRUE, 0);
6069       }
6070       else if (density == ACCUMVALUE_GAP) {
6071         AddAttribute (seg, COLOR_ATT, NULL, 0, 0, 1, 0);
6072         AddLine (seg, begin, FPSP->ceiling - height - (AIP->Height)/2, 
6073                       end,   FPSP->ceiling - height - (AIP->Height)/2, NO_ARROW, 0);
6074       } else { 
6075         /* put a small line above spaces between blocks we draw */
6076         if (0 < begin  &&  end < vContext->seqLength) { /* ignore gaps at beginning and end of bioseq */
6077           ASSERT(density == 0);
6078           space_pixs = (end - begin)/vContext->viewScale;
6079           /* if (3 <= space_pixs  &&  space_pixs <= 10  &&  end < vContext->seqLength) { */
6080           if (space_pixs <= 20) { /* don't draw this line if it is more than 20 pixels long. */
6081             if (space_pixs < 3) { /* make sure bar is always at least 3 pixels long */
6082               int mid_point = end + begin;
6083               end = (mid_point + 3 * vContext->viewScale) /2;
6084               begin = (mid_point - 3 * vContext->viewScale) /2;
6085             }
6086             AddAttribute (seg, COLOR_ATT, NULL, 0, 0, 1, 0);
6087             AddLine (seg, begin,                     FPSP->ceiling - height - (AIP->Height) - space_line_height - 1, 
6088                           end - vContext->viewScale, FPSP->ceiling - height - (AIP->Height) - space_line_height - 1, NO_ARROW, 0);
6089           }
6090         }
6091       }
6092       accumPos = GetIntervalFromAccumulator(NULL, accumPos, &begin, &end, &density);
6093     }
6094     AddAttribute (seg, COLOR_ATT, NULL, 0, 0, 1, 0);
6095     AddTextLabel (seg, 0, FPSP->ceiling - height - (AIP->Height)/2, 
6096                  "<", FIP->GroupLabelFont, 1, MIDDLE_LEFT, 0);
6097 
6098     height += space_line_height;
6099     height += AIP->Height + FIP->IntraRowPaddingPixels;
6100   }
6101     
6102 smearAlignmentsDone:
6103 
6104   FreeIntervalAccumulator(&plusIAP);
6105   FreeIntervalAccumulator(&minusIAP);
6106 
6107   return height;
6108 }
6109 
6110 
6111 static Boolean FilterAndLayout (
6112   ViewerContextPtr vContext
6113 )
6114 
6115 {
6116   FilterProcessState  FPS;
6117   FilterItemPtr       FIP;
6118   FilterPtr           FP;
6119   AppearancePtr       AP;
6120   FilterItem          tempFI;
6121   Int1                featdeftype;
6122   LayoutAlgorithm     layoutC;
6123   Int4                height, totalheight;
6124   SegmenT             filterSeg, invisibleSeg;
6125   Uint1               featdefOrder;
6126   Boolean             emptyFilterGroup;
6127   Int4                undoCeiling;
6128   BioseqPtr           BSP;
6129   SeqAnnotPtr         SAnnP;
6130   SeqAlignPtr         SAlnP;
6131 
6132 
6133   if (vContext == NULL) return FALSE;
6134   MemSet (&FPS, 0, sizeof (FilterProcessState));
6135   FPS.vContext = vContext;
6136   if (vContext->ceiling != NULL) {
6137     FPS.ceiling = *vContext->ceiling;
6138   } else {
6139     FPS.ceiling = 0;
6140   }
6141   FPS.featuresProcessed = MemNew (vContext->featureCount * sizeof (Boolean));
6142   if (FPS.featuresProcessed == NULL && vContext->featureCount != 0) return FALSE;
6143   AP = vContext->AppPtr;
6144   vContext->sanLevelSeg = CreateSegment (vContext->topLevelSeg, 0, 0);
6145   FPS.annotBoxDrawn = FALSE;
6146   FPS.annotLabelDrawn = FALSE;
6147 
6148   FP = vContext->FltPtr;
6149   if (! FP->AnnotBoxColorSet) {
6150     MemCopy (FP->AnnotBoxColor, AP->AnnotBoxColor, sizeof (Uint1 [3]));
6151   }
6152   if (! FP->AnnotLabelColorSet) {
6153     MemCopy (FP->AnnotLabelColor, AP->AnnotLabelColor, sizeof (Uint1 [3]));
6154   }
6155   if (! FP->AnnotLabelFontSet) {
6156     FP->AnnotLabelFont = AP->AnnotLabelFont;
6157   }
6158 
6159   for (FPS.currentFilterVNP = FP->FilterItemList;
6160        FPS.currentFilterVNP != NULL;
6161        FPS.currentFilterVNP = FPS.currentFilterVNP->next) {
6162 
6163     if (FPS.currentFilterVNP->data.ptrvalue == NULL) continue; /* this should not happen if config file parsing worked */
6164 
6165     filterSeg = CreateSegment (vContext->sanLevelSeg, 0, 0);
6166     MemSet (FPS.labelSegs, 0, sizeof (FPS.labelSegs));
6167     MemSet (FPS.drawSegs, 0, sizeof (FPS.drawSegs));
6168     vContext->drawOnMe = filterSeg;
6169     emptyFilterGroup = TRUE;
6170     undoCeiling = FPS.ceiling;
6171     FPS.featuresProcessedCount = 0;
6172     FPS.groupBoxDrawn = FALSE;
6173     FPS.groupLabelDrawn = FALSE;
6174 
6175     FIP = (FilterItemPtr) FPS.currentFilterVNP->data.ptrvalue;
6176 
6177     if (! FIP->GroupBoxColorSet) {
6178       MemCopy (FIP->GroupBoxColor, AP->GroupBoxColor, sizeof (Uint1 [3]));
6179     }
6180     if (! FIP->GroupLabelColorSet) {
6181       MemCopy (FIP->GroupLabelColor, AP->GroupLabelColor, sizeof (Uint1 [3]));
6182     }
6183     if (! FIP->GroupLabelFontSet) {
6184       FIP->GroupLabelFont = AP->GroupLabelFont;
6185     }
6186 
6187     switch (FIP->Type) {
6188       case InvalidFilter:
6189         break;
6190       case BioseqFilter:
6191         if (vContext->currentSAP) 
6192           break;
6193         height = DrawBioseq (FPS.ceiling, FIP, vContext);
6194         emptyFilterGroup = FALSE;
6195         break;
6196       case GraphFilter:
6197         if (vContext->currentSAP) 
6198           break;
6199         height = DrawGraphs (FPS.ceiling, vContext);
6200         if (height != 0) {
6201           emptyFilterGroup = FALSE;
6202         }
6203         break;
6204       case AlignmentFilter:
6205         BSP = vContext->BSP;
6206         height = 0;
6207         totalheight = 0;
6208         layoutC = (vContext->overrideLayout != Layout_Inherit) ? vContext->overrideLayout : FIP->LayoutChoice;
6209         FPS.currentFIP = FIP;
6210         
6211         /*
6212          *  Or we could move the current SAnnP into FPS.state.align and put all of this logic
6213          *  into GetNextRFIPinAlignmentFilter() which SmearAlignments would call, making Alignment
6214          *  processing more like Feature processing.
6215          */
6216         for (SAnnP = BSP->annot; SAnnP != NULL; SAnnP = SAnnP->next) 
6217         {
6218           if (SAnnP->type != 2) 
6219             continue;  /* type 2 annotation is an alignment */
6220           
6221           if (FP->GroupByAnnot) /* are we grouping by named annotations? */
6222           {
6223             if (GetSeqAnnotName(SAnnP)) /* this is a named annotation */
6224             {
6225               if (SAnnP != vContext->currentSAP) /* only do the named annotation we are currently doing. */
6226                 continue;
6227             }
6228             else if (vContext->currentSAP) /* don't do any unnamed annotation if we are doing a particular named one. */
6229               continue;
6230           }
6231                    
6232           emptyFilterGroup = FALSE;
6233           SAlnP = (SeqAlignPtr) SAnnP->data;
6234                     
6235           ResetFilterState (&FPS);
6236           if ( ! AlignmentFilterStateInit(SAlnP, BSP->id ,&FPS.state.align, vContext->extras))
6237             continue;
6238 
6239           switch (layoutC) {
6240             case Layout_FeatTypePerLine:
6241               height = SmearAlignments (&FPS, vContext);
6242               break;
6243             default:
6244               height = ProcessRows (layoutC, &FPS, vContext);
6245               break;
6246           }
6247           AlignmentFilterStateFree(&FPS.state.align);
6248           
6249           totalheight += height;
6250           if (height > 0)
6251             FPS.ceiling -= height;
6252         } /* SAnnp, cycle through all SeqAnnots on this Bioseq */
6253         height = 0;
6254         break;
6255       case FeatureFilter:
6256         layoutC = (vContext->overrideLayout != Layout_Inherit) ? vContext->overrideLayout : FIP->LayoutChoice;
6257         /*
6258           Some layouts act to the user as if they are single FilterGroups, but internally use multiple
6259           consecutive FilterGroups
6260           (currently, Layout_FeatTypePerLine, Layout_FeatTypePerLineGroup, Layout_GroupCorrespondingFeats, and Layout_GroupCorrespondingFeatsRepeat).
6261         */
6262         /*
6263           FeatTypePerLineGroup is equiv. to using PackUpwards several times, with single-feature filteritems
6264           FeatTypePerLine is similar (but using AllInOneLine)
6265         */
6266         switch (layoutC) {
6267           case Layout_FeatTypePerLine:
6268           case Layout_FeatTypePerLineGroup:
6269             FPS.currentFIP = &tempFI;
6270             MemCopy (&tempFI, FIP, sizeof (FilterItem)); /* copy the filter . . . */
6271             MemSet (&tempFI.IncludeFeature, 0, sizeof (tempFI.IncludeFeature));  /* but don't include any features */
6272             tempFI.AddTypeToLabel = TristateFalse;
6273             for (featdefOrder = 1; featdefOrder < APPEARANCEITEM_MAX; featdefOrder++) {
6274               for (featdeftype = 1; featdeftype < APPEARANCEITEM_MAX; featdeftype++) {
6275                 if (FIP->IncludeFeature [featdeftype] == featdefOrder) {
6276                   ResetFilterState (&FPS);
6277                   tempFI.IncludeFeature[featdeftype] = TRUE;
6278                   height = ProcessRows (layoutC, &FPS, vContext);
6279                   if (FPS.featuresProcessedCount != 0) {
6280                     emptyFilterGroup = FALSE;
6281                   }
6282                   FPS.ceiling -= height;
6283                   tempFI.IncludeFeature[featdeftype] = FALSE;
6284                 }
6285               }
6286             }
6287             height = 0; /* prevent FPS.ceiling from being bumped again */
6288             break;
6289           case Layout_GroupCorrespondingFeats:
6290           case Layout_GroupCorrespondingFeatsRepeat:
6291             /*
6292               This uses 3 FilterItems:
6293                 - All gene features (compact)
6294                 - CDS & mRNA (grouped by products)
6295                 - anything else included in the filter as specified by the user (compact)
6296             */
6297             FPS.currentFIP = &tempFI;
6298             MemCopy (&tempFI, FIP, sizeof (FilterItem)); /* copy the filter . . .*/
6299             MemSet (&tempFI.IncludeFeature, 0, sizeof (tempFI.IncludeFeature));  /* but don't include any features*/
6300             tempFI.GroupPadding = 0;
6301             tempFI.IncludeFeature [FEATDEF_GENE] = FIP->IncludeFeature [FEATDEF_GENE];
6302             ResetFilterState (&FPS);
6303             height = ProcessRows (Layout_PackUpward, &FPS, vContext);
6304             FPS.ceiling -= height;
6305 
6306             ResetFilterState (&FPS);
6307             tempFI.IncludeFeature [FEATDEF_CDS] = FIP->IncludeFeature [FEATDEF_CDS];
6308             tempFI.IncludeFeature [FEATDEF_mRNA] = FIP->IncludeFeature [FEATDEF_mRNA];
6309             height = ProcessRows (layoutC, &FPS, vContext);
6310             FPS.ceiling -= height;
6311 
6312             ResetFilterState (&FPS);
6313             MemCopy (&tempFI.IncludeFeature, &FIP->IncludeFeature, sizeof (tempFI.IncludeFeature));
6314             tempFI.GroupPadding = FIP->GroupPadding;
6315             height = ProcessRows (Layout_PackUpward, &FPS, vContext);
6316             FPS.ceiling -= height;
6317             height = 0;
6318             if (FPS.featuresProcessedCount != 0) {
6319               emptyFilterGroup = FALSE;
6320             }
6321 
6322             break;
6323           default:
6324             FPS.currentFIP = FIP;
6325             ResetFilterState (&FPS);
6326             height = ProcessRows (layoutC, &FPS, vContext);
6327             if (FPS.featuresProcessedCount != 0) {
6328               emptyFilterGroup = FALSE;
6329             }
6330             break;
6331         } /* switch (layoutC) */
6332         break;
6333     } /* switch (FIP->type) */
6334     if (emptyFilterGroup) {
6335       FPS.ceiling = undoCeiling;
6336       continue;
6337     } else {
6338       switch (FIP->GroupLabelLoc) {
6339       default: break;
6340       case LabelOnTop:
6341         /* already done in DrawFilterItemBoxLabel() */
6342         break;
6343       case LabelOnSide:
6344         AddAttribute (vContext->drawOnMe, COLOR_ATT, FIP->GroupLabelColor, 0, 0, 0, 0);
6345         AddTextLabel (filterSeg, 0, FPS.ceiling - height / 2, FIP->GroupLabel,
6346                       FIP->GroupLabelFont, 1, MIDDLE_RIGHT, 0);
6347         SelectFont (FIP->GroupLabelFont);
6348         height = MAX (height, LineHeight () + 3);
6349         FPS.groupLabelDrawn = TRUE;
6350         break;
6351       case LabelOnBottom:
6352         AddAttribute (vContext->drawOnMe, COLOR_ATT, FIP->GroupLabelColor, 0, 0, 0, 0);
6353         AddTextLabel (filterSeg, (vContext->from + vContext->to) / 2 , FPS.ceiling - height, FIP->GroupLabel,
6354                       FIP->GroupLabelFont, 1, LOWER_CENTER, 0);
6355         SelectFont (FIP->GroupLabelFont);
6356         height += LineHeight () + 3;
6357         FPS.groupLabelDrawn = TRUE;
6358         break;
6359       }
6360     }
6361     if (FPS.groupBoxDrawn) {
6362       FPS.ceiling -= 10;
6363     }
6364     FPS.ceiling -= height + FIP->GroupPadding;
6365     if (FPS.needFreeList != NULL) {
6366       FPS.needFreeList = ValNodeFreeData (FPS.needFreeList);
6367       FPS.lastInFreeList = NULL;
6368     }
6369   } /* for ( FP->FilterItemList->next ) */
6370 
6371   if (FP->GroupByAnnot && FP->AnnotLabelLoc == LabelOnBottom) {
6372     CharPtr             annotName;
6373 
6374     annotName = GetSeqAnnotName(vContext->currentSAP);
6375     if (annotName != NULL && !StringHasNoText(annotName))
6376     {
6377       AddAttribute (vContext->sanLevelSeg, COLOR_ATT, FP->AnnotLabelColor, 0, 0, 0, 0);
6378       AddTextLabel (vContext->sanLevelSeg, (vContext->from + vContext->to) / 2 , FPS.ceiling, annotName,
6379                     FP->AnnotLabelFont, 1, LOWER_CENTER, 0);
6380       SelectFont (FP->AnnotLabelFont);
6381       FPS.ceiling -= LineHeight () + 3;
6382       FPS.annotLabelDrawn = TRUE;
6383     }
6384   }
6385   if (FPS.annotBoxDrawn) {
6386     /* add invisible line to force width of SegRect and space under group boxes. */
6387     invisibleSeg = CreateSegment (vContext->sanLevelSeg, 0, 0);
6388     SetSegmentVisibleFlag (invisibleSeg, FALSE);
6389     AddLine (invisibleSeg, vContext->from - 3 * vContext->viewScale ,  FPS.ceiling + 10, 
6390                            vContext->to + 3 * vContext->viewScale ,  FPS.ceiling + 10, FALSE, 0);
6391     FPS.ceiling -= 20;
6392   }
6393   if (FPS.needFreeList != NULL) {
6394     FPS.needFreeList = ValNodeFreeData (FPS.needFreeList);
6395     FPS.lastInFreeList = NULL;
6396   }
6397 
6398   if (vContext->ceiling != NULL) {
6399     *vContext->ceiling = FPS.ceiling;
6400   }
6401   return TRUE;
6402 }
6403 
6404 NLM_EXTERN RelevantFeaturesPtr FreeCollectedFeatures (
6405   RelevantFeaturesPtr RFP
6406 )
6407 
6408 {
6409   if (RFP == NULL) return NULL;
6410   if (RFP->sapList) {
6411     MemFree (RFP->sapList);
6412   }
6413   if (RFP->featVNP != NULL) {
6414     ValNodeFreeData (RFP->featVNP);
6415   }
6416   MemFree (RFP);
6417   return NULL;
6418 }
6419 
6420 NLM_EXTERN SegmenT CreateGraphicViewInternal (
6421   BioseqPtr bsp,
6422   Int4 from,
6423   Int4 to,
6424   Boolean allFeatures,
6425   RelevantFeaturesPtr feats,
6426   Int4 scale,
6427   Int4Ptr ceiling,
6428   SegmenT topLevel,
6429   AppearancePtr AP,
6430   FilterPtr FP,
6431   LayoutAlgorithm overrideLayout,
6432   GraphicViewExtrasPtr extras
6433 )
6434 
6435 {
6436   ViewerContext  VC;
6437   Int2           sapIndex;
6438   Int4           theCeiling = 0;
6439 
6440   /*
6441     Removed checks feats->featureCount == 0 || feats->featVNP == NULL
6442     to allow display of BSP w/0 features on it.
6443    */
6444 
6445   if (AP == NULL || FP == NULL) return NULL;
6446   MemSet ((Pointer) &VC, 0, sizeof (ViewerContext));
6447   VC.from = MIN (from, to);
6448   VC.to =   MAX (from, to);
6449   VC.allFeatures = allFeatures;
6450   VC.BSP = bsp;
6451   VC.viewScale = scale;
6452   VC.sapList = feats->sapList;
6453   VC.sapCount = feats->sapCount;
6454   if (topLevel == NULL) {
6455     VC.drawOnMe = VC.sanLevelSeg = VC.topLevelSeg = CreatePicture ();
6456   } else {
6457     VC.drawOnMe = VC.sanLevelSeg = VC.topLevelSeg = topLevel;
6458   }
6459   VC.featureCount = feats->featureCount;
6460   VC.featVNP = feats->featVNP;
6461   VC.AppPtr = AP;
6462   VC.FltPtr = FP;
6463   VC.overrideLayout = overrideLayout;
6464   VC.seqLength = bsp->length;
6465   VC.extras = extras;
6466   
6467   if (NULL == ceiling)
6468     VC.ceiling = &theCeiling;
6469   else
6470     VC.ceiling = ceiling;
6471   
6472   VC.currentSAP = NULL;
6473   FilterAndLayout (&VC);
6474   /* do again for named Seq Annot's */
6475   if (FP->GroupByAnnot)
6476   {
6477     for (sapIndex = 0; sapIndex < VC.sapCount; ++sapIndex) {
6478       VC.currentSAP = VC.sapList[sapIndex];
6479       FilterAndLayout (&VC);    
6480     }
6481   }
6482   return VC.topLevelSeg;
6483 }
6484 
6485 /* returns the 1st segment in a linked list. caller must deallocate it */
6486 NLM_EXTERN SegmenT CreateGraphicViewFromBsp (
6487   BioseqPtr bsp,
6488   SeqLocPtr location,
6489   Int4 scale,
6490   Int4Ptr ceiling,
6491   SegmenT topLevel,
6492   AppearancePtr AP,
6493   FilterPtr FP,
6494   LayoutAlgorithm overrideLayout,
6495   GraphicViewExtrasPtr extras
6496 )
6497 
6498 {
6499   RelevantFeaturesPtr   RFP;
6500   SegmenT               seg;
6501   SeqIntPtr             sintp;
6502   BioseqPtr             parent;
6503   SeqMgrSegmentContext  context;
6504   Int4                  from = 0;
6505   Int4                  to = 0;
6506   Boolean               allFeatures = TRUE;
6507 
6508   if (location != NULL) {
6509     bsp = BioseqFindFromSeqLoc (location);
6510     if (bsp == NULL) return NULL;
6511     to = bsp->length - 1;
6512 
6513     if (location->choice == SEQLOC_WHOLE) {
6514       location = NULL; /* no special behavior needed if it's whole */
6515     } else if (location->choice == SEQLOC_INT) {
6516       sintp = (SeqIntPtr) location->data.ptrvalue;
6517       if (sintp != NULL && sintp->from == 0 && sintp->to == bsp->length - 1) {
6518         location = NULL;
6519       } else if (sintp != NULL) {
6520         from = sintp->from;
6521         to = sintp->to;
6522         allFeatures = FALSE;
6523       }
6524     }
6525   } else if (bsp != NULL) {
6526     to = bsp->length - 1;
6527   }
6528   if (bsp == NULL) return NULL;
6529   parent = SeqMgrGetParentOfPart (bsp, &context);
6530   if (parent != NULL) {
6531     from = context.cumOffset;
6532     to = from + context.to - context.from;
6533     allFeatures = FALSE;
6534     bsp = parent;
6535   }
6536   RFP = CollectFeatures (bsp);
6537   if (RFP == NULL) return NULL;
6538   seg = CreateGraphicViewInternal (bsp, from, to, allFeatures, RFP, scale, ceiling, topLevel, AP, FP, overrideLayout, extras);
6539   FreeCollectedFeatures (RFP);
6540   return seg;
6541 }
6542 
6543 NLM_EXTERN SegmenT CreateGraphicView (
6544   BioseqPtr bsp,
6545   SeqLocPtr location,
6546   Int4 scale,
6547   CharPtr styleName,
6548   CharPtr filterName,
6549   CharPtr overrideLayoutName,
6550   GraphicViewExtrasPtr extras
6551 )
6552 
6553 {
6554   ViewerConfigsPtr  myVCP;
6555   FilterPtr         FP;
6556   AppearancePtr     AP;
6557   LayoutAlgorithm   overrideLayout;
6558   Int1              i;
6559 
6560   if (bsp == NULL && location == NULL) return NULL;
6561   myVCP = GetGraphicConfigParseResults ();
6562 
6563   AP = FindAppearanceByName (styleName, myVCP);
6564   FP = FindFilterByName (filterName, myVCP);
6565   i = StringIndexInStringList (overrideLayoutName, LayoutStrings);
6566   if (i >= 0 && i < DIM (LayoutValues)) {
6567     overrideLayout = LayoutValues[i];
6568   } else {
6569     overrideLayout = Layout_Inherit;
6570   }
6571       
6572   return CreateGraphicViewFromBsp (bsp, location, scale, NULL, NULL, AP, FP, overrideLayout, extras);
6573 }
6574 
6575 NLM_EXTERN Uint2 GetAppearanceCount (void)
6576 
6577 {
6578   ViewerConfigsPtr VCP;
6579 
6580   VCP = GetGraphicConfigParseResults ();
6581   if (VCP == NULL) return 0;
6582   return VCP->AppearanceCount;
6583 }
6584 
6585 NLM_EXTERN Uint2 GetFilterCount (void)
6586 
6587 {
6588   ViewerConfigsPtr VCP;
6589 
6590   VCP = GetGraphicConfigParseResults ();
6591   if (VCP == NULL) return 0;
6592   return VCP->FilterCount;
6593 }
6594 
6595 NLM_EXTERN Uint2 GetLayoutCount (void)
6596 
6597 {
6598   return DIM (LayoutStrings);
6599 }
6600 
6601 NLM_EXTERN Uint2 GetAlnScoreCount (void)
6602 {
6603   return DIM (AlnScoreStrings);
6604 }
6605 
6606 NLM_EXTERN Uint2 GetAlnScoreCutoffCount (void)
6607 {
6608   return DIM (AlnScoreCutoffStrings);
6609 }
6610 
6611 NLM_EXTERN CharPtr PNTR GetStyleNameList (void)
6612 
6613 {
6614   ViewerConfigsPtr VCP;
6615 
6616   VCP = GetGraphicConfigParseResults ();
6617   if (VCP == NULL) return NULL;
6618   return VCP->AppearanceNameArray;
6619 }
6620 
6621 NLM_EXTERN CharPtr PNTR GetFilterNameList (void)
6622 
6623 {
6624   ViewerConfigsPtr VCP;
6625 
6626   VCP = GetGraphicConfigParseResults ();
6627   if (VCP == NULL) return NULL;
6628   return VCP->FilterNameArray;
6629 }
6630 
6631 NLM_EXTERN CharPtr PNTR GetAlnScoreNameList(void)
6632 {
6633     return AlnScoreStrings;
6634 }
6635 
6636 NLM_EXTERN CharPtr PNTR GetAlnScoreCutoffList(void)
6637 {
6638     return AlnScoreCutoffStrings;
6639 }
6640 
6641 NLM_EXTERN CharPtr PNTR GetLayoutNameList (void)
6642 
6643 {
6644   return LayoutStrings;
6645 }
6646 
6647 
6648 /* -=-=-=-=-=-=-=- append default-style.c contents after this point -=-=-=-=-=-=-=-=- */
6649 
6650 /* default-style.c : creates a default style for the new graphic viewer
6651   This is an automatically generated file, which came from an asn2gph configuration file (asn2gph.default)
6652   It might be better to edit the input file and then re-run the script create-default-style.tcl than to
6653   edit this file directly.
6654 */
6655 
6656 
6657 typedef struct configFileLine {
6658   CharPtr key, value;
6659 } ConfigFileLine, PNTR ConfigFileLinePtr;
6660 
6661 typedef struct staticConfigFile {
6662   ConfigFileLinePtr lines;
6663   CharPtr sectionName;
6664 } StaticConfigFile, PNTR StaticConfigFilePtr;
6665 
6666 
6667 /* [Styles] */
6668 static ConfigFileLine defaultStyleLines1 [] = {
6669   {"style00", "defaultStyle"},
6670   {"style100", "summary"},
6671   {NULL, NULL}
6672 };
6673 
6674 /* [defaultStyle.master] */
6675 static ConfigFileLine defaultStyleLines2 [] = {
6676   {"name", "Default"},
6677   {"maxarrowscale", "200"},
6678   {"minarrowpixels", "5"},
6679   {"shadesmears", "false"},
6680   {"color", "black"},
6681   {"labelfont", "program"},
6682   {"labelcolor", "black"},
6683   {"label", "above"},
6684   {"grouplabelfont", "program"},
6685   {"grouplabelcolor", "dark gray"},
6686   {"groupboxcolor", "gray"},
6687   {"displaywith", "box"},
6688   {"height", "8"},
6689   {"gap", "line"},
6690   {"showarrow", "no"},
6691   {"showtype", "yes"},
6692   {"showcontent", "yes"},
6693   {"shadesmears", "false"},
6694   {"annotboxcolor", "100, 100, 100"},
6695   {"annotlabelcolor", "black"},
6696   {"annotlabelfont", "program"},
6697   {NULL, NULL}
6698 };
6699 
6700 /* [defaultStyle.bioseq] */
6701 static ConfigFileLine defaultStyleLines3 [] = {
6702   {"label", "left"},
6703   {"format", "accn"},
6704   {"scale", "true"},
6705   {"labelfont", "program"},
6706   {"scalefont", "small"},
6707   {"height", "10"},
6708   {"scaleheight", "10"},
6709   {"color", "0, 0, 0"},
6710   {"labelcolor", "64, 64, 255"},
6711   {"scalecolor", "32, 32, 32"},
6712   {NULL, NULL}
6713 };
6714 
6715 /* [defaultStyle.gene] */
6716 static ConfigFileLine defaultStyleLines4 [] = {
6717   {"label", "above"},
6718   {"color", "blue"},
6719   {"labelcolor", "blue"},
6720   {"showarrow", "true"},
6721   {NULL, NULL}
6722 };
6723 
6724 /* [defaultStyle.mRNA] */
6725 static ConfigFileLine defaultStyleLines5 [] = {
6726   {"label", "above"},
6727   {"color", "cyan"},
6728   {"labelcolor", "cyan"},
6729   {"showarrow", "true"},
6730   {"gap", "line"},
6731   {NULL, NULL}
6732 };
6733 
6734 /* [defaultStyle.cds] */
6735 static ConfigFileLine defaultStyleLines6 [] = {
6736   {"label", "above"},
6737   {"color", "magenta"},
6738   {"labelcolor", "magenta"},
6739   {"showarrow", "true"},
6740   {"gap", "angle"},
6741   {NULL, NULL}
6742 };
6743 
6744 /* [defaultStyle.tRNA] */
6745 static ConfigFileLine defaultStyleLines7 [] = {
6746   {"label", "above"},
6747   {"color", "green"},
6748   {"labelcolor", "green"},
6749   {"showarrow", "true"},
6750   {"gap", "line"},
6751   {NULL, NULL}
6752 };
6753 
6754 /* [defaultStyle.imp] */
6755 static ConfigFileLine defaultStyleLines8 [] = {
6756   {"showcontent", "no"},
6757   {"color", "gray"},
6758   {"labelcolor", "gray"},
6759   {NULL, NULL}
6760 };
6761 
6762 /* [defaultStyle.exon] */
6763 static ConfigFileLine defaultStyleLines9 [] = {
6764   {"showcontent", "no"},
6765   {"color", "dark cyan"},
6766   {"labelcolor", "dark cyan"},
6767   {NULL, NULL}
6768 };
6769 
6770 /* [defaultStyle.intron] */
6771 static ConfigFileLine defaultStyleLines10 [] = {
6772   {"showcontent", "no"},
6773   {"color", "light gray"},
6774   {"labelcolor", "light gray"},
6775   {NULL, NULL}
6776 };
6777 
6778 /* [defaultStyle.bond] */
6779 static ConfigFileLine defaultStyleLines11 [] = {
6780   {"displaywith", "cappedline"},
6781   {NULL, NULL}
6782 };
6783 
6784 /* [defaultStyle.align] */
6785 static ConfigFileLine defaultStyleLines12 [] = {
6786   {"label", "above"},
6787   {"color", "blue"},
6788   {"labelcolor", "blue"},
6789   {"showarrow", "true"},
6790   {"showtype", "no"},
6791   {"showcontent", "yes"},
6792   {"format", "accession"},
6793   {NULL, NULL}
6794 };
6795 
6796 /* [summary.master] */
6797 static ConfigFileLine defaultStyleLines13 [] = {
6798   {"name", "Summary"},
6799   {"label", "none"},
6800   {"height", "3"},
6801   {"labelfont", "small"},
6802   {"grouplabelfont", "small"},
6803   {"annotlabelfont", "small"},
6804   {NULL, NULL}
6805 };
6806 
6807 /* [summary.bioseq] */
6808 static ConfigFileLine defaultStyleLines14 [] = {
6809   {"label", "above"},
6810   {"format", "accn"},
6811   {"scale", "true"},
6812   {"labelfont", "program"},
6813   {"scalefont", "small"},
6814   {"height", "5"},
6815   {"scaleheight", "5"},
6816   {"color", "0, 0, 0"},
6817   {"labelcolor", "64, 64, 255"},
6818   {"scalecolor", "32, 32, 32"},
6819   {NULL, NULL}
6820 };
6821 
6822 /* [summary.gene] */
6823 static ConfigFileLine defaultStyleLines15 [] = {
6824   {"label", "above"},
6825   {"color", "blue"},
6826   {"labelcolor", "blue"},
6827   {"showarrow", "true"},
6828   {NULL, NULL}
6829 };
6830 
6831 /* [summary.mRNA] */
6832 static ConfigFileLine defaultStyleLines16 [] = {
6833   {"label", "above"},
6834   {"color", "cyan"},
6835   {"labelcolor", "cyan"},
6836   {"showarrow", "true"},
6837   {"gap", "line"},
6838   {NULL, NULL}
6839 };
6840 
6841 /* [summary.cds] */
6842 static ConfigFileLine defaultStyleLines17 [] = {
6843   {"label", "above"},
6844   {"color", "magenta"},
6845   {"labelcolor", "magenta"},
6846   {"showarrow", "true"},
6847   {"gap", "angle"},
6848   {NULL, NULL}
6849 };
6850 
6851 /* [summary.tRNA] */
6852 static ConfigFileLine defaultStyleLines18 [] = {
6853   {"label", "above"},
6854   {"color", "green"},
6855   {"labelcolor", "green"},
6856   {"showarrow", "true"},
6857   {"gap", "line"},
6858   {NULL, NULL}
6859 };
6860 
6861 /* [summary.align] */
6862 static ConfigFileLine defaultStyleLines19 [] = {
6863   {"label", "above"},
6864   {"color", "blue"},
6865   {"labelcolor", "blue"},
6866   {"showarrow", "true"},
6867   {"showtype", "no"},
6868   {"showcontent", "yes"},
6869   {"format", "accession"},
6870   {NULL, NULL}
6871 };
6872 
6873 /* [summary.imp] */
6874 static ConfigFileLine defaultStyleLines20 [] = {
6875   {"showcontent", "no"},
6876   {"color", "gray"},
6877   {"labelcolor", "gray"},
6878   {NULL, NULL}
6879 };
6880 
6881 /* [summary.exon] */
6882 static ConfigFileLine defaultStyleLines21 [] = {
6883   {"showcontent", "no"},
6884   {"color", "dark cyan"},
6885   {"labelcolor", "dark cyan"},
6886   {NULL, NULL}
6887 };
6888 
6889 /* [summary.intron] */
6890 static ConfigFileLine defaultStyleLines22 [] = {
6891   {"showcontent", "no"},
6892   {"color", "light gray"},
6893   {"labelcolor", "light gray"},
6894   {NULL, NULL}
6895 };
6896 
6897 /* [summary.bond] */
6898 static ConfigFileLine defaultStyleLines23 [] = {
6899   {"displaywith", "cappedline"},
6900   {NULL, NULL}
6901 };
6902 
6903 /* [Filters] */
6904 static ConfigFileLine defaultStyleLines24 [] = {
6905   {"filter00", "defaultFilt"},
6906   {"filter100", "summary"},
6907   {"maxlabelscale", "200"},
6908   {"grouppadding", "2"},
6909   {"rowpadding", "2"},
6910   {"annotgroup", "yes"},
6911   {"annotbox", "yes"},
6912   {"annotlabel", "above"},
6913   {NULL, NULL}
6914 };
6915 
6916 /* [defaultFilt] */
6917 static ConfigFileLine defaultStyleLines25 [] = {
6918   {"name", "Default"},
6919   {"layout", "compact"},
6920   {"group1", "defaultFilt-operons"},
6921   {"group2", "defaultFilt-gene-cds-prot-mrna"},
6922   {"group3", "defaultFilt-other-rnas"},
6923   {"group4", "defaultFilt-exon-intron-label"},
6924   {"group5", "defaultFilt-variations"},
6925   {"group6", "defaultFilt-conflicts"},
6926   {"group7", "defaultFilt-STS"},
6927   {"group8", "defaultFilt-impfeats"},
6928   {"group9", "defaultFilt-alignments"},
6929   {"group10", "defaultFilt-everything-else-label"},
6930   {NULL, NULL}
6931 };
6932 
6933 /* [filters.defaultFilt-gene-cds-prot-mrna] */
6934 static ConfigFileLine defaultStyleLines26 [] = {
6935   {"feature1", "gene"},
6936   {"feature2", "cds"},
6937   {"feature3", "prot"},
6938   {"feature4", "mrna"},
6939   {"name", "Gene-mRNA-CDS-Prots"},
6940   {"grouplabel", "none"},
6941   {"layout", "geneproducts"},
6942   {"showtype", "yes"},
6943   {"showcontent", "yes"},
6944   {"label", "above"},
6945   {NULL, NULL}
6946 };
6947 
6948 /* [filters.defaultFilt-other-rnas] */
6949 static ConfigFileLine defaultStyleLines27 [] = {
6950   {"feature1", "rna"},
6951   {"name", "Structural RNAs"},
6952   {"grouplabel", "none"},
6953   {"label", "above"},
6954   {NULL, NULL}
6955 };
6956 
6957 /* [filters.defaultFilt-operons] */
6958 static ConfigFileLine defaultStyleLines28 [] = {
6959   {"feature1", "operon"},
6960   {"name", "Operons"},
6961   {"grouplabel", "none"},
6962   {"label", "above"},
6963   {NULL, NULL}
6964 };
6965 
6966 /* [filters.defaultFilt-exon-intron-label] */
6967 static ConfigFileLine defaultStyleLines29 [] = {
6968   {"feature1", "exon"},
6969   {"feature2", "intron"},
6970   {"name", "Introns and Exons"},
6971   {"grouplabel", "none"},
6972   {"label", "above"},
6973   {NULL, NULL}
6974 };
6975 
6976 /* [filters.defaultFilt-variations] */
6977 static ConfigFileLine defaultStyleLines30 [] = {
6978   {"feature1", "variation"},
6979   {"name", "Variations"},
6980   {"groupbox", "true"},
6981   {"boxcolor", "red"},
6982   {"grouplabel", "above"},
6983   {"layout", "smear"},
6984   {"showtype", "no"},
6985   {"showcontent", "no"},
6986   {NULL, NULL}
6987 };
6988 
6989 /* [filters.defaultFilt-conflicts] */
6990 static ConfigFileLine defaultStyleLines31 [] = {
6991   {"feature1", "conflict"},
6992   {"name", "Conflicts"},
6993   {"groupbox", "true"},
6994   {"boxcolor", "dark red"},
6995   {"grouplabel", "above"},
6996   {"layout", "smear"},
6997   {"showtype", "no"},
6998   {"showcontent", "no"},
6999   {NULL, NULL}
7000 };
7001 
7002 /* [filters.defaultFilt-STS] */
7003 static ConfigFileLine defaultStyleLines32 [] = {
7004   {"feature1", "STS"},
7005   {"name", "STS"},
7006   {"groupbox", "true"},
7007   {"boxcolor", "red"},
7008   {"grouplabel", "above"},
7009   {"layout", "smear"},
7010   {"showtype", "no"},
7011   {"showcontent", "no"},
7012   {NULL, NULL}
7013 };
7014 
7015 /* [filters.defaultFilt-impfeats] */
7016 static ConfigFileLine defaultStyleLines33 [] = {
7017   {"feature1", "import"},
7018   {"name", "Import Features"},
7019   {"grouplabel", "none"},
7020   {"label", "above"},
7021   {NULL, NULL}
7022 };
7023 
7024 /* [filters.defaultFilt-alignments] */
7025 static ConfigFileLine defaultStyleLines34 [] = {
7026   {"feature1", "align"},
7027   {"name", "Alignments"},
7028   {"grouplabel", "none"},
7029   {"label", "above"},
7030   {"layout", "smear"},
7031   {"showtype", "no"},
7032   {NULL, NULL}
7033 };
7034 
7035 /* [filters.defaultFilt-everything-else-label] */
7036 static ConfigFileLine defaultStyleLines35 [] = {
7037   {"feature1", "everything"},
7038   {"grouplabel", "none"},
7039   {"label", "above"},
7040   {NULL, NULL}
7041 };
7042 
7043 /* [summary] */
7044 static ConfigFileLine defaultStyleLines36 [] = {
7045   {"group1", "summary-gene-rna-cds-nolabel"},
7046   {"group2", "summary-allelse"},
7047   {"group3", "summary-aligns-nolabel"},
7048   {"name", "Summary"},
7049   {"defaultlayout", "compact"},
7050   {"rowpadding", "3"},
7051   {"grouppadding", "1"},
7052   {"label", "none"},
7053   {"annotlabelfont", "small"},
7054   {NULL, NULL}
7055 };
7056 
7057 /* [filters.summary-gene-rna-cds-nolabel] */
7058 static ConfigFileLine defaultStyleLines37 [] = {
7059   {"feature1", "gene"},
7060   {"feature2", "rna"},
7061   {"feature3", "cds"},
7062   {"layout", "geneproducts"},
7063   {"label", "none"},
7064   {NULL, NULL}
7065 };
7066 
7067 /* [filters.summary-aligns-nolabel] */
7068 static ConfigFileLine defaultStyleLines38 [] = {
7069   {"feature1", "align"},
7070   {"label", "none"},
7071   {NULL, NULL}
7072 };
7073 
7074  /* [filters.summary-allelse] */
7075 static ConfigFileLine defaultStyleLines39 [] = {
7076   {"feature1", "everything"},
7077   {"layout", "smear"},
7078   {"label", "none"},
7079   {NULL, NULL}
7080 };
7081 
7082 
7083 static StaticConfigFile defaultStyle [] = {
7084   {defaultStyleLines1, "Styles"},
7085   {defaultStyleLines2, "defaultStyle.master"},
7086   {defaultStyleLines3, "defaultStyle.bioseq"},
7087   {defaultStyleLines4, "defaultStyle.gene"},
7088   {defaultStyleLines5, "defaultStyle.mRNA"},
7089   {defaultStyleLines6, "defaultStyle.cds"},
7090   {defaultStyleLines7, "defaultStyle.tRNA"},
7091   {defaultStyleLines8, "defaultStyle.imp"},
7092   {defaultStyleLines9, "defaultStyle.exon"},
7093   {defaultStyleLines10, "defaultStyle.intron"},
7094   {defaultStyleLines11, "defaultStyle.bond"},
7095   {defaultStyleLines12, "defaultStyle.align"},
7096   {defaultStyleLines13, "summary.master"},
7097   {defaultStyleLines14, "summary.bioseq"},
7098   {defaultStyleLines15, "summary.gene"},
7099   {defaultStyleLines16, "summary.mRNA"},
7100   {defaultStyleLines17, "summary.cds"},
7101   {defaultStyleLines18, "summary.tRNA"},
7102   {defaultStyleLines19, "summary.align"},
7103   {defaultStyleLines20, "summary.imp"},
7104   {defaultStyleLines21, "summary.exon"},
7105   {defaultStyleLines22, "summary.intron"},
7106   {defaultStyleLines23, "summary.bond"},
7107   {defaultStyleLines24, "Filters"},
7108   {defaultStyleLines25, "defaultFilt"},
7109   {defaultStyleLines26, "filters.defaultFilt-gene-cds-prot-mrna"},
7110   {defaultStyleLines27, "filters.defaultFilt-other-rnas"},
7111   {defaultStyleLines28, "filters.defaultFilt-operons"},
7112   {defaultStyleLines29, "filters.defaultFilt-exon-intron-label"},
7113   {defaultStyleLines30, "filters.defaultFilt-variations"},
7114   {defaultStyleLines31, "filters.defaultFilt-conflicts"},
7115   {defaultStyleLines32, "filters.defaultFilt-STS"},
7116   {defaultStyleLines33, "filters.defaultFilt-impfeats"},
7117   {defaultStyleLines34, "filters.defaultFilt-alignments"},
7118   {defaultStyleLines35, "filters.defaultFilt-everything-else-label"},
7119   {defaultStyleLines36, "summary"},
7120   {defaultStyleLines37, "filters.summary-gene-rna-cds-nolabel"},
7121   {defaultStyleLines38, "filters.summary-aligns-nolabel"},
7122   {defaultStyleLines39, "filters.summary-allelse"},
7123   {NULL, NULL}
7124 };
7125 
7126 
7127 static void InitializeDefaultStyle (
7128   CharPtr configFileName
7129 )
7130 
7131 {
7132   Uint2             sectionNum, lineNum;
7133   ConfigFileLinePtr lines;
7134   CharPtr           sectionName;
7135 
7136   for (sectionNum = 0; defaultStyle [sectionNum].lines != NULL; sectionNum++) {
7137     lines = defaultStyle [sectionNum].lines;
7138     sectionName = defaultStyle [sectionNum].sectionName;
7139     for (lineNum = 0; lines [lineNum].key != NULL && lines [lineNum].value != NULL; lineNum++) {
7140       TransientSetAppParam (configFileName, sectionName, lines [lineNum].key, lines [lineNum].value);
7141     }
7142   }
7143 }
7144 
7145 /* End of automatically generated file. */
7146 
7147 

source navigation ]   [ diff markup ]   [ identifier search ]   [ freetext search ]   [ file search ]  

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.