Logo Search packages:      
Sourcecode: ncbi-tools6 version File versions

drawseq.c


#include <vibrant.h>
#include <drawseq.h>
#include <edutil.h>

Uint1 custom_color[3];

#define SCALE_SPACE 20
static Boolean  AddFlatNodeSymbol PROTO((SegmenT pseg, ValNodePtr fnp_node, Int4 scale));

/*######################################################################
#
#     functions for setting up the color for different object
#
######################################################################*/


#define RGB_B(x) (Uint1)((x)&255);
#define RGB_G(x) (Uint1)(((x)>>8)&255);
#define RGB_R(x) (Uint1)(((x)>>16)&255);
static Boolean convert_color(Int4 val, Uint1Ptr color)
{

      if(val<0 || color == NULL)
            return FALSE;
      color[0] = RGB_R(val);
      color[1] = RGB_G(val);
      color[2] = RGB_B(val);
      return TRUE;
}


Uint1 MUSK_COLOR[3];
/************************************************************************
*
*     get_seg_color(order)
*     set up the color for a segmented sequence
*
*************************************************************************/
Uint1Ptr get_seg_color(Int2 order)
{
      Int2 c_num;
      Int4 color_val;
      
      c_num = (Int2)GetMuskCParam(MSM_CCOLOR, MSM_NOSUBCLASS, MSM_NUM);
      if(c_num != 0)
      {
            order = (Int2)(order)%c_num +1;
            color_val = GetMuskCParam(MSM_CCOLOR, order, MSM_COLOR);
            if(convert_color(color_val, MUSK_COLOR))
                  return MUSK_COLOR;
      }
      return NULL;

}


/**********************************************************************
*
*     get the color for different enzymes
*     used in drawing the restriction map
*
**********************************************************************/
Uint1Ptr get_enz_color(Int2 enzID)
{
      return get_seg_color(enzID);
}


/**********************************************************************
*
*
*
***********************************************************************/
static void DrawSquiggle(SegmenT seg, Int4 x, Int4 y, Int2 primID, Boolean is_left)
{
      Int4 space;

      space = GetMuskCParam(MSM_TOPSTYLE, MSM_SPACE, MSM_HEIGHT);
      AddLine(seg, x, y, x, y-space, FALSE, 0);
      if(is_left)
            AddSymbol(seg, x+2, y - space, LEFT_TRIANGLE_SYMBOL, TRUE, LOWER_LEFT, primID);
      else
            AddSymbol(seg, x, y - space, RIGHT_TRIANGLE_SYMBOL, TRUE, LOWER_RIGHT, primID);

}


static Int2 get_arrow_for_strand(Uint1 strand)
{
      switch(strand)
      {
            case Seq_strand_minus:
                  return LEFT_ARROW;
            case Seq_strand_plus:
                  return RIGHT_ARROW;
            default:
                  return NO_ARROW;
      }
}

void add_attribute_pen(SegmenT seg, Int2 p_class, Int2 sub_class)
{
      Uint1 color[3];
      Uint1 flags = COLOR_ATT|STYLE_ATT|SHADING_ATT|WIDTH_ATT;
      Int4  c_val;
      Uint1 linestyle;
      Uint1 penwidth;
      Uint1 shading;

      c_val = GetMuskCParam(p_class, sub_class, MSM_COLOR);
      convert_color(c_val, color);

      linestyle = (Uint1)GetMuskCParam(p_class, sub_class, MSM_LTYPE);
      penwidth = (Uint1)GetMuskCParam(p_class, sub_class, MSM_PENWIDTH);
      shading = (Uint1)GetMuskCParam(p_class, sub_class, MSM_SHADING);

      AddAttribute(seg, flags, &color[0], linestyle, shading, penwidth, COPY_MODE);

}

/*************************************************************************
*
*     DrawFeatNode(vnp, type, rec_width, scale, seg, label_type, fill)
*     Draw a list of featnode, return TRUE for success
*     vnp: a list of featnode
*     rec_width: width for drawing a rectangle. if <2, draw a line
*     scale: max scale
*     seg: drawing segment
*     label_type: Add label on the segment
*     fill: fill the rectangle?
*
*************************************************************************/

static void AddLabelWithAlign(SegmenT seg, Uint1 label_align, Int4 left, Int4 right, Int4 top, Int4 bottom, CharPtr label, FonT font, Uint2 itemID)
{
      Int4 center, middle;
      

      switch(label_align)
      {
            case MSM_LABEL_TOP:
                  center = (left + right)/2;
                  AddTextLabel(seg, center, top, label, font, 0, UPPER_CENTER, itemID);
                  break;
            case MSM_LABEL_BOTTOM:
                  center = (left + right)/2;
                  AddTextLabel(seg, center, bottom, label, font, 0, LOWER_CENTER, itemID);
                  break;
            case MSM_LABEL_LEFT:
                  middle = (top + bottom)/2;
                  AddTextLabel(seg, left, middle, label, font, 0, MIDDLE_LEFT, itemID);
                  break;
            case MSM_LABEL_RIGHT:
                  middle = (top + bottom)/2;
                  AddTextLabel(seg, right, middle, label, font, 0, MIDDLE_RIGHT, itemID);
                  break;
            default:
                  break;
      }
}

static Boolean load_gene_data(GeneDataPtr gdp, FeatNodePtr fnp, Uint2 itemType, Int4 x, Int4 y)
{
      while(gdp)
      {
            if(gdp->entityID == fnp->entityID)
            {
                  if(gdp->itemID == fnp->itemID)
                  {
                        if(gdp->itemType == itemType)
                        {
                              gdp->x = x;
                              gdp->y = y;
                              return TRUE;
                        }
                  }
            }
            gdp = gdp->next;
      }
      return FALSE;
}

static Boolean AddBoxWithAlign(SegmenT seg, Uint1 label_align, FeatNodePtr fnp, Int4 scale, GeneDataPtr gdp, Uint2 itemType)
{
      Int4 center, middle;
      Int4 width;
      Int4 left, right, top, bottom;
      CharPtr label;
      Int4 labelHeight;
      Uint2 itemID;
      Char buf[10];

      if(fnp == NULL || fnp->label == NULL)
            return FALSE;
      left = fnp->extremes.left;
      right = fnp->extremes.right;
      top = fnp->top;
      bottom = fnp->bottom;
      labelHeight = fnp->labelHeight;
      itemID = fnp->itemID;
      label = fnp->label;
      if(labelHeight == 0)    /*the label will NOT be displayed*/
      {
            StringCpy(buf, "XXXX");
            label = buf;
            /* font = (FonT)GetMuskCParam(fnp->feattype, MSM_FLABEL, MSM_FONT);
            SelectFont(font); */
            labelHeight = FontHeight();
            label_align = MSM_LABEL_TOP;
            top -= labelHeight;
                  
      }
      
      width = scale * StringWidth(label);
      AddAttribute (seg, COLOR_ATT, YELLOW_COLOR, 0, 0, 0, 0);
      switch(label_align)
      {
            case MSM_LABEL_TOP:
                  center = (left + right)/2;
                  left = center - width/2;
                  right = center + width/2;
                  bottom = top;
                  top += labelHeight;
                  break;
            case MSM_LABEL_BOTTOM:
                  center = (left + right)/2;
                  left = center - width/2;
                  right = center + width/2;
                  top = bottom;
                  bottom -= labelHeight;
                  break;
            case MSM_LABEL_LEFT:
                  middle = (top + bottom)/2;
                  top = middle - labelHeight/2;
                  bottom = middle + labelHeight/2;
                  right = left;
                  left = left - width;
                  
                  break;
            case MSM_LABEL_RIGHT:
                  middle = (top + bottom)/2;
                  top = middle - labelHeight/2;
                  bottom = middle + labelHeight/2;
                  left  = right;
                  right += width;
                  break;
            default:
                  return FALSE;
      }
      AddRectangle (seg, left, top+5, right, bottom+5, FALSE, TRUE, itemID);
      load_gene_data(gdp, fnp, itemType, left, top);

      /*show the partial label*/
      if(fnp->labelHeight == 0 && fnp->label != NULL)
      {
            center = (fnp->extremes.left + fnp->extremes.right)/2;
            middle = (fnp->top + fnp->bottom)/2;
            AddAttribute (seg, COLOR_ATT, RED_COLOR, 0, 0, 0, 0);
            AddSymbol(seg, center, middle, DIAMOND_SYMBOL, TRUE, MIDDLE_CENTER, fnp->itemID); 
      }
      return TRUE;
}



static void AddFeatNodeLabel(SegmenT seg, FeatNodePtr fnp, Uint1 label_align, FonT font)
{
      if(fnp->labelHeight > 0)
            AddLabelWithAlign(seg, label_align, fnp->extremes.left, fnp->extremes.right, fnp->top, fnp->bottom, fnp->label, font, fnp->itemID);
}

static void AddFeatNodeTickMark(SegmenT seg, FeatNodePtr fnp, Uint1 label_align)
{
      Int4 center, middle;
      Int4 y_pos, x_pos;
      Int4 tick_len;
      

      tick_len = TICK_LEN;
      switch(label_align)
      {
            case MSM_LABEL_TOP:
                  center = (fnp->extremes.left + fnp->extremes.right)/2;
                  y_pos = fnp->bottom;
                  AddLine(seg, center, y_pos, center, y_pos-tick_len, FALSE, fnp->itemID);
                  break;
            case MSM_LABEL_BOTTOM:
                  center = (fnp->extremes.left + fnp->extremes.right)/2;
                  y_pos = fnp->top;
                  AddLine(seg, center, y_pos, center, y_pos+tick_len, FALSE, fnp->itemID);
                  break;
            case MSM_LABEL_LEFT:
                  middle = (fnp->top + fnp->bottom)/2;
                  x_pos = fnp->extremes.left;
                  AddLine(seg, x_pos, middle, x_pos-tick_len, middle, FALSE, fnp->itemID);
                  break;
            case MSM_LABEL_RIGHT:
                  middle = (fnp->top + fnp->bottom)/2;
                  x_pos = fnp->extremes.right;
                  AddLine(seg, x_pos, middle, x_pos+tick_len, middle, FALSE, fnp->itemID);
                  break;
            default:
                  break;
      }
}


/*gdata is added to record the possible selected marker*/
static Boolean  AddFeatNodeListLabel(SegmenT p_seg, ValNodePtr fnp_node, Uint1 label_align, Int4 scale, GeneDataPtr gdata)
{
      FeatNodePtr fnp;
      Boolean has_prev = FALSE;
      Int2 ptype = 0, _class;
      FonT font = NULL;
      Int4 color_val;
      Uint1 color[3];

      SegmenT seg = NULL;
      Uint2 p_entityID = 0;
      
/*    if(label_align > MSM_LABEL_RIGHT || label_align < MSM_LABEL_TOP)*/
      if(label_align > 3 /* || label_align < 0 */)
            return FALSE;
      while(fnp_node)
      {
            fnp = fnp_node->data.ptrvalue;
            if(!has_prev || fnp->entityID != p_entityID)
            {
                  seg = CreateSegment(p_seg, fnp->entityID, 0);
                  p_entityID = fnp->entityID;
                  has_prev = FALSE;
            }
            if(fnp && fnp->label)
            {
                  _class = get_current_class(fnp);
                        
                  if(!has_prev || ptype != _class)
                  {
                        /*if(has_prev)
                              DeleteFont(font);*/
                        font = (FonT)GetMuskCParam(_class, MSM_FLABEL, MSM_FONT);
                        SelectFont(font);
                        color_val = GetMuskCParam(_class, MSM_FLABEL, MSM_COLOR);
                        convert_color(color_val, color);
                        AddAttribute(seg, COLOR_ATT, &(color[0]), 0, 0, 0, 0);
                  
                        has_prev = TRUE;
                        ptype = _class;
                  }
                  if(fnp->landmark)
                  {
                        AddBoxWithAlign(seg, label_align, fnp, scale, gdata, (Uint2)(fnp_node->choice));
                        AddAttribute (seg, COLOR_ATT, color, 0, 0, 0, 0);
                  }
                  AddFeatNodeLabel(seg, fnp, label_align, font);
            }
            if(fnp->draw_tick)
                  AddFeatNodeTickMark(seg, fnp, label_align);
                  
            fnp_node = fnp_node->next;
      }
      
      /*if(has_prev)
            DeleteFont(font);*/
      return TRUE;
}

static void DrawGapInterval(SegmenT seg, Int4 left, Int4 right, Int4 top, Int4 bottom, Int4 gap_style, Uint2 itemID)
{
      Int4 center, middle;
      
      switch(gap_style)
      {
            case MSM_GAP_LINE:
                  middle = (top + bottom)/2;
                  AddLine(seg, left, middle, right, middle, FALSE, itemID);
                  break;
            case MSM_GAP_ANGLE:
                  center = (left + right)/2;
                  middle = (top + bottom)/2;
                  AddLine(seg, left, middle, center, top, FALSE, itemID);
                  AddLine(seg, center, top, right, middle, FALSE, itemID);
                  break;
            default:
                  break;
      }
}


static void AddGapForOneFeatNode(FeatNodePtr fnp, Int4Ptr pstyle, Int2Ptr p_class, BoolPtr has_prev, SegmenT seg)
{
      Int4 prev_pos, i_left, i_right;
      Int4 start, stop;
      Int4 style, segstyle;
      ValNodePtr ivals;
      IvalNodePtr inp = NULL;
      Int4 top, bottom;
      Int2 _class;
      Boolean show_gap;

      ivals = fnp->interval;
            
      _class = get_current_class(fnp);
      if(!(*has_prev) || (*p_class) != _class)
      {
            style = GetMuskCParam(_class, MSM_SEGMENT, MSM_STYLE);
            segstyle = (style & MSM_SEG_FORM);
            show_gap = ((segstyle == MSM_SEG_BOX) || (segstyle == MSM_SEG_LINE));
            if(!show_gap)
                  return;
                  
            add_attribute_pen(seg, _class, MSM_FGAP);
            style = GetMuskCParam(_class, MSM_FGAP, MSM_STYLE);
            *has_prev = TRUE;
            *p_class = _class;
            *pstyle = segstyle;
      }
      else
      {
            segstyle = *pstyle;
            show_gap =(segstyle == MSM_SEG_BOX || segstyle == MSM_SEG_LINE);
            if(!show_gap)
                  return;
            else
                  style = GetMuskCParam(_class, MSM_FGAP, MSM_STYLE);
                  
      }
                  

      top = fnp->top;
      bottom = fnp->bottom;
      if(ivals == NULL)
      {
            start = fnp->extremes.left;
            stop = fnp->extremes.right;
            DrawGapInterval(seg, start, stop, top, bottom, style, fnp->itemID);
            return;
      }

      if(fnp->extremes.strand == Seq_strand_minus)
            prev_pos = fnp->extremes.right;
      else
            prev_pos = fnp->extremes.left;
      while(ivals)
      {
            inp = ivals->data.ptrvalue;
            i_left = inp->gr.left;
            i_right = inp->gr.right;
            if(inp->gr.strand == Seq_strand_minus)
            {
                  stop = prev_pos;
                  start = i_right;
                  prev_pos = i_left;
            }
            else
            {
                  start = prev_pos;
                  stop = i_left;
                  prev_pos  = i_right;
            }
            if(stop > start)
                  DrawGapInterval(seg, start, stop, top, bottom, style, fnp->itemID);                       
            ivals = ivals->next;
      }

      /*check the last piece*/
      if(inp !=NULL)
      {
            if(inp->gr.strand == Seq_strand_minus)
            {
                  stop = prev_pos;
                  start = fnp->extremes.left;
            }
            else
            {
                  start = prev_pos;
                  stop = fnp->extremes.right;
            }
            if(stop > start)
                  DrawGapInterval(seg, start, stop, top, bottom, style, fnp->itemID);
      }
}


static void AddFeatNodeListGaps(SegmenT p_seg, ValNodePtr fnp_node)
{
      FeatNodePtr fnp;
      Boolean has_prev = FALSE;
      Int2 pclass = -1;
      Int4 style;
      SegmenT seg = NULL;
      Uint2 p_entityID = 0;
      
      while(fnp_node)
      {
            fnp = fnp_node->data.ptrvalue;
            if(!has_prev || fnp->entityID != p_entityID)
            {
                  seg = CreateSegment(p_seg, fnp->entityID, 0);
                  p_entityID = fnp->entityID;
                  has_prev = FALSE; /*Reset has_prev to reset the style of the current seg*/
            }
            AddGapForOneFeatNode(fnp, &style, &pclass, &has_prev, seg);
            fnp_node = fnp_node->next;
      }
}

static Boolean DrawFeatNodeSegment(FeatNodePtr fnp, Int4 segstyle, Int2 subclass, SegmenT seg, Boolean show_arrow, Boolean show_interval)
{
      Int4 e_left, e_right, end_pos;
      Int4 i_left, i_right;
      Int4 top, bottom;
      Uint1 strand;
      Boolean fill;
      Int2 arrow = NO_ARROW;
      ValNodePtr ivals;
      IvalNodePtr inp;

      if(fnp == NULL)
            return FALSE;     
      if(subclass != MSM_SEGMENT && subclass != MSM_SEG_BORD)
            return FALSE;
      if(segstyle != MSM_SEG_LINE && segstyle != MSM_SEG_BOX)
            return FALSE;
      if(segstyle == MSM_SEG_LINE && subclass == MSM_SEG_BORD)
            return FALSE;
      fill = (subclass == MSM_SEGMENT);
            
      e_left = fnp->extremes.left;
      e_right = fnp->extremes.right;
      top = fnp->top;
      bottom = fnp->bottom;
      strand = fnp->extremes.strand;
      if(fnp->interval == NULL)
      {
            if(show_interval)
                  return TRUE;
            if(show_arrow)
                  arrow = get_arrow_for_strand(strand);
            if(segstyle == MSM_SEG_BOX)
                  AddRectangle(seg, e_left, top, e_right, bottom, arrow, fill, fnp->itemID);
            else
                  AddLine(seg, e_left, bottom, e_right, bottom, FALSE, fnp->itemID);
      } 
      if(strand == Seq_strand_minus)
            end_pos = e_left;
      else
            end_pos = e_right;
                        
      for(ivals = fnp->interval; ivals !=NULL; ivals = ivals->next)
      {
            arrow = NO_ARROW;
            inp = ivals->data.ptrvalue;
            i_left = inp->gr.left;
            i_right = inp->gr.right;
            if(show_arrow)
            {
                  if(strand == Seq_strand_minus && i_left == end_pos)
                        arrow = LEFT_ARROW;
                  if(strand == Seq_strand_plus && i_right == end_pos)
                        arrow = RIGHT_ARROW;
            }
            if(segstyle == MSM_SEG_BOX)
                  AddRectangle (seg, i_left, top, i_right, bottom, arrow, fill, fnp->itemID);
            else
                  AddLine(seg, i_left, bottom, i_right, bottom, FALSE, fnp->itemID);
      }
      return TRUE;
}

      
static void AddFeatNodeListBorder(SegmenT p_seg, ValNodePtr fnp_node, Boolean show_arrow, Boolean show_interval)
{
      FeatNodePtr fnp;
      Boolean has_prev = FALSE;
      Int2 ptype = 0, _class;
      Boolean show_border = FALSE;
      Int4 style, segstyle = 0;
      Boolean arrow = FALSE;
      SegmenT seg = NULL;
      Uint2 p_entityID = 0;

      
      
      while(fnp_node)
      {
            fnp = fnp_node->data.ptrvalue;
            _class = get_current_class(fnp);
            if(!has_prev || fnp->entityID != p_entityID)
            {
                  seg = CreateSegment(p_seg, fnp->entityID, 0);
                  p_entityID = fnp->entityID;
                  has_prev = FALSE; /*force reset of the current style*/
            }
            if(!has_prev || ptype != _class)
            {
                  /*the border is drawn only when the segment is drawn as a box */
                  style = GetMuskCParam(_class, MSM_SEG_BORD, MSM_TRUEFALSE);
                  if(style != 0)
                  {
                        style = GetMuskCParam(_class, MSM_SEGMENT, MSM_STYLE);
                        segstyle = (style & MSM_SEG_FORM);
                        show_border = (segstyle == MSM_SEG_BOX);
                        if(show_border)
                        {
                              add_attribute_pen(seg, _class, MSM_SEG_BORD);
                              if(show_arrow)
                                    arrow = (Boolean)(style & (Int4)MSM_SEG_SHOWORIENT);
                              else
                                    arrow = FALSE;
                        }
                  }
                  else
                        show_border = FALSE;
                  has_prev = TRUE;
                  ptype = _class;
            }
            if(show_border)
                  DrawFeatNodeSegment(fnp, segstyle, MSM_SEG_BORD, seg, arrow, show_interval);
            fnp_node = fnp_node->next;
      }
}

static void AddFeatNodeListSegments(SegmenT p_seg, ValNodePtr fnp_node, Boolean show_arrow, Boolean show_interval)
{
      Int4 style, segstyle = 0;
      FeatNodePtr fnp;
      Boolean has_prev = FALSE;
      Int2 ptype = 0, _class;
      Boolean show_segments = FALSE;
      Boolean arrow = FALSE;
      SegmenT seg = NULL;
      Uint2 p_entityID = 0;
            
      while(fnp_node)
      {
            fnp = fnp_node->data.ptrvalue;
            if(!has_prev || fnp->entityID != p_entityID)
            {
                  seg = CreateSegment(p_seg, fnp->entityID, 0);
                  p_entityID = fnp->entityID;
                  has_prev = FALSE; /*force to reset the style*/
            }
            _class = get_current_class(fnp);
            if(!has_prev || ptype != _class)
            {

                  style = GetMuskCParam(_class, MSM_SEGMENT, MSM_STYLE);
                  segstyle = (style & MSM_SEG_FORM);
                  if(segstyle == MSM_SEG_BOX || segstyle == MSM_SEG_LINE)
                  {
                        add_attribute_pen(seg, _class, MSM_SEGMENT);
                        has_prev = TRUE;
                        ptype = _class;
                        show_segments = TRUE;
                        if(show_arrow)
                              arrow = (Boolean)(style & (Int4)MSM_SEG_SHOWORIENT);
                        else
                              arrow = FALSE;
                  }
            }
            if(show_segments)
                  DrawFeatNodeSegment(fnp, segstyle, MSM_SEGMENT, seg, arrow, show_interval);
            fnp_node = fnp_node->next;
      }
}


static ValNodePtr create_flat_node (ValNodePtr fnp_node)
{
      ValNodePtr flat_list = NULL, prev = NULL, curr;
      FeatNodePtr fnp;
      Int4 segstyle, style;
      Int2 _class;

      while(fnp_node)
      {
            if(fnp_node->choice == OBJ_SEQFEAT)
            {
                  fnp = fnp_node->data.ptrvalue;
                  _class = get_current_class(fnp);
                  style = GetMuskCParam(_class, MSM_SEGMENT, MSM_STYLE);
                  segstyle = (style & MSM_SEG_FORM);
                  if(segstyle != MSM_SEG_BOX && segstyle != MSM_SEG_LINE)
                  {
                        curr = ValNodeNew(NULL);
                        curr->choice = fnp_node->choice;
                        curr->data.ptrvalue = fnp;
                        if(prev == NULL)
                              flat_list = curr;
                        else
                              prev->next = curr;
                        prev = curr;
                  }
            }
            fnp_node = fnp_node->next;
      }

      return flat_list;
}

Boolean DrawFeatNode (ValNodePtr fnp_node, SegmenT seg, Uint1 label_type, Boolean show_arrow, Int4 scale, GeneDataPtr gdata)
{
      Uint1 label_align;
      Boolean show_interval = TRUE;
      ValNodePtr flat_list;

      label_align = (Uint1)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_STYLE);
      flat_list = create_flat_node (fnp_node);
      if(flat_list != NULL)
      {
            if(label_type != NO_LABEL)
                  AddFeatNodeListLabel(seg, flat_list, label_align, scale, gdata);
            AddFlatNodeSymbol(seg, flat_list, scale);
            ValNodeFree(flat_list);
      }

      if(scale > GetMuskCParam(MSM_TOPSTYLE, MSM_ENDS, MSM_SCALE))
            show_interval = FALSE;
      label_align = (Uint1)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_STYLE);

      if(fnp_node == NULL || fnp_node->choice !=OBJ_SEQFEAT)
            return FALSE;
      if(label_type != NO_LABEL)
            AddFeatNodeListLabel(seg, fnp_node, label_align, scale, gdata);
      if(show_interval)
            AddFeatNodeListGaps(seg, fnp_node);
      AddFeatNodeListSegments(seg, fnp_node, show_arrow, show_interval);
      AddFeatNodeListBorder(seg, fnp_node, show_arrow, show_interval);

            /**if(draw_trunc)
            {                 
                  if(fnp->extremes.l_trunc)
                        DrawSquiggle(seg, e_left, fnp->line, fnp->itemID, TRUE);
                  if(fnp->extremes.r_trunc)
                        DrawSquiggle(seg, e_right, fnp->line, fnp->itemID, FALSE);
            }**/

      return TRUE;
}

static Uint1 get_bin_symbol(Uint2 bin_order)
{
      switch(bin_order%4)
      {
            case 0:
                  return UP_TRIANGLE_SYMBOL;
            case 1:
                  return LEFT_TRIANGLE_SYMBOL;
            case 2:
                  return DOWN_TRIANGLE_SYMBOL;
            default:
                  return RIGHT_TRIANGLE_SYMBOL;
      }
}

Uint1 get_map_type(Uint4 extra)
{
      if(extra & EXTRA_FRAME_WORK)
            return FRAME_WORK;

      if(extra & EXTRA_RECMIN)
            return RECMIN;

      if(extra & EXTRA_LIKELY)
            return LIKELY;

      if(extra & EXTRA_MDUP)
            return MDUP;

      if(extra & EXTRA_DUP)
            return DUP;

      if(extra & EXTRA_CONTIG_STS)
            return CONTIG_STS;

      return 0;
}

static Uint1Ptr get_symbol_color(Uint4 extra)
{
      if(extra & EXTRA_YAC_END)
            return GREEN_COLOR;
      if(extra & EXTRA_RANDOM)
      {     /*this is a gold color */
            custom_color[0] = 255;
            custom_color[1] = 200;
            /* custom_color[1] = 215; */
            custom_color[2] = 0;
            return custom_color;
      }
      if(extra & EXTRA_GENETIC)
            return MAGENTA_COLOR;
      if(extra & EXTRA_GENE)
            return RED_COLOR;
      if(extra & EXTRA_EST)
            return BLUE_COLOR;
      if(extra & EXTRA_MISC) 
            return CYAN_COLOR;
      return BLACK_COLOR;
}

static Boolean  AddFlatNodeSymbol(SegmenT pseg, ValNodePtr fnp_node, Int4 scale)
{
      FeatNodePtr fnp;
      Boolean has_prev = FALSE;
      SegmenT seg = NULL;
      Int2 ptype, _class;
      Int4 symbol_height = 8;
      Int4 middle, center;
      /*Int4 color_val;*/
      Int4 segstyle;
      /*Uint1 color[3];*/
      Uint1Ptr this_color;
      Uint1 symbol_type = 0, symbol;
      /*Uint2 bin_order;*/
      Boolean fill;
      Uint1 map_mark_type;
      Uint2 p_entityID;

      if(fnp_node == NULL)
            return FALSE;
      ptype = 0;
      p_entityID = 0;
      while(fnp_node)
      {
            fnp = fnp_node->data.ptrvalue;
            _class = get_current_class(fnp);
            fnp = fnp_node->data.ptrvalue;
            if(!has_prev || fnp->entityID != p_entityID)
            {
                  seg = CreateSegment(pseg, fnp->entityID, 0);
                  p_entityID = fnp->entityID;
                  has_prev = FALSE;
            }
            if(!has_prev || ptype != _class)
            {
                  /*color_val = GetMuskCParam(_class, MSM_SEGMENT, MSM_COLOR);
                  convert_color(color_val, color);
                  AddAttribute(seg, COLOR_ATT, &(color[0]), 0, 0, 0, 0);*/
                  segstyle = GetMuskCParam(_class, MSM_SEGMENT, MSM_STYLE);
                  switch(segstyle & MSM_SEG_FORM)
                  {
                        case MSM_SEG_SYM_RECT:
                              symbol_type = RECT_SYMBOL;
                              break;
                        case MSM_SEG_SYM_DIAMOND:
                              symbol_type = DIAMOND_SYMBOL;
                              break;
                        case MSM_SEG_SYM_OVAL:
                              symbol_type = OVAL_SYMBOL;
                              break;
                        case MSM_SEG_SYM_TRIANGLE:
                              symbol_type = UP_TRIANGLE_SYMBOL;
                              break;
                        default:
                              symbol_type = DIAMOND_SYMBOL;
                              break;
                  }
                  
                  ptype = _class;
            }
            this_color = get_symbol_color(fnp->extra_data);
            AddAttribute(seg, COLOR_ATT, this_color, 0, 0, 0, 0);
            map_mark_type = get_map_type(fnp->extra_data);
            if(fnp->bin_order != 0)
                  symbol = get_bin_symbol(fnp->bin_order);
            else
                  symbol = symbol_type;
            fill = TRUE;
            if(map_mark_type == LIKELY || map_mark_type == MDUP || map_mark_type == DUP || map_mark_type == CONTIG_STS)
                  fill = FALSE;
            center = (fnp->extremes.left + fnp->extremes.right)/2;
            middle = (fnp->top + fnp->bottom)/2;
            AddSymbol(seg, center, middle, symbol, fill, MIDDLE_CENTER, fnp->itemID);
            if(map_mark_type == FRAME_WORK)
            {
                  AddSymbol(seg, center-8*scale, middle, symbol, TRUE, MIDDLE_CENTER, fnp->itemID);
                  AddSymbol(seg, center+8*scale, middle, symbol, TRUE, MIDDLE_CENTER, fnp->itemID);
            }
            p_entityID = fnp->entityID;
            fnp_node = fnp_node->next;
      }
      return TRUE;
}

Boolean DrawFlatNode(ValNodePtr vnp, SegmenT seg, Uint1 label_type, Int4 scale, GeneDataPtr gdata)
{
      Uint1 label_align;

      if(label_type == UPPER_LABEL)
            label_align = MSM_LABEL_TOP;
      else
            label_align = MSM_LABEL_BOTTOM;
      AddFeatNodeListLabel(seg, vnp, label_align, scale, gdata);
      AddFlatNodeSymbol(seg, vnp, scale);
      return TRUE;
}


static void AddAlignmentLabel(ValNodePtr anp_list, SegmenT seg)
{
      FonT font;
      Int4 color_val;
      Uint1 color[3];
      AlignNodePtr anp;
      Uint1 label_align;
      Uint2 itemID;
      
      
      font = (FonT)GetMuskCParam(MSM_SEQUENCE, MSM_FLABEL, MSM_FONT);
      SelectFont(font);
      color_val = GetMuskCParam(MSM_SEQUENCE, MSM_FLABEL, MSM_COLOR);
      convert_color(color_val, color);
      AddAttribute(seg, COLOR_ATT|SHADING_ATT, &(color[0]), 0, SOLID_SHADING, 0, COPY_MODE);

      label_align = (Uint1)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_STYLE);
      while(anp_list)
      {
            anp = anp_list->data.ptrvalue;
            if(anp->use_seq_ids)
                  itemID = anp->bsp_itemID;
            else
                  itemID = anp->itemID;
            AddLabelWithAlign(seg, label_align, anp->extremes.left, anp->extremes.right, anp->top, anp->bottom, anp->label, font, itemID);
            anp_list = anp_list->next;
      }
      /*DeleteFont(font);*/
}


static Uint1Ptr get_color_for_block (AlignNodePtr anp, Uint2 val)
{
      switch(anp->index)
      {
            case 0:
                  return get_seg_color((Int2)val);
            case 1:     /*it is a map index */
                  if(val == MAP_STATUS_Mapping)
                        return MAGENTA_COLOR;
                  if(val == MAP_STATUS_Mapped)
                        return RED_COLOR;
                  if(val == MAP_STATUS_Paused)
                        return BLUE_COLOR;
                  return BLACK_COLOR;
            case 2:     /*it is a sequence index*/
                  if(val == SEQ_STATUS_Plan)
                        return CYAN_COLOR;
                  if(val == SEQ_STATUS_Sequencing)
                        return MAGENTA_COLOR;
                  if(val == SEQ_STATUS_Sequenced)
                        return RED_COLOR;
                  if(val == SEQ_STATUS_Paused)
                        return BLUE_COLOR;
                  return BLACK_COLOR;
            default:
                  return BLACK_COLOR;
      }
}

                  
static void AddAlignBlocks(ValNodePtr anp_list, SegmenT seg)
{
      AlignNodePtr anp;
      Uint1Ptr color;
      AlignBlockPtr abp;
      Int2 arrow;
      Int2 c_num;
      Uint2 itemID;

      
      c_num = (Int2)GetMuskCParam(MSM_CCOLOR, MSM_NOSUBCLASS, MSM_NUM);
      while(anp_list)
      {
            anp = anp_list->data.ptrvalue;
            if(anp->use_seq_ids)
                  itemID = anp->bsp_itemID;
            else
                  itemID = anp->itemID;
            for(abp = anp->blocks; abp != NULL; abp = abp->next)
            {
                  arrow = get_arrow_for_strand(abp->gr.strand);
                  color = get_color_for_block (anp,(Int2)(abp->order)); 

                  /* pseg = CreateSegment(seg, OBJ_BIOSEQ_SEG, 0);
                  sseg = CreateSegment(pseg, anp->entityID, 0);
                  AddAttribute(sseg, COLOR_ATT, color, 0, 0, 0, 0);
                  AddRectangle(sseg, abp->gr.left, anp->top, abp->gr.right, anp->bottom, arrow, TRUE, abp->order); */
                  AddAttribute(seg, COLOR_ATT, color, 0, 0, 0, 0);
                  AddRectangle(seg, abp->gr.left, anp->top, abp->gr.right, anp->bottom, arrow, TRUE, itemID);
            }
            anp_list = anp_list->next;
      }
}

static void AddSeqWithAlignmentSymbol(SegmenT seg, ValNodePtr anp_list, Int4 scale)
{
      Int4 length, width;
      AlignNodePtr anp;
      Int4 left, right, center;
      Uint2 itemID;

      
      length = scale *4;
      width = GetMuskCParam(MSM_TOPSTYLE, MSM_SPACE, MSM_HEIGHT);
      if(width > 3)
            width = 3;

      while(anp_list)
      {
            anp = anp_list->data.ptrvalue;
            if(anp && anp->seq_has_align == TRUE)
            {
                  center = (anp->extremes.left + anp->extremes.right)/2;
                  left = center - length/2;
                  right = center + length/2;
                  if(anp->use_seq_ids)
                        itemID = anp->bsp_itemID;
                  else
                        itemID = anp->itemID;
                  AddRectangle(seg, left, anp->top + width, right, anp->top, FALSE, TRUE, itemID);
            }

            anp_list = anp_list->next;
      }

}
/*#######################################################################
#
#     functions related to DrawAlignNode
#
#######################################################################*/


static void AddRepeatsToAlignment (ValNodePtr anp_list, SegmenT seg)
{
      AlignNodePtr anp;
      AlignSegPtr asp;
      Int4 left, right;
      ValNodePtr f_node;
      FeatNodePtr fnp;
      Uint2 itemID;
      Uint1 arrow;

      AddAttribute(seg, COLOR_ATT|SHADING_ATT, BLACK_COLOR, 0, LIGHT_SHADING, 0, 0);
      while(anp_list)
      {
            if(anp_list->choice != OBJ_SEQANNOT)
            {
                  anp = anp_list->data.ptrvalue;
                  if(anp != NULL &&  anp->segs != NULL)
                  {
                        if(anp->use_seq_ids)
                              itemID = anp->bsp_itemID;
                        else
                              itemID = anp->itemID;
                        for(asp = anp->segs; asp != NULL; asp = asp->next)
                        {
                              f_node = asp->cnp;
                              while(f_node)
                              {
                                    fnp = f_node->data.ptrvalue;
                                    arrow = NO_ARROW;
                                    left = fnp->extremes.right;
                                    right =fnp->extremes.left;
                                    if(anp->extremes.strand == Seq_strand_minus)
                                    {
                                          if(fnp->extremes.left == anp->extremes.left)
                                                arrow = LEFT_ARROW;
                                    }
                                    else
                                    {
                                          if(fnp->extremes.right == anp->extremes.right)
                                                arrow = RIGHT_ARROW;
                                    }
                                    
                                    if(fnp->feattype == FEATDEF_repeat_region || fnp->feattype == FEATDEF_repeat_unit)
                                          AddRectangle(seg, left, asp->top-1, right, asp->bottom+1, arrow, TRUE, itemID);
                                    f_node = f_node->next;
                              }
                        }
                  }
            }
            anp_list = anp_list->next;
      }
      AddAttribute(seg, COLOR_ATT|SHADING_ATT, BLACK_COLOR, 0, SOLID_SHADING, 0, COPY_MODE);
}


static void DrawAlignSegs(SegmenT seg, AlignNodePtr anp)
{
      Int4 i_left, i_right, e_left, e_right;
      Int4 p_left = 0, p_right = 0, p_top = 0, p_bottom = 0;
      AlignSegPtr asp;
      Int2 c_arrow, p_arrow = 0;
      Uint1 strand;
      Boolean has_prev = FALSE;
      Boolean load;
      Int4 seglen;
      Uint2 itemID;

      

      /*add each segment in alignment*/
      strand = anp->extremes.strand;
      e_left = anp->extremes.left;
      e_right = anp->extremes.right;
      if(anp->use_seq_ids)
            itemID = anp->bsp_itemID;
      else
            itemID = anp->itemID;
      
      
      if(anp->extremes.l_trunc == TRUE)
            DrawSquiggle(seg, e_left, anp->bottom, itemID, TRUE);
      if(anp->extremes.r_trunc == TRUE)
            DrawSquiggle(seg, e_right, anp->bottom, itemID, FALSE);

      if(anp->segs == NULL)   /*no intervals*/
      {
            c_arrow = get_arrow_for_strand(strand);
            if(ABS(anp->bottom - anp->top) <= 3)
                  AddLine(seg, e_left, anp->top, e_right, anp->top, FALSE, itemID);
            else
                  AddRectangle(seg, e_left, anp->top, e_right, anp->bottom, c_arrow, FALSE, itemID);
            return;
      }
      


      for(asp = anp->segs; asp !=NULL; asp = asp->next)
      {
            switch(asp->type)
            {
                  case INS_SEG:
                        seglen = asp->gr.right;
                        AddRectangle(seg, asp->gr.left, asp->top, (asp->gr.left+seglen-1), asp->bottom, NO_ARROW, FALSE, itemID);
                        AddLine(seg, asp->ins_pos, anp->bottom, asp->ins_pos, asp->top, FALSE, itemID);
                        break;
                  case GAP_SEG:
                        AddLine(seg, asp->gr.left, asp->top, asp->gr.right, asp->top, FALSE, itemID);
                        break;
                  default:
                        i_left = asp->gr.left;
                        i_right = asp->gr.right;
                        c_arrow = NO_ARROW;
                        if(i_left == e_left && strand == Seq_strand_minus)
                              c_arrow = LEFT_ARROW;
                        if(i_right == e_right && strand == Seq_strand_plus)
                              c_arrow = RIGHT_ARROW;
                        load = FALSE;
                              
                        if(has_prev)
                        {
                              if(p_right+1 == i_left)
                              {
                                    p_right = i_right;
                                    if(strand != Seq_strand_minus)
                                          p_arrow = c_arrow;
                                    load = TRUE;
                              }
                              else
                                    AddRectangle(seg, p_left, asp->top, p_right, asp->bottom, p_arrow, FALSE, itemID);
                        }
                        if(!load)
                        {
                              p_left = i_left;
                              p_right = i_right;
                              p_top = asp->top;
                              p_bottom = asp->bottom;
                              p_arrow = c_arrow;
                        }
                        has_prev = TRUE;
                        break;
            }
      }
      if(has_prev)
            AddRectangle(seg, p_left, p_top, p_right, p_bottom, p_arrow, FALSE, itemID);


}

static void AddAlignmentSegs(ValNodePtr anp_list, SegmenT seg)
{
      AlignNodePtr anp;
      Int2 num;   /*counting the number of the follower groups*/
      Boolean p_follower = FALSE;

      add_attribute_pen(seg, MSM_SEQUENCE, MSM_SEG_BORD);
      num = 0;
      while(anp_list)
      {
            anp = anp_list->data.ptrvalue;
            if(anp->num_follower != 0 )
            {
                  ++num;
                  switch(num%3)
                  {
                        case 1:
                              AddAttribute (seg, COLOR_ATT, BLUE_COLOR, 
                                    NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
                              break;
                        case 2:
                              AddAttribute (seg, COLOR_ATT, CYAN_COLOR, 
                                    NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
                              break;
                        default:
                              AddAttribute (seg, COLOR_ATT, GREEN_COLOR, 
                                    NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
                              break;
                  }
            }
            else if(num > 0 && anp->follower == FALSE && p_follower == TRUE)
                  AddAttribute (seg, COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
            p_follower = anp->follower;
            DrawAlignSegs(seg, anp);
            anp_list = anp_list->next;
      }
}



typedef struct alignfeature{
      Uint2 itemID;
      Int4 e_left;
      Int4 e_right;
}AlignFeature, PNTR AlignFeaturePtr;

static void ck_load_itemID (Uint2 itemID, Int4 e_left, Int4 e_right, ValNodePtr PNTR item_list)
{
      ValNodePtr curr;
      AlignFeaturePtr afp;
      
            
      for(curr = *item_list; curr !=NULL; curr = curr->next)
      {
            afp = curr->data.ptrvalue;
            if(afp->itemID == itemID)
            {
                  afp->e_left = MIN(afp->e_left, e_left);
                  afp->e_right = MAX(afp->e_right, e_right);
                  return;
            }
      }

      afp = MemNew(sizeof(AlignFeature));
      afp->e_left = e_left;
      afp->e_right = e_right;
      afp->itemID = itemID;

      ValNodeAddPointer(item_list, 0, (Pointer)afp);
}
      

static ValNodePtr dup_interval_node(ValNodePtr list)
{
      ValNodePtr new = NULL;
      IvalNodePtr inp, n_inp;

      while(list)
      {
            inp = list->data.ptrvalue;
            n_inp = MemNew(sizeof(IvalNode));
            MemCopy((Pointer)n_inp, (Pointer)inp, sizeof(IvalNode));
            ValNodeAddPointer(&new, 0, n_inp);

            list = list->next;
      }

      return new;
}

static ValNodePtr get_last_node(ValNodePtr list)
{
      if(list == NULL)
            return NULL;

      while(list->next != NULL)
            list = list->next;
      return list;
}

static Boolean merge_feature_interval_node(ValNodePtr PNTR i_node, ValNodePtr list, GatherRangePtr grp_i, GatherRangePtr grp_l)
{
      ValNodePtr i_last, l_last, new;
      IvalNodePtr i_inp, l_inp;
      
      
      if(grp_i->right +1 == grp_l->left)
      {
            if(list != NULL)
            {
                  if(*i_node == NULL)
                        *i_node = dup_interval_node(list);
                  else
                  {
                        i_last = get_last_node(*i_node);
                        i_inp = i_last->data.ptrvalue;
                        l_inp = list->data.ptrvalue;
                        if(i_inp->gr.right +1 ==  l_inp->gr.left)
                        {
                              i_inp->gr.right = l_inp->gr.right;
                              i_last->next = dup_interval_node(list->next);
                        }
                        else
                              i_last->next = dup_interval_node(list);
                  }
            }
            grp_i->right = grp_l->right;
            return TRUE;
      }

      if(grp_i->left == grp_l->right +1)
      {
            if(list != NULL)
            {
                  if(*i_node == NULL)
                        *i_node = dup_interval_node(list);
                  else
                  {
                        new = dup_interval_node(list);
                        l_last = get_last_node(list);
                        l_inp = l_last->data.ptrvalue;
                        i_inp = (*i_node)->data.ptrvalue;
                        if(l_inp->gr.right +1 == i_inp->gr.left)
                        {
                              l_last = get_last_node(new);
                              l_inp = l_last->data.ptrvalue;
                              l_inp->gr.right = i_inp->gr.right;
                              l_last->next = (*i_node)->next;
                              (*i_node)->next = NULL;
                              ValNodeFreeData(*i_node);
                        }
                        else
                        {
                              ValNodeLink(&new, *i_node);
                              *i_node = new;
                        }
                  }

                  grp_i->left = grp_l->left;
            }

            return TRUE;
      }

      return FALSE;
}

            

static Boolean is_repeat_feature (Uint1 feattype)
{
      return (feattype == FEATDEF_repeat_region ||feattype == FEATDEF_repeat_unit);
}

static void AddAlignFeature(AlignNodePtr anp, SegmenT a_seg, Boolean show_arrow)
{
      ValNodePtr item_list = NULL, c_list;
      ValNodePtr i_node, p_node;
      Uint2 itemID;
      AlignSegPtr asp;
      FeatNodePtr fnp, c_fnp;
      GatherRange extremes;
      ValNode vn;
      ValNodePtr f_list;
      Uint1 ptype = 0;
      ValNodePtr repeat_node;
      
      SegmenT seg;
      Int4 offset;
      Uint1 strand;
      Boolean arrow;
      AlignFeaturePtr afp;
      

      /*collect a list of feature itemIDs for REG_SEGs because some
        may need to be merged later */
      /*features in INS_SEG+DIAG_SEG are drawn because they don't need 
        to be merged */ 
      for(asp = anp->segs; asp !=NULL; asp = asp->next)
      {
            if(asp->type == INS_SEG || asp->type == DIAG_SEG)
            {
                  if(asp->cnp !=NULL)
                  {
                        repeat_node = strip_repeat_feature(&(asp->cnp));
                        seg = CreateSegment(a_seg, OBJ_SEQFEAT, 0);
                        /*not showing any arrow here*/
                        DrawFeatNode (asp->cnp, seg, NO_LABEL, FALSE, 1, NULL);
                        if(repeat_node != NULL)
                              ValNodeLink(&(asp->cnp), repeat_node);
                        /*resume the previous layout for FeatNode*/
                        if(asp->type == INS_SEG)
                        {
                              offset = asp->gr.left - asp->ins_pos;
                              if(offset != 0)
                                    modify_insert_fnode(asp->cnp, (-offset));
                        }
                  }
            }
            if(asp->type == REG_SEG)      /*need some merging process*/
            {
                  for(f_list = asp->cnp; f_list !=NULL; f_list = f_list->next)
                  {
                        fnp = f_list->data.ptrvalue;
                        if(!is_repeat_feature (fnp->feattype))
                              ck_load_itemID (fnp->itemID, fnp->extremes.left, fnp->extremes.right, &item_list);
                  }
            }
      }
      
   if(item_list != NULL)
   {
            
      /*merge the features in REG_SEG according to their itemID*/       
      for(c_list = item_list; c_list != NULL; c_list = c_list->next)
      {
            seg = CreateSegment(a_seg, OBJ_SEQFEAT, 0);
            afp = c_list->data.ptrvalue;
            itemID = afp->itemID;
            c_fnp = NULL;
            i_node = NULL;
            for(asp = anp->segs; asp !=NULL; asp = asp->next)
            {     if(asp->type == REG_SEG)
                  {
                        for(f_list = asp->cnp; f_list !=NULL; f_list = f_list->next)
                        {
                              fnp = f_list->data.ptrvalue;
                              if(fnp->itemID == itemID && !is_repeat_feature(fnp->feattype))    /*find the match*/
                              {
                                    if(c_fnp == NULL)
                                    {
                                          c_fnp = fnp;
                                          if(fnp->interval != NULL)
                                                i_node = dup_interval_node(fnp->interval);
                                          MemCopy(&extremes, &(fnp->extremes), sizeof(GatherRange));
                                          
                                    }
                                    else if(!merge_feature_interval_node(&i_node, fnp->interval, &(c_fnp->extremes), &(fnp->extremes)))
                                    {
                                          if(show_arrow)
                                          {
                                                strand = c_fnp->extremes.strand;
                                                if(strand == Seq_strand_minus)
                                                      arrow = (c_fnp->extremes.left == afp->e_left);
                                                else
                                                      arrow = (c_fnp->extremes.right == afp->e_right);
                                          }
                                          else
                                                arrow = FALSE;
                                          p_node = c_fnp->interval;
                                          c_fnp->interval = i_node;
                                          vn.choice = OBJ_SEQFEAT;
                                          vn.data.ptrvalue = c_fnp;
                                          vn.next = NULL;
                                          DrawFeatNode (&vn, seg, NO_LABEL, arrow, 1, NULL);
                                          c_fnp->interval = p_node;
                                          MemCopy((Pointer)&(c_fnp->extremes), (Pointer)&extremes, sizeof(GatherRange));
                                          if(i_node != NULL)
                                                i_node = ValNodeFreeData(i_node);
                                          c_fnp = fnp;
                                          if(fnp->interval != NULL)
                                                i_node = dup_interval_node(fnp->interval);
                                          MemCopy((Pointer)&extremes, (Pointer)&(fnp->extremes), sizeof(GatherRange));
                                    }
                              }
                        
                        }
                  }
            }
                  
            if(c_fnp != NULL)
            {
                  if(show_arrow)
                  {
                        strand = c_fnp->extremes.strand;
                        if(strand == Seq_strand_minus)
                              arrow = (c_fnp->extremes.left == afp->e_left);
                        else
                              arrow = (c_fnp->extremes.right == afp->e_right);
                  }
                  else
                        arrow = FALSE;
                  p_node = c_fnp->interval;
                  c_fnp->interval = i_node;
                  vn.choice = OBJ_SEQFEAT;
                  vn.data.ptrvalue = c_fnp;
                  vn.next = NULL;
                  DrawFeatNode (&vn, seg, NO_LABEL, arrow, 1, NULL);
                  c_fnp->interval = p_node;
                  MemCopy((Pointer)&(c_fnp->extremes), (Pointer)&extremes, sizeof(GatherRange));
            }
            if(i_node != NULL)
                  i_node = ValNodeFreeData(i_node);
      }

      ValNodeFreeData(item_list);
   }

}

static void AddAlignmentFeature(ValNodePtr anp_node, SegmenT seg, Int4 scale)
{
      AlignNodePtr anp;
      Boolean show_arrow;

      show_arrow = (GetMuskCParam(MSM_TOPSTYLE, MSM_ENDS, MSM_SCALE) >= scale);
      
      while(anp_node)
      {
            anp = anp_node->data.ptrvalue;
            if(anp->segs !=NULL)
                  AddAlignFeature(anp, seg, show_arrow);
            anp_node = anp_node->next;
      }
}     

static void AddAlignmentMismatch(ValNodePtr anp_node, Int4 scale, SegmenT seg)
{
      Int4 i_right;
      Int4 top, bottom;
      Int4 p_i, j;
      ValNodePtr curr, list;
      AlignNodePtr anp;
      AlignSegPtr asp;
      Int4 symbol_width = 8;
      Int2 p_type = -1, type;
      Uint2 itemID;

      AddAttribute (seg, WIDTH_ATT | COLOR_ATT, RED_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      
      for(list = anp_node; list !=NULL; list = list->next)
      {
            anp = list->data.ptrvalue;
            p_i = -1;
            if(anp->use_seq_ids)
                  itemID = anp->bsp_itemID;
            else
                  itemID = anp->itemID;
            for(asp = anp->segs; asp !=NULL; asp = asp->next)
            {
                  i_right = asp->gr.right;
                  top = asp->top;
                  bottom = asp->bottom;
                  for(curr = asp->mismatch; curr!=NULL; curr = curr->next)
                  {
                        j = curr->data.intvalue;
                        type = curr->choice;
                        if(type == 0)
                              symbol_width = 1;
                        else
                              symbol_width = 8;
                        if(p_i == -1 || (j-p_i) > scale * symbol_width || p_type != type)
                        {
                              switch (type)
                              {
                                    case MISMATCH_OPEN:
                                          AddSymbol(seg, j, (top+bottom)/2, OVAL_SYMBOL, FALSE, MIDDLE_CENTER, itemID);
                                          break;
                                    case MISMATCH_CLOSE:
                                          AddSymbol(seg, j, (top+bottom)/2, OVAL_SYMBOL, TRUE, MIDDLE_CENTER, itemID);
                                          break;
                                    case MISMATCH_SQUARE:
                                          AddSymbol(seg, j, (top+bottom)/2, RECT_SYMBOL, TRUE, MIDDLE_CENTER, itemID);
                                          break;
                                    default:
                                          AddLine(seg, j, asp->top, j, (asp->bottom), FALSE, itemID);
                                          break;
                              }
                              p_i = j;
                              p_type = type;
                        }
                  }
            }
      }
}

static ValNodePtr extra_align_node(ValNodePtr anp_node, Boolean use_seq_ids)
{
      ValNodePtr head = NULL;
      AlignNodePtr anp;

      while(anp_node)
      {
            anp = anp_node->data.ptrvalue;
            if(anp->use_seq_ids == use_seq_ids)
                  ValNodeAddPointer(&head, anp_node->choice, anp);
            anp_node = anp_node->next;
      }

      return head;
}

/**********************************************************************
*
*     DrawAlignNode(vnp, scale, seg)
*     Draw a list of AlignNode. 
*     1) It always labels the sequences
*     2) the label is always on top of the sequence
*     3) right now, it does NOT show the truncation
*
**********************************************************************/
Boolean DrawAlignNode(ValNodePtr vnp, Int4 scale, SegmenT seg)
{
      ValNodePtr anp_node, curr, next;
      AlignNodePtr anp;
      SegmenT t_seg, p_seg;

      if(vnp == NULL)
            return FALSE;

      anp_node = extra_align_node(vnp, TRUE);
      if(anp_node != NULL)
      {
            p_seg = CreateSegment(seg, OBJ_BIOSEQ, 0);
            curr = anp_node;
            while(curr)
            {
                  next = curr->next;
                  curr->next = NULL;
                  anp = curr->data.ptrvalue;
                  t_seg = CreateSegment(p_seg, anp->seq_entityID, 0);
                  AddAlignmentSegs(curr, t_seg);
                  AddSeqWithAlignmentSymbol(t_seg, curr, scale);
                  AddAlignmentMismatch(curr, scale, t_seg);
                  AddAlignmentFeature(curr, t_seg, scale);
                  AddAlignBlocks(curr, t_seg);  /*add the blocks that are used in segmented seq*/
                  AddAlignmentLabel(curr, t_seg);
                  AddRepeatsToAlignment (curr, t_seg); 
                  curr->next = next;
                  curr = next;
            }
            ValNodeFree(anp_node);
      }

            
      anp_node = extra_align_node(vnp, FALSE);
      if(anp_node != NULL)
      {
            AddAlignmentSegs(anp_node, seg);
            AddSeqWithAlignmentSymbol(seg, anp_node, scale);
            AddAlignmentMismatch(anp_node, scale, seg);
            AddAlignmentFeature(anp_node, seg, scale);
            AddAlignBlocks(anp_node, seg);      /*add the blocks that are used in segmented seq*/
            AddAlignmentLabel(anp_node, seg);
            AddRepeatsToAlignment(anp_node, seg); 
            ValNodeFree(anp_node);
      }

      return TRUE;
}
      

Boolean DrawFlatAlign(SegmenT seg, ValNodePtr anp_list)
{
      Uint2 itemID;
      AlignNodePtr anp;
      Int4 pos;

      if(anp_list == NULL)
            return FALSE;
      AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      while(anp_list)
      {
            if(anp_list->choice != OBJ_SEQANNOT)
            {
                  anp = anp_list->data.ptrvalue;
                  itemID = anp->itemID;
                  pos = anp->extremes.left;
                  
                  AddSymbol(seg, pos, anp->line, DIAMOND_SYMBOL, TRUE, UPPER_CENTER, itemID);
      
            }
            anp_list = anp_list->next;
      }

      return TRUE;
}

      

/*************************************************************************
*
*     DrawGeneticMap(features, mpp, pic, scale)
*     draw the genetic map from featnode
*     features: the list of FeatNode, will be resorted
*     mpp: the current map position. The drawing will recalculate
*     mpp->seq_top and mpp->bottom
*     pic: the drawing picture
*     scale: the maximum scale in drawing. Will be used in the layout
*     gdata: to include the possible selected marker
*
*************************************************************************/
Boolean DrawGeneticMap(ValNodePtr PNTR features, MapPosPtr mpp, SegmenT pic, Int4 scale, GeneDataPtr gdata, ValNodePtr PNTR image_list)
{
      SegmenT seg;
      Int4 top;
      ValNodePtr g_node;
      Boolean retval = FALSE;

      seg = CreateSegment(pic, OBJ_BIOSEQ_MAPFEAT, 0);
      g_node = extract_node_list(features, OBJ_BIOSEQ_MAPFEAT, 0, 0, ALL_LABEL);
      top = mpp->seq_top;
      AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      if(g_node)
      {
            top = LayoutNodeFlat(&g_node, scale, top, UPPER_LABEL, FALSE);
            if(image_list != NULL)
                  load_align_label_rectangle(g_node, image_list, scale, TRUE);
            DrawFlatNode(g_node, seg, UPPER_LABEL, scale, gdata);
            FreeFeatureList(g_node);
            mpp->seq_bottom = mpp->seq_top = top;
            retval = TRUE;
      }
      AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      AddLine(seg, mpp->left, mpp->seq_top, mpp->right, mpp->seq_top, FALSE, 0);
      DrawSeqScale(seg, mpp->slp, mpp->left, mpp->seq_top, scale, FALSE);
      mpp->bottom = mpp->seq_bottom - SCALE_SPACE;
      return retval;
}



/*************************************************************************
*
*     DrawPhysicalMap(features, mpp, pic, scale)
*     draw the physical map from featnode
*     features: the list of FeatNode, will be resorted
*     mpp: the current map position. The drawing will recalculate
*     mpp->seq_top and mpp->bottom
*     pic: the drawing picture
*     scale: the maximum scale in drawing. Will be used in the layout
*
*************************************************************************/
Boolean DrawPhysicalMap(ValNodePtr PNTR features, MapPosPtr mpp, SegmenT pic, Int4 scale, ValNodePtr PNTR image_list)
{
      SegmenT pseg, seg;
      Int4 top;
      ValNodePtr g_node;
      Boolean retval = FALSE;

      pseg = CreateSegment(pic, OBJ_BIOSEQ_MAPFEAT, 0);
      seg = CreateSegment(pseg, mpp->entityID, 0);
      g_node = extract_node_list(features, OBJ_SEQFEAT, 0, 0, NUM_LABEL);
      top = mpp->seq_top;
      AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      AddLine(seg, mpp->left, top, mpp->right, top, FALSE, 0); 
      if(g_node)
      {
            top = LayoutNodeFlat(&g_node, scale, top, LOWER_LABEL, FALSE);
            if(image_list != NULL)
                  load_align_label_rectangle(g_node, image_list, scale, TRUE);
            DrawFlatNode(g_node, pseg, LOWER_LABEL, scale, NULL);
            top -= (TICK_LEN+LABEL_SPACE);
            FreeFeatureList(g_node);
            mpp->seq_bottom = mpp->seq_top;
            retval = TRUE;
      }
      else
            DrawSeqScale(pic, mpp->slp, mpp->left, mpp->bottom, scale, FALSE);

      top -= (LABEL_SPACE+TICK_LEN);
      mpp->bottom = top;
      return retval;
}


static ValNodePtr sort_same_itemID(ValNodePtr head)
{
      FeatNodePtr fnp;
      ValNodePtr list = NULL, new;

      while(head)
      {
            fnp = head->data.ptrvalue;
            new = merge_same_itemID(&head, fnp->itemID);
            ValNodeLink(&list, new);
      }

      return list;
}
      

                  
/***********************************************************************
*
*     DrawRestrictionMap(features, mpp, pic, rsite_flat, strand)
*     draw the restriction map
*     features: the FeatNode contains the info for restriction map
*     mpp: the map position
*     pic: picture
*     rsite_flat: if(TRUE), all the enzymes are shown in one line. 
*     strand: the orientation of the map
*
***********************************************************************/
Boolean DrawRestrictionMap(ValNodePtr PNTR features, MapPosPtr mpp, SegmenT pic, Boolean rsite_flat, Uint1 strand, Int4 scale)
{

      Int4 x1, x2, center;
      Int2 arrow;
      ValNodePtr seq_node, g_node, curr, c_head;
      FeatNodePtr fnp;
      IvalNodePtr inp;
      ValNodePtr ivals;
      Int4 top;
      Int2 i;
      Uint1Ptr color;
      Boolean retval = FALSE;
      SegmenT pseg, seg;
      Int4 seq_width;
      FonT font;

      font = (FonT)GetMuskCParam(MSM_SEQUENCE, MSM_SLABEL, MSM_FONT);
      SelectFont(font);
      seq_width = FontHeight();
      /* seq_width = GetMuskCParam(MSM_SEQUENCE, MSM_SEGMENT, MSM_HEIGHT); */
      arrow = get_arrow_for_strand(strand);
      x1 = mpp->left;
      x2 = mpp->right;
      center = (x1+x2)/2;
      top = mpp->seq_top;

      pseg = CreateSegment(pic, OBJ_BIOSEQ_MAPFEAT, 0);
      seg = CreateSegment(pseg, mpp->entityID, 0);
      if(rsite_flat)
      {
            seq_node = extract_node_list(features, OBJ_BIOSEQ, 0, 0, ALL_LABEL);
            fnp = seq_node->data.ptrvalue;
            AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
            AddRectangle (seg, x1, top, x2, top-seq_width, arrow, FALSE, fnp->itemID);
            AddLabel(seg, x1, top, fnp->label, SMALL_TEXT, 0, LOWER_CENTER, fnp->itemID);
            mpp->seq_top = top;
            mpp->seq_bottom = top - seq_width;
            top -= LABEL_SPACE;
            FreeFeatureList(seq_node);
            mpp->bottom = top;
      }

      g_node = extract_node_list(features, OBJ_BIOSEQ_MAPFEAT, 0, 0, ALL_LABEL);
      if(g_node == NULL)
            return FALSE;
      i = 0;
      while(g_node)
      {
            fnp = (FeatNodePtr)(g_node->data.ptrvalue);
            c_head  = merge_same_itemID(&g_node, fnp->itemID);
            if(c_head)
            {
                  if(!rsite_flat)
                  {
                        (mpp->seq_bottom) -=seq_width;
                        top = mpp->seq_bottom + seq_width;
                        AddAttribute (seg, WIDTH_ATT | COLOR_ATT,BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, COPY_MODE);
                        AddLabel(seg, mpp->left, top+seq_width/2, fnp->label, SMALL_TEXT, 5, LOWER_LEFT, fnp->itemID);
                        AddLine(seg, mpp->left, top - seq_width, mpp->right, top-seq_width, FALSE, 0);
                        if(i == 0)
                              AddLine(seg, mpp->left, top, mpp->right, top, FALSE, 0);
                  }

               for(curr = c_head; curr !=NULL; curr = curr->next)
               {
                  fnp = (FeatNodePtr)(curr->data.ptrvalue);
                  color = get_enz_color(i);
                  AddAttribute (seg, WIDTH_ATT | COLOR_ATT,color, NO_LINE_STYLE, NO_SHADING, 1, COPY_MODE);
                  ivals = fnp->interval;
                  while(ivals)
                  {
                        inp = ivals->data.ptrvalue;
                        AddLine(seg, inp->gr.left, (mpp->seq_bottom+seq_width), inp->gr.left, mpp->seq_bottom, FALSE, fnp->itemID);
                        ivals = ivals->next;
                  }
               }
               if(!rsite_flat)
                  top = mpp->seq_bottom;
               ++i;
               FreeFeatureList(c_head);
            }
      }
      DrawSeqScale(seg, mpp->slp, mpp->left, mpp->seq_bottom, scale, FALSE);
      mpp->bottom = mpp->seq_bottom - SCALE_SPACE;
      return TRUE;
}
            
/***********************************************************************
*
*     DrawCytoMap(features, mpp, scale, pic)
*     draw the cytogenetic map
*     features: the FeatNode for cytogenetic map
*     scale: maximum scale, used in layout
*     pic: the drawing segment
*
***********************************************************************/
static Boolean label_cyto_band(Int4 x1, Int4 x2, Int4Ptr prev, CharPtr label, Int4 scale)
{
      Int4 center;
      Int4 half_len;

      half_len = StringWidth(label)/2;
      center = (x1+x2)/(2 * scale);
      if((*prev == -1) || ((center - half_len) > (*prev)))
      {
            *prev = center + half_len;
            return TRUE;
      }
      else
            return FALSE;
}

Boolean DrawCytoMap(ValNodePtr PNTR features, MapPosPtr mpp, Int4 scale, SegmenT pic)
{
      Int4 x1, x2, center;
      SegmenT seg, pseg;
      Int4 top;
      Int4 prev_pos = -1;
      ValNodePtr g_node, curr;
      FeatNodePtr fnp;
      Boolean retval = FALSE;
      Uint1Ptr color = NULL;
      Uint1 shading = 0;
      CharPtr subdiv = NULL;
      Int2 numdiv =0;
      Int4 prev_right = -1;
      Int4 seq_width;


      top = mpp->seq_top;
      seq_width = GetMuskCParam(MSM_SEQUENCE, MSM_SEGMENT, MSM_HEIGHT);

      pseg = CreateSegment(pic, OBJ_BIOSEQ_MAPFEAT, 0);
      seg = CreateSegment(pseg, mpp->entityID, 0);

      g_node = extract_node_list(features, OBJ_BIOSEQ_MAPFEAT, 0, 0, ALL_LABEL);
      if(g_node)
      {
            for(curr = g_node; curr!=NULL; curr = curr->next)
            {
                  fnp = curr->data.ptrvalue;
                  switch(fnp->band)
                  {
                        case BND:
                              color = BLACK_COLOR;
                              break;
                        case HET :
                              color = BLUE_COLOR;
                              break;
                        case TEL:
                        case CEN:
                              color = RED_COLOR;
                              shading = SOLID_SHADING;
                              break;
                        case BAND_POINT:
                              color = RED_COLOR;
                              shading = SOLID_SHADING;
                              break;
                        case GIEMSA_POS:
                              color = BLACK_COLOR;
                              shading = SOLID_SHADING;
                              break;
                        case GIEMSA_NEG:
                              color = WHITE_COLOR;
                              shading = SOLID_SHADING;
                              break;
                        case ACRO_CENTRIC:
                              color = BLACK_COLOR;
                              shading = THICK_NWSE_SHADING;
                              break;
                        case VARIABLE_REG:
                              color = BLACK_COLOR;
                              shading = THIN_NESW_SHADING;
                              break;

                        case MISC_BND:
                              color = BLACK_COLOR;
                              shading = SOLID_SHADING;
                              break;
                        
                        default:
                              Message(MSG_ERROR, "Illegal type");
                              exit(1);
                  }

                  if(fnp->band == BND || fnp->band == HET)
                  {
                        if(numdiv == 0)
                        {
                              ++numdiv;
                              subdiv = fnp->pos_label;
                        }
                        else
                        {
                              if(StringCmp(subdiv, fnp->pos_label))
                              {
                                    ++numdiv;
                                    subdiv = fnp->pos_label;
                              }
                        }
                        if(numdiv%2 == 0)
                              shading = THIN_NESW_SHADING;
                        else
                              shading = THIN_NWSE_SHADING;
                  }
                  
                  x1= fnp->extremes.left;
                  x2= fnp->extremes.right;
                  if(prev_right !=-1 && (x1 - prev_right) > scale)
                  {
                        AddAttribute (seg, SHADING_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, SOLID_SHADING, 1, COPY_MODE);
                        AddLine(seg, prev_right, top, x1, top, FALSE, 0);
                        AddLine(seg, prev_right, top-seq_width, x1, top-seq_width, FALSE, 0);
                  }
                  prev_right = x2;

                  if(fnp->band == MISC_BND)
                  {
                        AddAttribute (seg, COLOR_ATT|SHADING_ATT, BLACK_COLOR, NO_LINE_STYLE, SOLID_SHADING, 1, COPY_MODE);
                        AddRectangle(seg, x1, top, x2, top-seq_width, NO_ARROW, FALSE, fnp->itemID);
                  }
                  else
                  {
                        AddAttribute (seg, SHADING_ATT | COLOR_ATT, color, NO_LINE_STYLE, shading, 1, COPY_MODE);
                        AddRectangle(seg, x1, top, x2, top-seq_width, NO_ARROW, TRUE, fnp->itemID);
                        if(fnp->band == GIEMSA_NEG)
                              color = BLACK_COLOR;
                        AddAttribute (seg, SHADING_ATT | COLOR_ATT, color, NO_LINE_STYLE, SOLID_SHADING, 1, COPY_MODE);
                        AddRectangle(seg, x1, top, x2, top-seq_width, NO_ARROW, FALSE, fnp->itemID);
                  }
                  if(fnp->label)
                  {
                        if(label_cyto_band(x1, x2, &prev_pos, fnp->label, scale))
                        {
                              center = (x1+ x2)/2;
                              AddLabel(seg, center, top-seq_width-5, fnp->label, SMALL_TEXT, 0, LOWER_CENTER, fnp->itemID);
                        }
                  }
            }
            mpp->seq_top = top;
            mpp->seq_bottom = top - seq_width;

            top -= ( seq_width + LABEL_SPACE+5);
            mpp->bottom = top;
            FreeFeatureList(g_node);
            retval = TRUE;
      }

      return retval;
}


      
static void draw_sequence_fnp(FeatNodePtr fnp, Boolean fill, Uint1 label_align, SegmenT seg, Uint1Ptr color, Int4 segstyle, Boolean show_border)
{
      Int2 arrow;
      FonT font;
      Int4 c_val;
      Uint1 label_color[3];
      Uint1 flags = COLOR_ATT|STYLE_ATT|SHADING_ATT|WIDTH_ATT;
      Uint1 linestyle, penwidth, shading;


      if(fnp->label != NULL)
      {

            font = (FonT)GetMuskCParam(MSM_SEQUENCE, MSM_SLABEL, MSM_FONT);
            SelectFont(font);
            fnp->labelHeight = FontHeight();
      
            c_val = GetMuskCParam(MSM_SEQUENCE, MSM_SLABEL, MSM_COLOR);
            convert_color(c_val, label_color);
            AddAttribute (seg, COLOR_ATT, &label_color[0], 0, 0, 0, 0);
            AddFeatNodeLabel(seg, fnp, label_align, font);
      }

      arrow = get_arrow_for_strand(fnp->extremes.strand);

      if(color == NULL)
      {
            if(segstyle == MSM_SEG_BOX)
                  add_attribute_pen(seg, MSM_SEQUENCE, MSM_SEGMENT);
            else
                  add_attribute_pen(seg, MSM_SEQUENCE, (Int2)segstyle);
      }
      else  /*the one's that uses cycle color*/
      {
            linestyle = (Uint1)GetMuskCParam(MSM_SEQUENCE, (Int2)segstyle, MSM_LTYPE);
            penwidth = (Uint1)GetMuskCParam(MSM_SEQUENCE, (Int2)segstyle, MSM_PENWIDTH);
            shading = (Uint1)GetMuskCParam(MSM_SEQUENCE, (Int2)segstyle, MSM_SHADING);
            AddAttribute(seg, flags, color, linestyle, shading, penwidth, COPY_MODE);
      }

      if(fill)
      {

            if(segstyle == MSM_SEG_LINE)
                  AddLine(seg, fnp->extremes.left, fnp->top, fnp->extremes.right, fnp->top, FALSE, fnp->itemID);
            else
                  AddRectangle (seg, fnp->extremes.left, fnp->top, fnp->extremes.right, fnp->bottom, arrow, fill, fnp->itemID);
      }

      if(show_border || !fill)
      {
            add_attribute_pen(seg, MSM_SEQUENCE, MSM_SEG_BORD);
            AddRectangle (seg, fnp->extremes.left, fnp->top, fnp->extremes.right, fnp->bottom, arrow, FALSE, fnp->itemID);
      }

}


/*********************************************************************
*
*     DrawSeqMap(features, mpp, pic, flat, scale, is_raw_seq)
*     Draw the real sequence map
*     features: FeatNode from which the Bioseqs can be extracted
*     mpp: the MapPos that contains the current map position
*     pic: the picture
*     flat: if TRUE, draw the map with features stacked on top
*     scale: maximum scale of the sequence
*     is_raw_seq: if TRUE, it is a raw DNA sequence. (This is 
*     used to distinguish raw sequence from virtual sequence
*     if(show_segment and image_list), record the positions of the 
*     segments
*
*********************************************************************/
Boolean DrawSeqMap(ValNodePtr PNTR features, MapPosPtr mpp, SegmenT pic, Int4 scale, Boolean is_raw_seq, Boolean show_segment, ValNodePtr PNTR image_list)
{
      ValNodePtr seg_node = NULL, g_node, c_node;
      ValNodePtr prev, next, gap_node = NULL;
      Int4 middle;
      FeatNodePtr fnp, c_fnp;
      SegmenT seg, pseg;
      Uint1Ptr color;
      Boolean fill;
      Int4 top;
      Int4 e_left, e_right;
      Uint2 entityID;
      Int2 arrow;
      Uint1 label_align;
      Uint1 seq_label_align;
      Int4 style, segstyle;
      Boolean show_border;
      Int2 obj_bioseq_seg_or_delta;



      entityID = (Uint2)mpp->entityID;
      label_align = (Uint1)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_STYLE);

      c_node = extract_node_list(features, OBJ_BIOSEQ, entityID, 0, ALL_LABEL);
      c_node = sort_same_itemID(c_node);
      if(c_node == NULL)
            return FALSE;
      c_fnp = c_node->data.ptrvalue;
      c_fnp->line = 0;

      style = GetMuskCParam(MSM_SEQUENCE, MSM_SEGMENT, MSM_STYLE);
      segstyle = (style & MSM_SEG_FORM);
      if(segstyle != MSM_SEG_BOX && segstyle != MSM_SEG_LINE)
            segstyle = MSM_SEG_BOX;
      if(segstyle ==MSM_SEG_BOX)
            show_border = (Boolean)GetMuskCParam(MSM_SEQUENCE, MSM_SEG_BORD, MSM_TRUEFALSE);
      else
            show_border = FALSE;

      top = LayoutFeatNode(c_node, mpp->seq_top, scale, 0, TRUE, FALSE);
      obj_bioseq_seg_or_delta = OBJ_BIOSEQ_SEG;
      seg_node = extract_node_list(features, OBJ_BIOSEQ_SEG, entityID, 0, ALL_LABEL);
      if(seg_node == NULL) {
            obj_bioseq_seg_or_delta = OBJ_BIOSEQ_DELTA;
            seg_node = extract_node_list(features, OBJ_BIOSEQ_DELTA, entityID, 0, ALL_LABEL);
      }
      fill = (is_raw_seq && seg_node == NULL);

      pseg = CreateSegment(pic, OBJ_BIOSEQ, 0);
      seg = CreateSegment(pseg, entityID, 0);
      if(label_align != MSM_LABEL_RIGHT)
            seq_label_align = MSM_LABEL_LEFT;
      else
            seq_label_align = MSM_LABEL_RIGHT;
      
      draw_sequence_fnp(c_fnp, fill, seq_label_align, seg, NULL, segstyle, show_border);
      /* if(image_list != NULL)
      {
            vn.choice = OBJ_BIOSEQ;
            vn.data.ptrvalue = c_fnp;
            vn.next = NULL;
            load_align_label_rectangle(&vn, image_list, scale, FALSE);
      } */
      mpp->seq_top = c_fnp->top;
      mpp->seq_bottom = c_fnp->bottom;


      /*add the segments to the sequence picture. Also get rid of the 
      FeatNode that only contains the gaps. if(fnp->follower = TRUE), it 
      is a gap */

      g_node = seg_node;
      prev = NULL;
      gap_node = NULL;
      while(g_node)
      {
            next = g_node->next;
            arrow = NO_ARROW;
            fnp = g_node->data.ptrvalue;
            e_left = fnp->extremes.left;
            e_right = fnp->extremes.right;
            if(fnp->follower) /*it is a gap between the segment*/
            {
                  if(prev == NULL)
                        seg_node = g_node->next;
                  else
                        prev->next = g_node->next;
                  g_node->next = NULL;
                  ValNodeLink(&gap_node, g_node);
            }
            else
            {
                  if(e_left == c_fnp->extremes.left && c_fnp->extremes.strand == Seq_strand_minus)
                        arrow = LEFT_ARROW;
                  if(e_right == c_fnp->extremes.right && c_fnp->extremes.strand == Seq_strand_plus)
                        arrow = RIGHT_ARROW;
                  color = get_seg_color(fnp->itemID);
                  AddAttribute (seg, COLOR_ATT, color, 0, 0, 0, COPY_MODE);
                  if(segstyle == MSM_SEG_BOX)
                        AddRectangle (seg, e_left, c_fnp->top, e_right, c_fnp->bottom, arrow, TRUE, c_fnp->itemID);
                  else
                        AddLine(seg, e_left, c_fnp->top, e_right, c_fnp->top, FALSE, c_fnp->itemID);
                  prev = g_node;
            }
            g_node = next;
      }


      if(gap_node != NULL)
      {
            AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 2, NO_MODE);
            middle = (c_fnp->top + c_fnp->bottom)/2;
            for(g_node = gap_node; g_node != NULL; g_node = g_node->next)
            {
                  fnp = g_node->data.ptrvalue;
                  e_left = fnp->extremes.left;
                  AddSymbol(seg, e_left, middle, DIAMOND_SYMBOL, FALSE, MIDDLE_CENTER, fnp->itemID);
            }
            FreeFeatureList(gap_node);
      }

      DrawSeqScale(seg, mpp->slp, mpp->left, mpp->seq_bottom, scale, FALSE);
      top -= SCALE_SPACE;     /*space for drawing the scale */
      if(seg_node !=NULL && show_segment)
      {
            top -= GetMuskCParam(MSM_TOPSTYLE, MSM_SPACE, MSM_HEIGHT);
            pseg = CreateSegment(pic, obj_bioseq_seg_or_delta, 0);
            seg = CreateSegment(pseg, entityID, 0);
            
            ReSetFeatNode(seg_node);
            top = LayoutFeatNode(seg_node, top, scale, 0, TRUE, FALSE);
            
            for(g_node = seg_node; g_node !=NULL; g_node = g_node->next)
            {
                  fnp = g_node->data.ptrvalue;
                  e_left = fnp->extremes.left;
                  e_right = fnp->extremes.right;
                  color = get_seg_color(fnp->itemID);
                  draw_sequence_fnp(fnp, TRUE, label_align, seg, color, segstyle, show_border);
            }
            if(image_list != NULL)
                  load_align_label_rectangle(seg_node, image_list, scale, FALSE); 
      }


      mpp->bottom = top;

      FreeFeatureList(c_node);
      FreeFeatureList(seg_node);
      return TRUE;
}



static Int4 DrawOneFeatNode(ValNodePtr g_node, Int4 top, SegmenT seg,  Int4 scale, Int2 group_num, GeneDataPtr gdata, ValNodePtr PNTR image_rect)
{
      Boolean show_arrow;
      Boolean compress;


      if(g_node != NULL)
      {

            show_arrow = (GetMuskCParam(MSM_TOPSTYLE, MSM_ENDS, MSM_SCALE) >= scale);
            compress = (GetMuskCParam(MSM_GROUPS, group_num, MSM_STYLE) == MSM_COMPRESS);
            
            top -= GetMuskCParam(MSM_TOPSTYLE, MSM_SPACE, MSM_HEIGHT);

            top = LayoutFeatNode(g_node,top, scale, scale, TRUE, compress);

            if(image_rect)
                  load_align_label_rectangle(g_node, image_rect, scale, FALSE);
            DrawFeatNode (g_node, seg, UPPER_LABEL, show_arrow, scale, gdata);
      }

      return top;
}

/******************************************************************
*
*     ck_ordered_group(): check if the internal order is important
*     to a group. Currently, only set up for the Gene-CDS-mRNA feature
*
*******************************************************************/
static Boolean ck_ordered_group(Uint1 group_num, Uint1Ptr groupOrder)
{
      Uint2 i;

      if(groupOrder == NULL)
            return FALSE;

      for(i =0; i<FEATDEF_ANY; ++i)
      {
            if(groupOrder[i] == group_num)
            {
                  if(i == FEATDEF_GENE || i == FEATDEF_mRNA || i == FEATDEF_CDS)
                        return TRUE;
            }
      }
      return FALSE;
}


static Boolean fnp_from_same_annot(FeatNodePtr fnp, CharPtr annot_db)
{
      if(annot_db[0] == '\0')
            return (fnp->annotDB[0] == '\0');
      else
            return (StringCmp(fnp->annotDB, annot_db) ==0);
}

static ValNodePtr group_feature_by_annot_label(ValNodePtr PNTR g_node, CharPtr annot_db)
{
      ValNodePtr list = NULL;
      FeatNodePtr fnp;
      ValNodePtr curr, next, prev = NULL;

      if(g_node == NULL || *g_node == NULL)
            return NULL;

      curr = *g_node;
      fnp = curr->data.ptrvalue;
      if(fnp == NULL)
            return NULL;
      if(fnp->annotDB[0] == '\0')
            annot_db[0] = '\0';
      else
            StringCpy(annot_db, fnp->annotDB);

      while(curr)
      {
            next = curr->next;
            fnp = curr->data.ptrvalue;
            if(fnp_from_same_annot(fnp, annot_db))
            {
                  if(prev == NULL)
                        *g_node = next;
                  else
                        prev->next = next;
                  curr->next = NULL;
                  ValNodeLink(&list, curr);
            }
            else
                  prev = curr;
            curr = next;
      }

      return list;
}
                  
static void AddAnnotGroup(Int4 left, Int4 top, Int4 right, Int4 bottom, Int2 order, CharPtr annot_db, Uint2 itemID, SegmenT seg)
{
      Uint1Ptr color;
      Int4 center;

      color = get_seg_color(order);
      AddAttribute (seg, COLOR_ATT, color, 0, 0, 0, 0);

      /*need to draw the rectangle line by line. Otherwise, the segments 
      will be incorrect*/
      AddLine(seg, left, top, right, top, FALSE, itemID);
      AddLine(seg, left, bottom, right, bottom, FALSE, itemID);
      AddLine(seg, left, top, left, bottom, FALSE, itemID);
      AddLine(seg, right, top, right, bottom, FALSE, itemID);

      if(annot_db != NULL && annot_db[0] != '\0')
      {
            AddAttribute (seg, COLOR_ATT, BLACK_COLOR, 0, 0, 0, 0);
            center = (left + right)/2;
            AddTextLabel(seg, center, top, annot_db, programFont, 0, MIDDLE_CENTER, itemID);
      }
}
      


static Boolean is_featnode_lod_score (ValNodePtr fnp_list)
{
      FeatNodePtr fnp;

      while(fnp_list)
      {
            if(fnp_list->choice == OBJ_SEQFEAT || fnp_list->choice == OBJ_BIOSEQ_MAPFEAT)
            {
                  fnp = fnp_list->data.ptrvalue;
                  if(fnp != NULL)
                  {
                        if((fnp->extra_data) & EXTRA_LOD_SCORE)
                              return TRUE;
                        else
                              return FALSE;
                  }
            }

            fnp_list = fnp_list->next;
      }

      return FALSE;
}


static Uint1Ptr GetLodScoreBitColor(Uint1 bit_val)
{
      switch(bit_val)
      {
            case 1:
                  return BLUE_COLOR;
            case 2:
                  return CYAN_COLOR;
            case 3:
                  return GREEN_COLOR;
            case 4:
                  return YELLOW_COLOR;
            case 5:
                  return MAGENTA_COLOR;
            case 6:
                  return RED_COLOR;
            default:
                  return BLACK_COLOR;
      }
}

static Int4 DrawLODScore(ValNodePtr t_node, SegmenT seg, Int4 scale, Int4 top)
{
      Int4 height;
      FeatNodePtr fnp;
      Uint1Ptr color;
      Int4 space;

      height = GetMuskCParam(MSM_SEQUENCE, MSM_SEGMENT, MSM_HEIGHT);
      space = GetMuskCParam(MSM_TOPSTYLE, MSM_SPACE, MSM_HEIGHT);
      top -= space;

      while(t_node)
      {
            fnp = t_node->data.ptrvalue;
            if(fnp != NULL)
            {
                  color = GetLodScoreBitColor((Uint1)(fnp->bin_order));
                  AddAttribute (seg, COLOR_ATT, color, 0, 0, 0, 0);
                  AddRectangle (seg, fnp->extremes.left, top, 
                        fnp->extremes.right, top-height, FALSE, TRUE, 0);
            }

            t_node = t_node->next;
      }

      return (top - space - height);
}
            

static Int4 DrawFeatureWithAnnot(ValNodePtr PNTR t_head, Int4 top, Int4 scale, Boolean flat, Int4 group_num, Boolean order_group, Int4 left, Int4 right, GeneDataPtr gdata, SegmenT seg, ValNodePtr PNTR image_list)
{
      Char annot_db[100];
      Int4 g_top = 0, g_bottom;
      Int4 group_font_height;
      Boolean draw_annot_group;
      Int2 annot_order;
      ValNodePtr t_node;

      SelectFont(programFont);
      group_font_height = FontHeight();

      annot_order = 0;
      while((t_node = group_feature_by_annot_label(t_head, annot_db)) != NULL)
      {
            if(*t_head  == NULL && annot_order == 0 && annot_db[0] == '\0')
                  draw_annot_group = FALSE;
            else
                  draw_annot_group = TRUE;
            if(draw_annot_group )
            {
                  if(annot_db[0] == '\0')
                        g_top = top;
                  else
                  {
                        top -= group_font_height;
                        g_top = top + group_font_height/2;
                  }
            }
            if(is_featnode_lod_score (t_node))
                  top = DrawLODScore(t_node, seg, scale, top);
            else
            {
                  if(flat)
                  {
                        top = LayoutNodeFlat(&t_node, scale, top, UPPER_LABEL, TRUE);
                        if(image_list != NULL)
                              load_align_label_rectangle(t_node, image_list, scale, TRUE);
                        DrawFlatNode(t_node, seg, UPPER_LABEL, scale, gdata);
                  }
                  else
                  {
                        if(order_group)
                              /* GroupFeatNode(&t_node); */
                              t_node = OrderCdsmRNA(&t_node);
                        else
                              t_node= SortFeatNode(t_node, NULL, NULL);
                        top = DrawOneFeatNode(t_node, top, seg, scale, (Int2)group_num, gdata, image_list);
                  }
            }
            t_node= FreeFeatureList(t_node);
            g_bottom = top;

            if(draw_annot_group)
            {
                  g_bottom -= group_font_height/2;
                  AddAnnotGroup(left, g_top, right, g_bottom, annot_order, annot_db, 0, seg);
                  top = g_bottom;
            }
            ++annot_order;
      }

      return top;
}

/***********************************************************************
*
*     DrawFeatures(features, mpp, pic, flat, simple, compact, 
*     scale)
*
*     Draw the features in the a sequence
*     features: the FeatNdoe contains the features
*     mpp: the graphic position of the current sequence
*     pic: the drawing segment
*     flat: if TRUE, stack features on top
*     simple: if TRUE, draw the feature intervals as line
*     compact: if TRUE, does not distinguish subtype of different features
*     scale: the maximum scale for drawing
*       image_list: the list of AlignRect to store the image information
*
*********************************************************************/
Boolean DrawFeatures(ValNodePtr g_node, MapPosPtr mpp, SegmenT pic, Boolean flat, Uint1Ptr featureOrder, Uint1Ptr groupOrder, Int4 scale, GeneDataPtr gdata, ValNodePtr PNTR image_list)
{
      ValNodePtr t_node, t_head;
      Int4 top;
      Int4 left, right;
      Int2 entityID;
      SegmenT seg;
      Uint1 i, j, k, maxGroup;
      Boolean is_end = FALSE;
      Boolean order_group;    /*need to show the order of the group*/

      if(g_node == NULL)
            return FALSE;
      left = mpp->left;
      right = mpp->right;
      top = mpp->bottom;
      entityID = mpp->entityID;
      seg = CreateSegment(pic, OBJ_SEQFEAT, 0);

      if(flat)    /*show the features first*/
      {
            mpp->bottom = DrawFeatureWithAnnot(&g_node, top, scale, TRUE, 0, FALSE, left, right, gdata, seg, image_list);
      }
      else
      {
            maxGroup= 0;
            for(k = 0; k<FEATDEF_ANY; ++k)
                  maxGroup = MAX(maxGroup, groupOrder[k]);
                  
            is_end = FALSE;
            order_group = FALSE;

            for(k =1; k<=maxGroup && !is_end; ++k)
            {
                  order_group = ck_ordered_group(k, groupOrder);
                  t_head = NULL;
                  for(i =1; i<FEATDEF_ANY && !is_end; ++i)  /*this is the order of features*/
                  {
                        for(j = 1; j<FEATDEF_ANY && !is_end; ++j)
                        {
                              if(groupOrder[j] == k && featureOrder[j] == i)
                              {
                                    t_node = extract_node_list(&g_node, OBJ_SEQFEAT, 0, j, ALL_LABEL);
                                    if(t_node != NULL)
                                    {
                                          ValNodeLink(&t_head, t_node);
                                          is_end = (g_node == NULL);
                                    }
                              }
                        }
                  }

                  top = DrawFeatureWithAnnot(&t_head, top, scale, FALSE, k,order_group, left, right, gdata, seg, image_list);
            }
            mpp->bottom = top;
            if(g_node != NULL)
                  FreeFeatureList(g_node);
      }
                        
      return TRUE;
}



Boolean DrawHistory(ValNodePtr aligns, MapPosPtr mpp, Int4 seq_label_len, SegmenT pic, Int4 scale, ValNodePtr PNTR image_list)
{
      
      Boolean retval;
      Uint1 label_align;
      Int4 left, right;
      Int4 offset;
 
      if(aligns)
      {
            (mpp->bottom) -= 20;

            offset = scale * (MAX(seq_label_len, 8));
            label_align = (Uint1)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_STYLE);
            
            left = mpp->left;
            right = mpp->right;
            switch(label_align)
            {
                  case MSM_LABEL_RIGHT:
                        left -= (8 * scale);
                        right += offset;
                        break;

                  case MSM_LABEL_TOP:
                  case MSM_LABEL_BOTTOM:
                        left -= (8*8*scale);
                        right += (8*8*scale);
                        break;

                  case MSM_LABEL_LEFT:
                        left -=offset;
                        right += (8 * scale);   /*8 is the size of a symbol*/
                        break;

                  default:
                        break;
            }
            retval = DrawMPAlignment(aligns, left, right, NULL, mpp->entityID, scale, &(mpp->bottom), MSM_SEQHIST, FALSE, pic);
            if(image_list != NULL)
                  load_align_label_rectangle (aligns, image_list, scale, FALSE);
            FreeAlignNode(aligns);
            return retval;
      }
      else
            return FALSE;
}


static MapLayoutPtr get_next_ypos(MapLayoutPtr mlp, Int4Ptr top)
{
      MapPosPtr mpp;

      if(mlp == NULL)
      {
            *top = -20;
            return NULL;
      }
      else
      {
            while(mlp->next != NULL)
                  mlp = mlp->next;
            mpp = mlp->data.ptrvalue;
            *top = mpp->bottom -MAX(10, GetMuskCParam(MSM_TOPSTYLE, MSM_SPACE, MSM_HEIGHT));
            return mlp;
      }
}



static Boolean set_seq_collect_option(CollectSeqOptionPtr csop, Int4 scale)
{
      Int4 topstyle;
      Int4 ends;
      Int2 i;

      if(csop == NULL || scale <= 0)
            return FALSE;
            
      topstyle = GetMuskCParam(MSM_TOPSTYLE, 0, MSM_STYLE);

      /*collect the features, maps, and features*/
      csop->seglevels = (topstyle & MSM_SEGMENTS) ? 1:0;
      ends = GetMuskCParam(MSM_TOPSTYLE, MSM_ENDS, MSM_SCALE);
      csop->nointerval = (ends < scale);
      csop->slabel_format = (Uint1)GetMuskCParam(MSM_SEQUENCE, MSM_SLABEL, MSM_STYLE);
      for(i =0; i<FEATDEF_ANY; ++i)
            csop->flabel_format[i] = (Uint1)GetMuskCParam(i, MSM_FLABEL, MSM_STYLE);
      csop->label_size = (Int2)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_NUM);
      csop->filter_level = 0;
            
      for( i =0; i<FEATDEF_ANY; ++i)      /*for checking the features to load*/
            csop->features[i] = (GetMuskCParam(i, MSM_FORDER, MSM_NUM) !=0);
            
      return TRUE;
}


/*add the label to draw the sequence*/
static Int4 add_seq_label_for_map(ValNodePtr PNTR features, MapPosPtr mpp, SegmenT pic)
{
      ValNodePtr c_node;
      FeatNodePtr fnp;
      FonT font;
      Int4 c_val;
      Uint1 label_color[3];
      SegmenT seg, pseg;
      Uint1 label_align;
      Int4 label_len;
      
      label_len = 0;
      c_node = extract_node_list(features, OBJ_BIOSEQ, mpp->entityID, 0, ALL_LABEL);
      if(c_node == NULL)
            return 0;
      fnp = c_node->data.ptrvalue;
      
      if(fnp->label != NULL)
      {
      
            font = (FonT)GetMuskCParam(MSM_SEQUENCE, MSM_FLABEL, MSM_FONT);
                  SelectFont(font);
      
            label_len = StringWidth(fnp->label);
            label_align = (Uint1)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_STYLE);
            if(label_align != MSM_LABEL_RIGHT)
                  label_align = MSM_LABEL_LEFT;
            c_val = GetMuskCParam(MSM_SEQUENCE, MSM_FLABEL, MSM_COLOR);
            convert_color(c_val, label_color);
            pseg = CreateSegment(pic, OBJ_BIOSEQ, 0);
            seg = CreateSegment(pseg, mpp->entityID, 0);
            AddAttribute (seg, COLOR_ATT, &label_color[0], 0, 0, 0, 0);

            AddLabelWithAlign(seg, label_align, mpp->left, mpp->left, mpp->seq_top, mpp->seq_bottom, fnp->label, font, fnp->itemID);
      }
      FreeFeatureList(c_node);
      return label_len;
}


static void set_lolli_pop_feature(Uint1Ptr l_feature)
{
      Int2 i;
      Int4 segstyle;

      for(i =0; i<FEATDEF_ANY; ++i)
      {
            segstyle = GetMuskCParam(i, MSM_SEGMENT, MSM_STYLE);
            switch (segstyle & MSM_SEG_FORM)
            {
                  case MSM_SEG_SYM_RECT:
                  case MSM_SEG_SYM_DIAMOND:
                  case MSM_SEG_SYM_OVAL:
                  case MSM_SEG_SYM_TRIANGLE:
                        l_feature[i] = TRUE;
                        break;
                  default:
                        l_feature[i]= FALSE;
            }
      }
}

static void load_feature_class(ValNodePtr PNTR type_list, Int2 _class)
{
      ValNodePtr curr;

      for(curr = *type_list; curr != NULL; curr = curr->next)
      {
            if(curr->data.intvalue == (Int4)_class)
                  return;
      }
      ValNodeAddInt(type_list, 0, (Int4)_class);
}

/*******************************************************************
*
*     from the list of FeatNode, load the feature types contained 
*     for this seq-loc. It will be used to draw the legend
*
*******************************************************************/
static void load_feature_type (ValNodePtr fnp_list, ValNodePtr PNTR type_list)
{
      FeatNodePtr fnp;
      Int2 _class;

      if(type_list == NULL)
            return;
      while(fnp_list)
      {
            if(fnp_list->choice == OBJ_SEQFEAT)
            {
                  fnp = fnp_list->data.ptrvalue;
                  if(fnp != NULL)
                  {
                        _class = get_current_class(fnp);
                        load_feature_class(type_list, _class);
                  }
            }

            fnp_list = fnp_list->next;
      }
}
            
      

/*********************************************************************
*
*     from the list of AlignNode, extract all the alignments 
*     that were considered as the bins of the FISH maps
*     since the alignment of the FISH map is displayed at the 
*     top, those were extracted at the first
*
*********************************************************************/
static ValNodePtr extract_fish_align(ValNodePtr PNTR anp_list)
{
      ValNodePtr curr, prev, next;
      ValNodePtr fish_list;
      AnnotInfoPtr info;
      AlignNodePtr anp;
      Boolean is_fish;

      if(*anp_list == NULL)
            return NULL;
      fish_list = NULL;
      prev = NULL;
      curr = *anp_list;

      is_fish = FALSE;
      while(curr)
      {
            next = curr->next;
            if(curr->choice == OBJ_SEQANNOT)
            {
                  info = (AnnotInfoPtr)(curr->data.ptrvalue);
                  is_fish = info->is_fish_align;
            }
            else
            {
                  anp = curr->data.ptrvalue;
                  if(is_fish)
                        anp->label = MemFree(anp->label);
            }
            if(is_fish)
            {
                  if(prev == NULL)
                        *anp_list = next;
                  else
                        prev->next = next;
                  curr->next = NULL;
                  if(curr->choice == OBJ_SEQANNOT)
                        FreeAlignNode(curr);
                  else
                        ValNodeLink(&fish_list, curr);
            }
            else
                  prev = curr;
            curr = next;
      }

      return fish_list;
}
                        

static Int4 add_fish_align (Int4 top, Uint2 entityID, ValNodePtr fish_align, 
      Int4 scale, SegmenT pic)
{
      Int4 bottom;
      SegmenT pseg, seg;
      AlignNodePtr anp;
      Int4 middle;
      ValNodePtr curr;
      Int4 space = 4;
      Int4Ptr y_pos;
      Int2 num;
      Int4 line;

      num = get_vnp_num(fish_align);
      y_pos = MemNew((size_t)(2*num) * sizeof(Int4));
      for(curr = fish_align; curr != NULL; curr = curr->next)
      {
            anp = curr->data.ptrvalue;
            if(anp->extremes.right - anp->extremes.left >= 5* scale)
                  anp->line = find_f_pos(anp->extremes.left, 
                        anp->extremes.right, y_pos, scale, num);
      }
      MemFree(y_pos);

                                          /* 1 is the pen-width */
      pseg = CreateSegment(pic, OBJ_SEQALIGN, 0);
      seg = CreateSegment(pseg, entityID, 0);

      AddAttribute (seg, COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      top -= 8;
      bottom = top;
      while(fish_align)
      {
            anp = fish_align->data.ptrvalue;
            if(anp->extremes.right - anp->extremes.left >= 5* scale)
            {
                  line = top - space * (anp->line +1);
                  bottom = MIN(bottom, line);
                  AddLine(seg, anp->extremes.left, line, anp->extremes.right, 
                        line, FALSE, anp->itemID);
                  middle = (anp->extremes.left + anp->extremes.right)/2;
                  AddLine(seg, middle, line, middle, top, FALSE, anp->itemID);
                  AddSymbol(seg, middle, top, UP_TRIANGLE_SYMBOL, TRUE, UPPER_CENTER, anp->itemID);
            }
            fish_align = fish_align->next;
      }
      
      return bottom-4;
}
      
static void extract_sts_search_node(ValNodePtr PNTR features, ValNodePtr PNTR g_node)
{
      ValNodePtr curr, prev, next;
      FeatNodePtr fnp;
      ValNodePtr g_prev;
      Boolean found;

      if(features == NULL || *features == NULL)
            return;
      g_prev = NULL;
      if(*g_node != NULL)
      {
            g_prev = *g_node;
            while(g_prev->next != NULL)
                  g_prev = g_prev->next;
      }

      curr = *features;
      prev = NULL;
      while(curr)
      {
            next = curr->next;
            fnp = curr->data.ptrvalue;
            found = FALSE;
            if(fnp->annotDB[0] != '\0')
            {
                  if(StringCmp(fnp->annotDB, "STS Search") == 0)
                  {
                        curr->next = NULL;
                        if(g_prev == NULL)
                              *g_node = curr;
                        else
                              g_prev->next = curr;
                        g_prev = curr;

                        if(prev == NULL)
                              *features = next;
                        else
                              prev->next = next;
                        found = TRUE;
                  }
            }
            if(!found)
                  prev = curr;
            curr = next;
      }
}
      

/**********************************************************************
*
*     DrawOneSeqLoc(slp, left, scale, pic, mlp, gdata, priority)
*     draw the current Seq-loc picture
*
*   slp:   the location on a Sequence, can be a list of Seq-loc for a 
*          circular molecule
*   left:  the leftmost position on the graph
*   scale: the scale for the current picture
*   pic:   the current picture
*   mlp:   to obtain the layout information
*   gdata: the selected gene symbols for display
*   priority: the priority of search for the current sequence
*   ftype_list: return a list of type of features for the current 
*   Seq-loc. This is going to be used for drawing the legend
*   image_list: the list to store the image mapping information
*   align_num: the top number of alignment for display
*
***********************************************************************/            
static Boolean DrawOneSeqLoc(SeqLocPtr slp, Int4 left, Int4 scale, SegmenT pic, MapLayoutPtr PNTR mlp,
GeneDataPtr gdata, Uint2 priority, ValNodePtr PNTR ftype_list, ValNodePtr PNTR image_list,
Boolean forceSeglevelsTo1, ValNodePtr extraEntityList)
{
      ValNodePtr aligns = NULL, features = NULL;
      ValNodePtr g_node;
      Uint2 entityID;
      Boolean ends;
      CollectSeqOption cs_option;
      CollectAlignOption ca_option;
      SeqIdPtr sip;
      BioseqPtr bsp;
      Uint1 bsptype;
      Char buf[41];
      Int2 i;
      MapPosPtr mpp;
      Int4 top;
      Int4 topstyle;
      Boolean show_seq_label = TRUE;      /*show the label of a sequence*/
      Boolean is_raw_seq;     /*is it a raw sequence*/
      Boolean show_segment = TRUE;  /*draw each segments in a segmented sequence?*/
      Boolean is_aa;
      SeqIdPtr maybe_mapid;   /*a possible map-id*/
      Boolean is_map;

      Uint1 featureOrder[FEATDEF_ANY];
      Uint1 groupOrder[FEATDEF_ANY];
      Boolean lolli_feature[FEATDEF_ANY]; /*identify the lollipop features*/
      Int4 groupNum;
      Int4 top_ends;
      Int4 seq_label_len;
      SeqIntPtr sint;
      Boolean has_align;
      ValNodePtr fish_align;
      Int4 fish_top;
      ValNodePtr cyto_node;
      ValNodePtr vnp1, vnp2;
      DeltaSeqPtr dsp;
      SeqLocPtr loc;


      if(slp == NULL || pic == NULL || scale <=0)
            return FALSE;

      /*set up the option for collecting sequences and features*/       
      if(!set_seq_collect_option(&cs_option, scale))
            return FALSE;
      ends = cs_option.nointerval;  /*only show the two-ends of a feature*/

      /*fetch the current Bioseq*/
      sip = SeqLocId(slp);
      bsp = BioseqLockById(sip);
      if(bsp == NULL)
      {
            SeqIdWrite(sip, buf, PRINTID_TEXTID_ACCESSION, 40);
            Message(MSG_ERROR, "fail to get the sequence %s", buf);
            return FALSE;
      }
      /* adjust the interval */
      if(slp->choice == SEQLOC_INT)
      {
            sint = slp->data.ptrvalue;
            if(sint->from < 0)
                  sint->from = 0;
            if(sint->to <0 || sint->to > bsp->length-1)
                  sint->to = bsp->length-1;
      }
      entityID = ObjMgrGetEntityIDForPointer((Pointer)bsp);
      if(entityID == 0)
      {
            BioseqUnlock(bsp);
            Message(MSG_ERROR, "Fail to find the entityID");
            return FALSE;
      }
      if(bsp->repr == Seq_repr_seg)
            maybe_mapid = figure_map_seqid((SeqLocPtr)(bsp->seq_ext));
      else if (bsp->repr == Seq_repr_delta && bsp->seq_ext_type == 4)
      {
            dsp = (DeltaSeqPtr) bsp->seq_ext;
            loc = DeltaSeqsToSeqLocs (dsp);
            maybe_mapid = figure_map_seqid(loc);
            SeqLocFree (loc);
      }
      else
            maybe_mapid = NULL;

      if(bsp->repr == Seq_repr_seg || bsp->repr == Seq_repr_delta)
      {
            /* cs_option.seglevels = 0; */
            /* cs_option.seglevels = 1; */
            if (forceSeglevelsTo1) {
                  cs_option.seglevels = 1;
            } else
                  if(BioseqHasFeature(bsp))
            { 
                  if(maybe_mapid !=NULL)
                        cs_option.seglevels = 0;
                  if(!BioseqHasLandMark(bsp))
                        cs_option.seglevels = 0;
                  if(cs_option.seglevels == 1)
                        cs_option.filter_level = 1;
            }
            else
                  cs_option.seglevels = 1; 
      }
      else
            cs_option.seglevels = 0;


      
      /*collecting the features + sequence segments*/
      bsptype = get_Bioseq_type(bsp);
      cs_option.bsp_type = bsptype;
      is_aa = (bsp->mol == Seq_mol_aa);
      features  = CollectItemForSeqLocEx(slp, entityID, left, is_aa, maybe_mapid, &cs_option, gdata, priority, forceSeglevelsTo1);
      for (vnp1 = extraEntityList; vnp1 != NULL; vnp1 = vnp1->next) {
        if ((Uint2) (vnp1->data.intvalue) != entityID) {
          vnp2  = CollectItemForSeqLocEx(slp, (Uint2) (vnp1->data.intvalue), left, is_aa, maybe_mapid, &cs_option, gdata, priority, forceSeglevelsTo1);
          ValNodeLink (&features, vnp2);
        }
      }
      if(features == NULL)
      {
            return FALSE;
      }
      /*load the type of the features to draw the legend for the features*/
      load_feature_type (features, ftype_list);

      /*collecting the history*/          
      topstyle = GetMuskCParam(MSM_TOPSTYLE, 0, MSM_STYLE);
      has_align = FALSE;
      if(topstyle & MSM_HISTORY)
      {
            set_option_for_collect_align(&ca_option, cs_option.label_size, COLLECT_HISTORY);
            ca_option.only_history = FALSE;
            ca_option.map_insert = TRUE;
            ca_option.slabel_format = cs_option.slabel_format;
            ca_option.nointerval = cs_option.nointerval;
            ca_option.no_sort = FALSE;
            ca_option.flat_insert = FALSE;
            aligns = collect_anpnode_with_option(&ca_option, slp, entityID, COLLECT_HISTORY, OBJ_SEQALIGN, NULL, NULL, FALSE);
          for (vnp1 = extraEntityList; vnp1 != NULL; vnp1 = vnp1->next) {
            if ((Uint2) (vnp1->data.intvalue) != entityID) {
                vnp2 = collect_anpnode_with_option(&ca_option, slp, (Uint2) (vnp1->data.intvalue), COLLECT_HISTORY, OBJ_SEQALIGN, NULL, NULL, FALSE);
              ValNodeLink (&aligns, vnp2);
            }
          }
            has_align = alignode_has_alignments(aligns);
            if(has_align)
            {
                  show_segment = FALSE;
                  /*for the alignment of STSs in SegMap*/
                  CollectSegMapSTSAlign(entityID, aligns, slp, left);
            }
      }
      else
            show_segment = FALSE;


      
      /*set the layout for the current Seq-loc*/
      get_next_ypos(*mlp, &top);
      mpp = MemNew(sizeof(MapPos));
      mpp->slp = slp;
      mpp->left = left;
      mpp->right = left + slp_list_len(slp);
      mpp->top  = top;
      mpp->bottom = top;
      mpp->seq_top = top;
      mpp->seq_bottom = top;
      mpp->entityID = entityID;
      

      /*display the alignment to the FISH map first */
      fish_top = 99999;
      if(get_FISH_align(bsp) != NULL && has_align)
      {     
            fish_align = extract_fish_align(&aligns);
            if(fish_align != NULL)
            {
                  fish_top = top;
                  mpp->top = add_fish_align (top, entityID, fish_align, scale, pic);
                  mpp->bottom = mpp->top;
                  mpp->seq_top = mpp->top;
                  mpp->seq_bottom = mpp->top;
                  mpp->bottom = mpp->top;
                  FreeAlignNode(fish_align);
            }
      }

      /*map the feature order and group order*/
      for( i =0; i<FEATDEF_ANY; ++i)      /*for checking the features to load*/
      {
            featureOrder[i] = (Uint1)GetMuskCParam(i, MSM_FORDER, MSM_NUM);
            groupNum = (Uint1)GetMuskCParam(i, MSM_FGROUP, MSM_NUM);
            groupOrder[i] = (Uint1)GetMuskCParam(MSM_GROUPS, (Int2)groupNum, MSM_NUM);
      }



      g_node = NULL;
      if(topstyle & MSM_STACKGENE)  /*draw the features on top of the sequence*/
            g_node = extract_node_list(&features, OBJ_SEQFEAT, 0, 0, ALL_LABEL);
      else
      {

            if((bsptype==SEG_SEQ && maybe_mapid == NULL)|| bsptype == RAW_SEQ)
            {
                  set_lolli_pop_feature(lolli_feature);
                  g_node = extract_lollipop_feature(&features, 0, lolli_feature);
            }
      
            else
            {
                  top_ends = GetMuskCParam(MSM_TOPSTYLE, MSM_ENDS, MSM_SCALE);
                  if(scale > top_ends)
                        g_node = extract_node_list(&features, OBJ_SEQFEAT, 0, 0, ALL_LABEL);
                  else
                        g_node = extract_lollipop_feature(&features, scale*10, NULL);

                  /*g_node = extract_lollipop_feature(&features, scale, NULL);*/
            }
      }
                  
      extract_sts_search_node(&features, &g_node);
      if(g_node)
            if(DrawFeatures(g_node, mpp, pic, TRUE, featureOrder, groupOrder, scale, gdata, image_list))
                  show_seq_label = FALSE;
      mpp->seq_top = mpp->bottom;
      mpp->seq_bottom = mpp->bottom;
      is_map = TRUE;    /*map does not extract bioseqs*/
      switch(bsptype)
      {
         case GENETIC_MAP:
            DrawGeneticMap(&features, mpp, pic, scale, gdata, image_list);
            break;

         case PHYSICAL_MAP:
            DrawPhysicalMap(&features, mpp, pic, scale, image_list);
            break;

         case RESTRICTION_MAP:
            DrawRestrictionMap(&features, mpp, pic, FALSE, SeqLocStrand(slp), scale);
            break;

         case CYTO_MAP:
            DrawCytoMap(&features, mpp, scale, pic);
            break;

         default:
            is_raw_seq = (bsptype == RAW_SEQ);
            is_map = FALSE;
            DrawSeqMap(&features, mpp, pic, scale, is_raw_seq, show_segment, image_list);
            break;
      }
      
      if(is_map && bsptype != RESTRICTION_MAP)  /*need to extract from the Bioseq*/
            seq_label_len = add_seq_label_for_map(&features, mpp, pic);
      else
            seq_label_len = 0;
      
      /*add the label of the sequence*/

      
      DrawHistory(aligns, mpp, seq_label_len, pic, scale, image_list);

      if(bsptype == CYTO_MAP && gdata != NULL)
      {
            cyto_node = convert_gdata_to_featnode(gdata, mpp->slp, mpp->left);
            if(cyto_node != NULL)
                  ValNodeLink(&features, cyto_node);
      }
            

      g_node = extract_node_list(&features, OBJ_SEQFEAT, 0, 0, ALL_LABEL);
      DrawFeatures(g_node, mpp, pic, FALSE, featureOrder, groupOrder, scale, gdata, image_list);
      FreeFeatureList(features);


      if(fish_top != 99999)
      {     /*there is a FISH mapping data */
            mpp->seq_bottom = fish_top - (mpp->seq_top - mpp->seq_bottom);
            mpp->seq_top = fish_top;
      }
      ValNodeAddPointer(mlp, 0, mpp);
      BioseqUnlock(bsp);
      return TRUE;

}

      
/*
*     MakeCompressAlignList: Build the AlignNode for  a compact alignment 
*   view associated 
*     with a sequence
*     query_bsp is the query sequence
*     annot: the alignment data. If NULL, the alignment data will be extracted 
*                 from sequence
*     return the list of AlignNode
*/
ValNodePtr MakeCompressAlignList(BioseqPtr query_bsp, SeqAnnotPtr annot, Uint2Ptr p_entityID)
{
      SeqAnnotPtr sap;
      SeqLocPtr slp;
      CollectAlignOption ca_option;
      CollectSeqOption cs_option;
      Uint2 entityID, t_entityID;

      ValNodePtr aligns = NULL, h_aligns, t_aligns;


      if(query_bsp == NULL)
            return NULL;

      entityID = ObjMgrGetEntityIDForPointer((Pointer)query_bsp);
      if(entityID == 0)
      {
            Message(MSG_ERROR, "Fail to find the entityID for the Bioseq");
            return NULL;
      }

      if(!set_seq_collect_option(&cs_option, 200))
            return NULL;


      slp = SeqLocIntNew(0, query_bsp->length-1, 0, query_bsp->id);
      set_option_for_collect_align(&ca_option, 0, COLLECT_HISTORY);
      ca_option.only_history = FALSE;
      ca_option.map_insert = TRUE;
      ca_option.slabel_format = cs_option.slabel_format;
      ca_option.nointerval = TRUE;
      ca_option.no_sort = FALSE;
      ca_option.flat_insert = FALSE;
      ca_option.align_num = -1;
      ca_option.csop = &cs_option;
      aligns = collect_anpnode_with_option(&ca_option, slp, entityID, COLLECT_HISTORY, OBJ_SEQALIGN, NULL, NULL, FALSE);
      if(!alignode_has_alignments(aligns))
      {
            FreeAlignNode(aligns);
            aligns = NULL;
      }

      /*no alignment from the Bioseq, trying the Seq-annot*/
      if(aligns == NULL)
      {
            for(sap = annot; sap != NULL; sap = sap->next)
            {
                  if(sap->type == 2)
                  {
                        t_entityID = ObjMgrRegister(OBJ_SEQANNOT, (Pointer)sap);
                        if(t_entityID == 0)
                        {
                              Message(MSG_ERROR, "Fail to register the Seq-annot");
                        }
                        else
                        {
                              t_aligns = collect_anpnode_with_option(&ca_option, slp, t_entityID, COLLECT_HISTORY, OBJ_SEQALIGN, NULL, NULL, TRUE);
                              ValNodeLink(&aligns, t_aligns);
                        }
                        *p_entityID = t_entityID;
                        break;
                  }
            }
      }
      else
            *p_entityID = entityID;
      if(aligns == NULL)
      {
            ErrPostEx(SEV_WARNING, 0, 0, "No alignment data");
            SeqLocFree(slp);
            return NULL;
      }

      /*collecting the alignment for the query sequence*/
      cs_option.features[FEATDEF_repeat_region] = TRUE;
      cs_option.features[FEATDEF_repeat_unit] = TRUE;
      ca_option.show_feature = TRUE;
      ca_option.nointerval = FALSE;
      ca_option.label_size = cs_option.label_size;
      h_aligns = collect_master_align_node(&ca_option, slp, OBJ_SEQALIGN, entityID);
      SeqLocFree(slp);

      ValNodeLink(&h_aligns, aligns);

      return h_aligns;
}


SegmenT DrawCompressAlignment(ValNodePtr anp_list, SeqLocPtr m_loc, Int4 width, Int4Ptr p_scale, Int4Ptr p_maxwidth, Uint2 entityID, ValNodePtr PNTR arect_list)
{
      Int2 styleNum;
      CharPtr old_style;
      Int4 scale;
      SegmenT pic;
      ValNode vn;
      Int4 max_width;
      Int4 y_pos;
      

      if(anp_list == NULL)
            return NULL;
      if(arect_list != NULL)
            *arect_list = NULL;
      vn.data.ptrvalue = m_loc;
      vn.next = NULL;
      max_width = CountMaxSeqLabel(&vn);
      scale = FigureMaxScale(&vn, (Int2)width, max_width);
      if(scale <=0)
            return NULL;
      pic = CreatePicture();

      /*set up the sepcial style */
      styleNum = GetMuskCurrentSt();
      old_style = GetMuskStyleName(styleNum);
      SetMuskCurrentSt("StyleX");

      y_pos = 0;
      DrawMPAlignment(anp_list, 0, SeqLocLen(m_loc)-1, m_loc, entityID, scale, &y_pos, MSM_SEQHIST, TRUE, pic);
      if(arect_list != NULL)
            load_align_label_rectangle(anp_list, arect_list, scale, FALSE);

      *p_scale = scale;
      *p_maxwidth = max_width;
      SetMuskCurrentSt(old_style);
      return pic;
}


/*************************************************************************
*
*     Label_GData(): Label the gene_data node specified by the user
*
*************************************************************************/
Boolean Label_GData(GeneDataPtr gdata, SeqLocPtr slp, Int4 left, Int4 top, SegmenT seg)
{
      SeqLocPtr c_slp;
      Int4 c_left;
      Boolean success = FALSE;
      Int4 m_start, m_stop, t_start, t_stop;
      Int4 g_pos;
      

      if(slp == NULL ||gdata == NULL)
            return FALSE;
      while(gdata)
      {
            t_start = SeqLocStart(gdata->location);
            t_stop = SeqLocStop(gdata->location);
            for(c_slp = slp, c_left = left; slp!=NULL; c_slp=c_slp->next)
            {
                  m_start = SeqLocStart(c_slp);
                  m_stop = SeqLocStop(c_slp);
                  
                  if(!(t_start > m_stop || t_stop < m_start))
                  {
                        g_pos = MAX(0, (t_start - m_start)) + c_left;
                        AddSymbol(seg, g_pos, top, DIAMOND_SYMBOL, TRUE, LOWER_CENTER, 0);
                        success = TRUE;
                  }
                  c_left += SeqLocLen(c_slp);
            }
            gdata = gdata->next;
      }
      
      return TRUE;
}
      






/*************************************************************************
*
*     DrawVerticalAlign (align, pic, mlp)
*     draw alignment among the sequences. (connecting aligned seg by line)
*     align: the Seq-align which contains the alignment among sequence
*     It is assumed to be a Std-seg
*     pic: the drawing segment
*     mlp: the list of the layout of the sequences
*
*************************************************************************/

void draw_one_align(AlignPos ap, Int2 num, Int2 order, SegmenT seg)
{
      Int4 x1, x2;
      Int2 i;
      
      for(i = 0; i<num-1; ++i)
      {
            x1 = (ABS(ap.left[i]) + ABS(ap.right[i]))/2;
            x2 = (ABS(ap.left[i+1]) + ABS(ap.right[i+1]))/2;
            AddLine(seg, x1, ap.top[i], x2, ap.bottom[i+1], FALSE, order);
            if(ap.left[i] < 0)
            {
                  AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLUE_COLOR, NO_LINE_STYLE, NO_SHADING, 1, COPY_MODE);
                  AddSymbol(seg, x1, ap.bottom[i], UP_TRIANGLE_SYMBOL, TRUE, MIDDLE_CENTER, 0);
                  AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, COPY_MODE);
            }
            if(ap.left[i+1] < 0)
            {
                  AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLUE_COLOR, NO_LINE_STYLE, NO_SHADING, 1, COPY_MODE);
                  AddSymbol(seg, x2, ap.bottom[i], DOWN_TRIANGLE_SYMBOL, TRUE, MIDDLE_CENTER, 0);
                  AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, COPY_MODE);
            }
      }
}

static MapPosPtr get_map_pos(MapLayoutPtr mlp, SeqIdPtr sip)
{
      MapPosPtr mpp;

      while(mlp)
      {
            mpp = mlp->data.ptrvalue;
            if(SeqIdMatch(sip, SeqLocId(mpp->slp)))
                  return mpp;
            mlp = mlp->next;
      }
      return NULL;
}

static AlignRangePtr get_ith_arp(AlignRangePtr arp, Int2 i)
{
      Int2 k = 0;

      while(arp)
      {
            if(k == i)
                  return arp;
            arp = arp->next;
            ++k;
      }
      return NULL;
}

Boolean DrawVerticalAlign (SeqAlignPtr align, SegmenT pic, MapLayoutPtr mlp)
{
      StdSegPtr ssp;
      SeqLocPtr locs;
      MapLayoutPtr curr;
      MapPosPtr mpp;
      Int2 j;
      GatherRange gr;
      AlignPos ap;
      SeqLoc sl_1;

            

      sl_1.choice = SEQLOC_MIX;
      sl_1.next = NULL;
      j = 0;
      if(align->segtype != 3)
            return FALSE;
      ssp= align->segs;
      for(curr = mlp; curr != NULL; curr = curr->next)
      {
            mpp = (MapPosPtr)(curr->data.ptrvalue);
            sl_1.data.ptrvalue = mpp->slp;
            for(locs = ssp->loc; locs != NULL; locs = locs->next)
            {
                  if(SeqLocOffset(&sl_1, locs, &gr, mpp->left))
                  {
            
                        ap.left[j] = gr.left;
                        ap.right[j] = gr.right;
                        ap.top[j] = mpp->seq_top;
                        ap.bottom[j] = mpp->seq_bottom;
                        ++j;
                  }
            }
      }

      if(j >1)
      {
            SortAlignPosition(&ap, j);
            draw_one_align(ap, j, 0, pic);
            return TRUE;
      }
      else
            return FALSE;
}




static void DrawEquivAlign (ValNodePtr e_align_list, SegmenT pic, MapLayoutPtr mlp)
{
      SeqAlignPtr align;
      SeqAnnotPtr annot;
      Int2 type;
      Uint1 my_gray_color[]={63, 63, 63};

      /* AddAttribute (pic, WIDTH_ATT | COLOR_ATT, GREEN_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE); */
      while(e_align_list)
      {
            annot = (SeqAnnotPtr)(e_align_list->data.ptrvalue);
            type = GetEquivAlignType (annot);
            add_attribute_pen(pic, MSM_ALIGNMENT, MSM_ALINE);
            switch(type)
            {
                  case 2:  /*it is inconsistent markers*/
                        AddAttribute(pic, COLOR_ATT|STYLE_ATT, RED_COLOR, DOTTED_LINE, 0, 0, 0);
                        break;
                  case 3:     /*it is unknown type*/
                  case 0:
                        AddAttribute(pic, COLOR_ATT|STYLE_ATT, CYAN_COLOR, SOLID_LINE, 0, 0, 0);
                        break;
                  case 4:  /*it is the alignment to the FISH map*/
                        AddAttribute(pic, COLOR_ATT|STYLE_ATT, BLACK_COLOR, SOLID_LINE, 0, 0, 0);
                        break;
                  default:
                        AddAttribute(pic, STYLE_ATT, 0, SOLID_LINE, 0, 0, 0);
                        break;
            }
            align = (SeqAlignPtr)(annot->data);
            while(align)
            {
                  DrawVerticalAlign (align, pic, mlp);
                  align = align->next;
            }
            e_align_list = e_align_list->next;
      }
}
            






/*********************************************************************
*
*     DrawGenomeMap(slp_list, e_align_list, scale, mlp)
*
*     draw all the SeqLocs in the list and show their alignment (if any)
*     slp_list: the list of Seq-locs
*     e_align_list: the list of alignment among the Seq-locs
*     scale: the scale for the picture
*     mlp: store the layout of the current picture
*     return the picture of the genome map
*     ftype_list: return a list of type of features for the current
*     Seq-loc. This is going to be used for drawing the legend
*     image_list: the list to store the image mapping information
*     align_num: number of the top alignment to show. -1 = show everything
*
*********************************************************************/
SegmenT DrawGenomeMapEx (ValNodePtr slp_list, ValNodePtr e_align_list, Int4 scale, GeneDataPtr gdata,
MapLayoutPtr PNTR mlp, ValNodePtr PNTR ftype_list, ValNodePtr PNTR image_list,
Boolean forceSeglevelsTo1, ValNodePtr extraEntityList)
{
      Boolean success = FALSE;
      ValNodePtr curr;
      SeqLocPtr slp;
      MapLayoutPtr mlp_head= NULL;
      SegmenT pic;
      /* ValNodePtr t_image = NULL;  */
      
      if(slp_list == NULL || scale <=0)
            return NULL;
      
      scale = MIN(scale, MAX_SCALE);
      pic = CreatePicture();
      if(ftype_list != NULL)
            *ftype_list = NULL;
      if(image_list != NULL)
            *image_list = NULL;
      
      for(curr = slp_list; curr !=NULL; curr = curr->next)
      {
            slp = curr->data.ptrvalue;
            /* if(DrawOneSeqLoc(slp, 0, scale, pic, &mlp_head, gdata, (Uint2)(curr->choice), ftype_list, &t_image)) */
            if(DrawOneSeqLoc(slp, 0, scale, pic, &mlp_head, gdata, (Uint2)(curr->choice), ftype_list,
                             image_list, forceSeglevelsTo1, extraEntityList)) 
                  success = TRUE;
      }
      /* ValNodeFreeData(t_image);  */
      if(!success)
      {
            DeletePicture(pic);
            return NULL;
      }

      DrawEquivAlign (e_align_list, pic, mlp_head);
      if(mlp !=NULL)
            *mlp = mlp_head;
      else
            MapLayoutFree(mlp_head);
      return pic;
}

SegmenT DrawGenomeMap (ValNodePtr slp_list, ValNodePtr e_align_list, Int4 scale, GeneDataPtr gdata, MapLayoutPtr PNTR mlp, ValNodePtr PNTR ftype_list, ValNodePtr PNTR image_list)
{
      return DrawGenomeMapEx (slp_list, e_align_list, scale, gdata, mlp, ftype_list, image_list, FALSE, NULL);
}

/**************************************************************************
*
*     DrawSequinMap(slp_list, sep, scale)
*     return a picture with the interface defined by Jonathan K. 
*     slp_list: a ValNode list of Seq-loc
*     sep: the Seq-entry pointer
*     scale: the scale of the picture
*
**************************************************************************/
SegmenT DrawSequinMapEx (ValNodePtr slp_list, SeqEntryPtr sep, Int4 scale, GeneDataPtr gdata, ValNodePtr PNTR ftype_list, Boolean forceSeglevelsTo1, ValNodePtr extraEntityList)
{
      ValNodePtr e_align_list = NULL;
      SegmenT pic;
      
      if(slp_list == NULL || sep == NULL || scale <=0)
            return NULL;
            
      e_align_list = get_equiv_align(sep);
      /*gdata = make_gene_data(g_list);*/
      
      pic = DrawGenomeMapEx(slp_list, e_align_list, scale, gdata, NULL, ftype_list, NULL, forceSeglevelsTo1, extraEntityList);
      /*GeneDataFree(gdata);*/
      ValNodeFree(e_align_list);
      return pic;
}

SegmenT DrawSequinMap (ValNodePtr slp_list, SeqEntryPtr sep, Int4 scale, GeneDataPtr gdata, ValNodePtr PNTR ftype_list)
{
      return DrawSequinMapEx (slp_list, sep, scale, gdata, ftype_list, FALSE, NULL);
}

Boolean load_align_option_for_graphic(CollectAlignOptionPtr caop, CollectSeqOptionPtr csop, Int4 style, Boolean flat_insert)
{
      Int2 i;

      if(style < MSM_SEQHIST || style > MSM_MDIM)
            return FALSE;
      MemSet((Pointer)caop, 0, sizeof(CollectAlignOption));
      caop->graphic_offset = 0;
      caop->only_history = FALSE;
      caop->show_mismatch = (style == MSM_MPAIR);     /*can not show mismatch for MDIM for now*/
      caop->label_size= (Int2)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_NUM);
      caop->nointerval = FALSE;
      caop->map_insert = (style== MSM_MPAIR || style == MSM_SEQHIST);
      caop->flat_insert = (style == MSM_MPAIR && flat_insert);
      caop->map_graphic = TRUE;
      caop->show_feature = TRUE;
      caop->slabel_format = (Uint1)GetMuskCParam(MSM_SEQUENCE, MSM_SLABEL, MSM_STYLE);
      caop->align_num = DEFAULT_ALIGN_NUM; 
      /* caop->align_num = (Int4)GetMuskCParam(MSM_ALIGNMENT, MSM_NOSUBCLASS, MSM_SCALE); */

      csop->seglevels = 0;
      csop->nointerval = FALSE;
      csop->slabel_format = (Uint1)GetMuskCParam(MSM_SEQUENCE, MSM_SLABEL, MSM_STYLE);
      for(i =0; i<FEATDEF_ANY; ++i)
            csop->flabel_format[i] = (Uint1)GetMuskCParam(i, MSM_FLABEL, MSM_STYLE);
      csop->label_size = caop->label_size;
      csop->filter_level = 0;
      return TRUE;
}



/************************************************************
*
* collect_alignnode_from_alp(m_loc)
* collect all the alignment stored as the history of the 
* master sequence
* m_loc: the selected location of the master sequence
* return a list of AlignNode
*
************************************************************/
ValNodePtr collect_alignnode_from_slp(SeqLocPtr m_loc, Uint2Ptr t_entityID, Boolean flat_insert)
{
      ValNodePtr anp_node;
      CollectAlignOption ca_option;
      CollectSeqOption cs_option;
      BioseqPtr bsp;
      Uint2 entityID;

      if(m_loc == NULL)
            return NULL;
      bsp = BioseqLockById(SeqLocId(m_loc));
      if(bsp == NULL)
            return NULL;
      entityID = ObjMgrGetEntityIDForPointer((Pointer)bsp);
      if(entityID == 0)
            return NULL;
      if(t_entityID != NULL)
            *t_entityID = entityID;
      load_align_option_for_graphic(&ca_option, &cs_option, MSM_MPAIR, flat_insert);
      ca_option.csop = &cs_option;
      if(!BioseqHasFeature(bsp))
            cs_option.seglevels = 1;
      anp_node= collect_anpnode_with_option(&ca_option, m_loc, entityID, COLLECT_MP, OBJ_SEQHIST_ALIGN, NULL, NULL, FALSE);

      BioseqUnlock(bsp);
      return anp_node;
}




/************************************************************************
*
*
************************************************************************/
SegmenT DrawSeqHistoryAlignment(SeqLocPtr m_loc, Int4 scale, ValNodePtr PNTR anp_list, Uint2Ptr seq_entityID, Boolean flat_insert)
{
      ValNodePtr anp_node;
      SegmenT pic;
      Int4 y_pos = 0;
      Int4 len;
      Uint2 entityID;

      anp_node = collect_alignnode_from_slp(m_loc, &entityID, flat_insert);
      if(anp_node == NULL)
            return NULL;
      pic = CreatePicture();
      len = slp_list_len(m_loc);
      DrawMPAlignment(anp_node, 0, len-1, m_loc, entityID, scale, &y_pos, MSM_MPAIR, FALSE, pic);

      if(seq_entityID != NULL)
            *seq_entityID = entityID;
      if(anp_list != NULL)
            *anp_list = anp_node;
      else
            FreeAlignNode(anp_node);
      return pic;
}

/*draw something that looks like a folder for the un-aligned contigs in 
  Eric Green's map
*/

static Boolean draw_unknown_folder(SegmenT seg, Int4 left, Int4 right, Int4 y_pos, AnnotInfoPtr annot_info)
{
      Char label[101];
      Int4 center, middle;
      Int4 font_height;

      if(annot_info->annotDB[0] == '\0')
            return FALSE;
      font_height = FontHeight();
      middle = y_pos - font_height/2;
      SelectFont(programFont);
      sprintf(label, "[%s]", annot_info->annotDB);
      center = (left + right)/2;

      AddAttribute (seg, COLOR_ATT, YELLOW_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      AddRectangle (seg, left, y_pos, right, y_pos-font_height, FALSE, TRUE, annot_info->itemID);
      AddAttribute (seg, COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      AddTextLabel(seg, center, middle, label, programFont, 0, MIDDLE_CENTER, annot_info->itemID);
      return TRUE;
}

      
static Boolean get_master_alignnode_extremes (ValNodePtr anp_node, Int4Ptr m_left, Int4Ptr m_right)
{
      AlignNodePtr anp;

      while(anp_node != NULL)
      {
            if(anp_node->choice != OBJ_SEQANNOT)
            {
                  anp = anp_node->data.ptrvalue;
                  if(anp->is_master)
                  {
                        *m_left = anp->extremes.left;
                        *m_right = anp->extremes.right;
                        return TRUE;
                  }
            }

            anp_node = anp_node->next;
      }

      return FALSE;
}


Boolean DrawMPAlignment(ValNodePtr anp_node, Int4 left, Int4 right, SeqLocPtr m_loc, Uint2 entityID, Int4 scale, Int4Ptr cur_pos, Uint1 style, Boolean compress, SegmenT pic)
{
      Int4 y_pos;
      SegmenT pseg, seg;
      Boolean ends = FALSE;
      Int4 height, ends_scale;

      Int4 g_top, g_bottom;
      Int4 group_font_height;

      ValNodePtr list, curr, prev, last, next;
      ValNodePtr head;
      AnnotInfoPtr annot_info;
      Int4 space;
      Int4 m_left = -1, m_right = -1;
      Int4 offset;
      SeqLocPtr last_loc;
      SeqIntPtr sint;
      Int4 len;
      Boolean use_symbol_for_align; /*for each alignment, only use 
                              the diamond symbol to indicate 
                              the alignment location*/


      if(anp_node == NULL || pic == NULL || cur_pos == NULL)
            return FALSE;
      if(style > MSM_FIXED || style < MSM_SEQHIST)
            return FALSE;
      if(style == MSM_SEQHIST )
      {
            ends_scale = GetMuskCParam(MSM_TOPSTYLE, MSM_ENDS, MSM_SCALE);
            if(ends_scale < scale)
                  ends = TRUE;
      }
      if(compress)
            ends = TRUE;
      if(ends)
            height = GetMuskCParam(MSM_SEQUENCE, MSM_SEG_BORD, MSM_PENWIDTH);
      else
            height = GetMuskCParam(MSM_SEQUENCE, MSM_SEGMENT, MSM_HEIGHT);

      y_pos = *cur_pos;
      if(m_loc != NULL)
            y_pos -= 40;      /*leaves enough space for drawing the ruler*/
      
      pseg = CreateSegment(pic, anp_node->choice, 0);
      seg = CreateSegment(pseg, entityID, 0);

      if(!get_master_alignnode_extremes(anp_node, &m_left, &m_right))
      {
            m_left = left;
            m_right = right;
      }
      if(m_loc != NULL)
      {
            len = SeqLocLen(m_loc);
            last_loc = m_loc;
            offset = 0;
            sint = NULL;
            while(last_loc->next != NULL)
            {
                  len += SeqLocLen(last_loc->next);
                  last_loc = last_loc->next;
            }
            if(m_left != -1 && m_right != -1 && last_loc->choice == SEQLOC_INT)
            {
                  offset = m_right - m_left + 1 - len;
                  if(offset > 0)
                  {
                        sint = last_loc->data.ptrvalue;
                        if(sint->strand == Seq_strand_minus)
                              sint->from -= offset;
                        else
                              sint->to += offset;
                  }
            }
            DrawSeqScale(seg, m_loc, 0, 0, scale, TRUE);
            if(offset > 0 && sint != NULL)
            {
                  if(sint->strand == Seq_strand_minus)
                        sint->from += offset;
                  else
                        sint->to -= offset;
            }
      }

      SelectFont(programFont);
      group_font_height = FontHeight();
      space = GetMuskCParam(MSM_TOPSTYLE, MSM_SPACE, MSM_HEIGHT);


      if(anp_node == NULL)
            return FALSE;
      list = anp_node;
      prev = NULL;
      head = NULL;
      use_symbol_for_align = FALSE;
      while(list != NULL)
      {
            annot_info = NULL;
            /* if(prev != NULL)
                  prev->next = list; */
            while(list && list->choice == OBJ_SEQANNOT)
            {
                  annot_info = (AnnotInfoPtr)(list->data.ptrvalue);
                  if(annot_info != NULL && annot_info->consistent == ALIGN_UNKNOWN)
                  {     
                        /*it is the folder like un-aligned thing*/
                        pseg = CreateSegment(pic, OBJ_SEQANNOT, 0);
                        seg = CreateSegment(pseg, annot_info->entityID, 0);
                        if(draw_unknown_folder(seg, m_left, m_right, y_pos, annot_info))
                        {
                              y_pos -= (group_font_height + space);
                              *cur_pos = y_pos;
                        }
                  }
                  if(head == NULL)
                        head = list;
                  prev = list;
                  list = list->next;
            }
            if(list != NULL)
            {
                  use_symbol_for_align = FALSE;
                  curr = list;
                  last = NULL;
                  while(curr && curr->choice != OBJ_SEQANNOT)
                  {
                        last = curr;
                        curr = curr->next;
                  }
                  next = last->next;
                  last->next = NULL;
                  g_top = y_pos;
                  if(annot_info != NULL)  /*there is a Seq-annot wrapper*/
                  {
                        y_pos  -= group_font_height;
                        g_top -= group_font_height/2;
                        if(annot_info->annotDB[0] != '\0')
                        {
                              if(StringCmp(annot_info->annotDB, "UniGene") == 0)
                                    use_symbol_for_align = TRUE;
                        }
                  }
                  if(use_symbol_for_align)
                        *cur_pos = LayoutAlignFlat(list, scale, y_pos);
                  else
                        *cur_pos = LayoutAlignNode(list, y_pos, scale, height);
                  g_bottom = *cur_pos; 
                  y_pos = *cur_pos;

                  pseg = CreateSegment(pic, list->choice, 0);
                  seg = CreateSegment(pseg, entityID, 0);
                  if(use_symbol_for_align)
                        DrawFlatAlign(seg, list);
                  else
                        DrawAlignNode(list, scale, seg);

                  if(annot_info != NULL)
                  {
                        pseg = CreateSegment(pic, OBJ_SEQANNOT, 0);
                        seg = CreateSegment(pseg, annot_info->entityID, 0);
                        AddAnnotGroup(m_left, g_top, m_right, g_bottom, (Int2)(annot_info->displayOrder), annot_info->annotDB, annot_info->itemID, seg); 
                        y_pos -= group_font_height;
                  }
                  /* last->next = next; */
                  if(prev != NULL)
                        prev->next = list;
                  last = list;
                  while(last->next != NULL)
                        last = last->next;
                  last->next = next;
                  prev = last;
                  list = next;
            }
      }
      return TRUE;
}
      
      
      



/************************************************************************
*
*     DrawSeqScale(seg, slp, left, ypos, scaleX)
*     draw a scale for the Seq-loc. It can be a list of Seq-loc
*     seg: the drawing segment
*     slp: the Seq-loc
*     left: the left offset
*     ypos: the top position
*     scaleX: the current scale of the picture
*
************************************************************************/
void DrawSeqScale(SegmenT seg, SeqLocPtr slp, Int4 left, Int4 ypos, Int4 scaleX, Boolean add_x_line)
{

  Int4 ruler;
  Int4 r_start, r_stop, curpos;
  Int4 xpos;
  Char str[20];
  Int2 i;
  NumberingPtr np;
  Boolean use_kb;
  Int2 label_width = 0;
  Int2 c_width;
  Int4 pre_pos = -1;
  BioseqPtr bsp;



       ruler = calculate_ruler(scaleX);
       AddAttribute (seg, WIDTH_ATT | COLOR_ATT, BLACK_COLOR, NO_LINE_STYLE, NO_SHADING, 1, NO_MODE);
      ypos -=2;
      bsp = BioseqFind(SeqLocId(slp));
      if(bsp == NULL)
            np = NULL;
      else
            np = getBioseqNumbering(bsp);
      use_kb = (scaleX >= 1000);
      while(slp)
      {
            r_start = SeqLocStart(slp);
            r_stop = SeqLocStop(slp);
            if(add_x_line)
                  AddLine(seg, left, ypos, left+SeqLocLen(slp), ypos, FALSE, 0);
            for(curpos = r_start; curpos <=r_stop; curpos +=ruler)
            {
                  map_unit_label(curpos, np, str, use_kb);
                  xpos = left + curpos - r_start;
                  AddLine(seg, xpos, ypos, xpos, ypos-8, FALSE, 0);
                  c_width = StringWidth(str);
                  if(start_new_stack(pre_pos, xpos, scaleX, &label_width, c_width))
                  {
                        pre_pos = xpos;
                        AddLabel(seg, xpos, ypos-4, str, SMALL_TEXT, 5, LOWER_CENTER, 0);
                  }
                  for(i =1; i<10; ++i)
                  {
                        if(curpos + ruler/10*i <=r_stop)
                        {
                              xpos = left + curpos + ruler/10*i - r_start;
                              if(i ==5)
                                    AddLine(seg, xpos, ypos, xpos, ypos-5, FALSE, 0);
                              else
                                    AddLine(seg, xpos, ypos, xpos, ypos-3, FALSE, 0);
                        }
                  }
            }
            left += SeqLocLen(slp);
            slp = slp->next;
      }
}
      
                  

Int4 CountMaxSeqLabel(ValNodePtr slp_list)
{
      FonT font;
      SeqLocPtr slp;
      BioseqPtr bsp;
      Uint1 format;
      Char label[100];
      Int4 max_width = 0;


      font = (FonT)GetMuskCParam(MSM_SEQUENCE, MSM_SLABEL, MSM_FONT);
      format = (Uint1)GetMuskCParam(MSM_SEQUENCE, MSM_SLABEL, MSM_STYLE);

      SelectFont(font);

      while(slp_list)
      {
            slp = slp_list->data.ptrvalue;
            if(slp != NULL)
            {
                  bsp = BioseqFind(SeqLocId(slp));
                  if(bsp != NULL)
                        MuskSeqIdWrite(bsp->id, label, 99, format, TRUE, FALSE);
                  else
                        MuskSeqIdWrite(SeqLocId(slp), label, 99, format, FALSE, FALSE);
                  max_width = MAX(StringWidth(label), max_width);
            }
            slp_list = slp_list->next;
      }
      return max_width;
}
                  

Int4 FigureMaxScale(ValNodePtr slp_list, Int2 view_width, Int4 max_label)
{
      Int4 width;
      Int4 scale;
      
      Int4 seq_len;
      SeqLocPtr slp;
      ValNodePtr curr;
      Int4 len;
      
      seq_len = 0;
      for(curr = slp_list; curr !=NULL; curr = curr->next)
      {
            slp = (SeqLocPtr)(curr->data.ptrvalue);
            len = slp_list_len(slp);
            seq_len = MAX(len, seq_len);
      }
      if(seq_len == 0)
            return -1;
      width = (Int4)view_width- max_label;
      if(width <=0)
            width = view_width/2;
      scale = seq_len/width + (((Int4)width%(Int4)view_width)>0);
      return MIN(scale, MAX_SCALE); 
      /* return scale; */
}

Int4 FigureMinScale(ValNodePtr slp_list, Int4 max_label)
{
      Int4 seq_len;
      SeqLocPtr slp;
      ValNodePtr curr;
      
      seq_len = 0;
      for(curr = slp_list; curr !=NULL; curr = curr->next)
      {
            slp = (SeqLocPtr)(slp_list->data.ptrvalue);
            seq_len = MAX(slp_list_len(slp), seq_len);
      }
      if(seq_len == 0)
            return 1;
      return MAX(1, (seq_len + max_label)/320000L);
}


static Uint1 find_hot_link_gi(SeqIdPtr h_sip, Int4Ptr gi)
{
      DbtagPtr db_tag;
      ObjectIdPtr oip;
      SeqIdPtr sip;

      *gi = 0;
      sip = h_sip;
      while(sip)
      {
            if(sip->choice == SEQID_GI)
            {
                  *gi = sip->data.intvalue;
                  return ENTREZ_DB;
            }
            else if(sip->choice == SEQID_GENERAL)
            {
                  db_tag = sip->data.ptrvalue;
                  if(db_tag && db_tag->db && StringCmp(db_tag->db, "THC") == 0)
                  {
                        oip = db_tag->tag;
                        if(oip && oip->id != 0)
                        {
                              *gi = oip->id;
                              return THC_DB;
                        }
                  }
            }

            sip = sip->next;
      }

      for(sip = h_sip; sip != NULL; sip = sip->next)
      {
            if(sip->choice == SEQID_GENERAL)
            {
                  *gi = GetUniGeneIDForSeqId (sip);
                  if(*gi != 0)
                        return ENTREZ_KLUDGE_ID;
            }
      }


      return 0;
}

static void link_new_align_label(ValNodePtr PNTR head, ValNodePtr PNTR prev, Uint1 choice, AlignLabelRectPtr alrp)
{
      ValNodePtr curr;

      curr = ValNodeNew(NULL);
      curr->choice = choice;
      curr->data.ptrvalue = alrp;

      if(*prev == NULL)
            *head = curr;
      else
            (*prev)->next = curr;
      *prev = curr;
}


/*
*
*     Add the rectangle for the label of the sequence
*     width is the width of the label
*
*/
static Boolean add_label_rectangle (AlignLabelRectPtr alrp, Int4 width, Int4 label_height, 
            Uint1 label_align, Int4 e_left, Int4 e_top, Int4 e_right, Int4 e_bottom)
{
      Int4 center, middle;
      
      if(alrp == NULL || width == 0)
            return FALSE;
      
      switch(label_align)
      {
            case MSM_LABEL_TOP:
                  center = (e_left + e_right)/2;
                  alrp->left = center - width/2;
                  alrp->right = center + width/2;
                  alrp->bottom = e_top;
                  alrp->top = e_top + label_height;
                  break;
            
            case MSM_LABEL_BOTTOM:
                  center = (e_left + e_right)/2;
                  alrp->left = center - width/2;
                  alrp->right = center + width/2;
                  alrp->top = e_bottom;
                  alrp->bottom = e_bottom - label_height;
                  break;
                        
            case MSM_LABEL_LEFT:
                  middle = (e_top + e_bottom)/2;
                  alrp->right= e_left;
                  alrp->left=  alrp->right - width;
                  alrp->bottom = middle - label_height/2;
                  alrp->top = middle + label_height/2;
                  break;

            case MSM_LABEL_RIGHT:
                  middle = (e_top + e_bottom)/2;
                  alrp->left=e_right;
                  alrp->right =  alrp->left + width;
                  alrp->bottom = middle - label_height/2;
                  alrp->top = middle + label_height/2;
                  break;
            default:
                  break;
      }
      return TRUE;
}

/************************************************************************
*
*     load the information for image mapping
*
************************************************************************/
Boolean load_align_label_rectangle(ValNodePtr data_list, ValNodePtr PNTR arect_list, Int4 scale, Boolean flat)
{
      AlignNodePtr anp;
      FeatNodePtr fnp;
      BioseqPtr bsp;
      Uint1 label_align, t_label_align;
      AlignLabelRectPtr alrp;
      Int4 width;
      Int4 e_left = 0, e_right = 0;
      Int4 e_top = 0, e_bottom = 0;
      Int4 font_height;
      FonT font;
      Uint1 link_db;
      Int4 gi;
      Int4 label_height = stdLineHeight;
      ValNodePtr prev, curr;
      Uint1 choice;
      Boolean found_it;

      if(data_list == NULL || arect_list == NULL)
            return FALSE;
      prev = NULL;
      curr = *arect_list;
      if(curr != NULL)
      {
            while(curr->next != NULL)
            {
                  prev = curr;
                  curr = curr->next;
            }
      }

      font = (FonT)GetMuskCParam(MSM_SEQUENCE, MSM_FLABEL, MSM_FONT);
      SelectFont(font);
      font_height = FontHeight();
      label_align = (Uint1)GetMuskCParam(MSM_TOPSTYLE, MSM_LABEL, MSM_STYLE);

      while(data_list)
      {
            found_it = FALSE;
            alrp = NULL;
            /* t_label_align = label_align; */
            if(flat)
                  t_label_align = MSM_LABEL_TOP;
            else
                  t_label_align = label_align;
            width = 0;
            choice = 0;
            /*process the intervals first */
            switch(data_list->choice)
            {
                  case OBJ_SEQALIGN:
                  case OBJ_SEQHIST_ALIGN:
                        anp = data_list->data.ptrvalue;
                        e_left = anp->extremes.left;
                        e_right = anp->extremes.right;
                        e_top = anp->top;
                        e_bottom = anp->bottom;
                        label_height = font_height;
                        if(anp->label != NULL)
                              width = StringWidth(anp->label) * scale;

                        bsp = BioseqFindCore(anp->sip);
                        gi = 0;
                        link_db = 0;
                        choice = 0;
                        if(bsp != NULL)
                        {
                              link_db = find_hot_link_gi(bsp->id, &gi);
                              choice = bsp->mol;
                        }
                        else
                        {
                              link_db = find_hot_link_gi(anp->sip, &gi);
                        }
                        if((e_right - e_left) /scale > 1)
                        {
                              alrp = MemNew(sizeof(AlignLabelRect));
                              alrp->itemID = anp->itemID;
                              alrp->itemType = data_list->choice;
                              if(link_db != UNKNOWN_DB && gi != 0)
                              {
                                    alrp->link_db = link_db;
                                    alrp->gi = gi;
                              }
                              alrp->left = e_left;
                              alrp->right = e_right;
                              alrp->top = e_top;
                              alrp->bottom = e_bottom;
                              link_new_align_label(arect_list, &prev, choice, alrp);
                        }
                        /*create the new one for the label*/
                        if(width > 0)
                        {
                              alrp = MemNew(sizeof(AlignLabelRect));
                              alrp->itemID = anp->itemID;
                              alrp->itemType = data_list->choice;
                              alrp->link_db = link_db;
                              alrp->gi = gi;
                        }

                        break;

                  case OBJ_SEQFEAT:
                  case OBJ_BIOSEQ:
                  case OBJ_BIOSEQ_SEG:
                        fnp = data_list->data.ptrvalue;
                        if((fnp->extremes.right - fnp->extremes.left)/scale > 1)
                        {
                              alrp = MemNew(sizeof(AlignLabelRect));
                              alrp->itemID = fnp->itemID;
                              alrp->itemType = data_list->choice;
                              alrp->right= fnp->extremes.right;
                              alrp->left=  fnp->extremes.left;
                              alrp->bottom = fnp->bottom;
                              alrp->top = fnp->top;
                              link_new_align_label(arect_list, &prev, choice, alrp);
                        }

                        if(fnp->label != NULL && fnp->labelHeight > 0 && fnp->label_len > 0)
                        {
                              e_left = fnp->extremes.left;
                              e_right = fnp->extremes.right;
                              e_top = fnp->top;
                              e_bottom = fnp->bottom;
                              label_height = fnp->labelHeight;
                              width = scale * (fnp->label_len);
                              alrp = MemNew(sizeof(AlignLabelRect));
                              alrp->itemID = fnp->itemID;
                              alrp->itemType = data_list->choice;
                        }
                        if(data_list->choice != OBJ_SEQFEAT)
                        {     /*it is a Bioseq or a segment of a Bioseq*/
                              if(label_align != MSM_LABEL_RIGHT)
                                    t_label_align = MSM_LABEL_LEFT;
                              else
                                    t_label_align = MSM_LABEL_RIGHT;
                        }
                        break;

                  case OBJ_BIOSEQ_MAPFEAT:
                        fnp = data_list->data.ptrvalue;
                        if(fnp->label != NULL && fnp->labelHeight > 0 && fnp->label_len > 0)
                        {
                              e_left = fnp->extremes.left;
                              e_right = fnp->extremes.right;
                              e_top = fnp->top;
                              e_bottom = fnp->bottom;
                              label_height = fnp->labelHeight;
                              width = scale * (fnp->label_len);
                              t_label_align = MSM_LABEL_TOP;
                              alrp = MemNew(sizeof(AlignLabelRect));
                              alrp->itemID = fnp->itemID;
                              alrp->itemType = data_list->choice;

                        }
                        break;
                  default:
                        break;
            }

            /*process the labels*/
            if(width > 0)
            {

                  add_label_rectangle (alrp, width, label_height, t_label_align, 
                        e_left, e_top, e_right, e_bottom);
                  link_new_align_label(arect_list, &prev, choice, alrp);
            }

            data_list = data_list->next;
      }

      return TRUE;
}

/*
*     for the contig maps, the real contig length may not correspond
*     with the mapped interval on the graphics. Need a way to find the 
*     store the interval mapped in proportion to what is in the graphic
*     the intervals are mapped by the function: FindContigList
*/
void AddIntervalForImage(ValNodePtr contig_list, ValNodePtr image_list)
{
      SeqLocPtr contig_slp;
      SeqIdPtr sip;
      ObjectIdPtr oip;
      DbtagPtr db_tag;
      ValNodePtr curr, vnp;
      AlignLabelRectPtr alrp;
      Uint1 db_type;


      for(vnp = contig_list; vnp != NULL; vnp = vnp->next)
      {
            contig_slp = vnp->data.ptrvalue;
            sip = SeqLocId(contig_slp);
            if(sip != NULL && (db_type = FindContigDB(sip)) != 0)
            {
                  db_tag = sip->data.ptrvalue;
                  oip = db_tag->tag;
                  for(curr = image_list; curr != NULL; curr = curr->next)
                  {
                        alrp = curr->data.ptrvalue;
                        if(alrp->link_db == db_type && 
                              StringCmp(alrp->label, oip->str) == 0)
                        {
                              alrp->from = SeqLocStart(contig_slp);
                              alrp->to = SeqLocStop(contig_slp);
                        }
                  }
            }
      }
}




SegmenT PicForAlignNode(ValNodePtr anp_list, SeqLocPtr m_loc, Int4 width, Int4Ptr p_scale, Int4Ptr p_maxwidth, ValNodePtr PNTR arect_list)
{
      Int4 scale;
      SegmenT pic;
      ValNode vn;
      Int4 max_width, min_scale;
      Int4 y_pos;
      Uint2 entityID;
      BioseqPtr bsp;
      

      if(anp_list == NULL)
            return NULL;
      if(arect_list != NULL)
            *arect_list = NULL;
      vn.data.ptrvalue = m_loc;
      vn.next = NULL;
      max_width = CountMaxSeqLabel(&vn);
      scale = FigureMaxScale(&vn, (Int2)width, max_width);
      min_scale = FigureMinScale (&vn, max_width);
      if(scale <=0)
            return NULL;
      pic = CreatePicture();
      y_pos = 0;
      bsp = BioseqFind(SeqLocId(m_loc));
      entityID = ObjMgrGetEntityIDForPointer((Pointer)bsp);
      DrawMPAlignment(anp_list, 0, SeqLocLen(m_loc)-1, m_loc, entityID, scale, &y_pos, MSM_MPAIR, FALSE, pic);
        load_align_label_rectangle(anp_list, arect_list, scale, FALSE);
        /* ObjMgrClearHold(); */
        SeqMgrFreeCache();

      *p_scale = scale;
      *p_maxwidth = max_width;
      return pic;
}


static void setup_option_for_cytomap(CollectSeqOptionPtr csop)
{
      Int2 i;

      csop->nointerval = TRUE;
      csop->slabel_format = PRINTID_TEXTID_ACCESSION;
      csop->seglevels = 0;
      csop->label_size = 20;
      csop->filter_level = 0;
      csop->bsp_type = CYTO_MAP;

      for(i = 0; i<FEATDEF_ANY; ++i)
      {
            csop->flabel_format[i] = OM_LABEL_CONTENT;
            csop->features[i] = FALSE;
      }
}

static void CleanUnanchoredContig(ValNodePtr PNTR anp_list)
{
      ValNodePtr vnp, prev, next;
      AnnotInfoPtr info;
      Boolean clean = FALSE;

      vnp = *anp_list;
      prev = NULL;
      while(vnp != NULL)
      {
            next = vnp->next;
            if(vnp->choice == OBJ_SEQANNOT)
            {
                  info = vnp->data.ptrvalue;
                  if(info && info->consistent == ALIGN_UNKNOWN)
                        clean = TRUE;
                  else
                        clean = FALSE;
            }
            if(clean)
            {
                  if(prev == NULL)
                        *anp_list = next;
                  else
                        prev->next = next;
                  vnp->next = NULL;
                  FreeAlignNode(vnp);
            }
            else
                  prev = vnp;
            vnp = next;
      }
}

static void AddAlignNodeOffset(ValNodePtr anp_list, Int4 offset)
{
      AlignNodePtr anp;
      AlignSegPtr asp;

      while(anp_list)
      {
            if(anp_list->choice != OBJ_SEQANNOT)
            {
                  anp = anp_list->data.ptrvalue;
                  anp->extremes.left += offset;
                  anp->extremes.right += offset;

                  for(asp = anp->segs; asp != NULL; asp = asp->next)
                  {
                        asp->gr.left += offset;
                        asp->gr.right += offset;
                  }
            }

            anp_list = anp_list->next;
      }
}

static void AddYACAnnotInfo(ValNodePtr PNTR anp_list, Uint1 type)
{
      AnnotInfoPtr info;
      ValNodePtr vnp;

      vnp = *anp_list;
      if(vnp->choice == OBJ_SEQANNOT)
      {
            info = vnp->data.ptrvalue;
            if(type == YAC_MIT)
                  StringCpy(info->annotDB, "Contigs from WI/MIT");
            else
                  StringCpy(info->annotDB, "Contigs from NHGRI");
            return;
      }

      info = MemNew(sizeof(AnnotInfo));
      if(type == YAC_MIT)
            StringCpy(info->annotDB, "Contigs from WI/MIT");
      else
            StringCpy(info->annotDB, "Contigs from NHGRI");
      vnp = ValNodeNew(NULL);
      vnp->choice = OBJ_SEQANNOT;
      vnp->data.ptrvalue = info;
      vnp->next = *anp_list;
      *anp_list = vnp;
}


static AlignNodePtr find_anp_for_image(AlignLabelRectPtr alrp, ValNodePtr anp_list)
{
      AlignNodePtr anp;

      while(anp_list)
      {
            if(anp_list->choice != OBJ_SEQANNOT)
            {
                  anp = anp_list->data.ptrvalue;
                  if(anp->itemID == alrp->itemID && anp_list->choice == alrp->itemType)
                        return anp;
            }
            anp_list = anp_list->next;
      }

      return NULL;
}


static void add_label_to_image(ValNodePtr PNTR image_list, ValNodePtr anp_list)
{
      AlignLabelRectPtr alrp;
      AlignNodePtr anp;
      ValNodePtr prev, next, curr;
      Uint1 db_type;
      DbtagPtr db_tag;
      ObjectIdPtr oip;

      curr = *image_list;
      prev = NULL;
      while(curr)
      {
            next = curr->next;
            alrp = curr->data.ptrvalue;
            anp = find_anp_for_image(alrp, anp_list);
            if(anp == NULL || anp->sip == NULL || 
                  (db_type = FindContigDB(anp->sip)) == 0)
            {
                  if(prev == NULL)
                        *image_list = next;
                  else
                        prev->next = next;
                  curr->next = NULL;
                  ValNodeFreeData(curr);
            }
            else
            {
                  prev = curr;
                  alrp->link_db = db_type;
                  db_tag = anp->sip->data.ptrvalue;
                  oip = db_tag->tag;
                  alrp->label = StringSave(oip->str);
            }

            curr = next;
      }
}
                  


/*produce a picture which shows the contigs that align to the 
* cytogenetic band. It will expand left to the half band size and 
*     right to the half band size. pic_width is the width of the picture
* in the image map, use link_db to do MIT_YAC or NHGRI_YAC and use
*     label to load oip->str. that should give enough information to 
*     re-produce the seq-id
*/
SegmenT DrawCytoContigMap (ValNodePtr slp_list, BioseqPtr cyto_bsp, 
                  Int4 pic_width, Int4Ptr pscale, ValNodePtr PNTR image_list)
{
      ValNodePtr curr;
      SeqLocPtr cyto_slp;
      SeqLocPtr slp, t_slp;
      Int4 off_start = 0, off_stop = 0;
      SeqIntPtr sint;
      Int4 len;

      SegmenT pic;
      CollectSeqOption cs_option;
      CollectAlignOption ca_option;

      ValNodePtr features;
      Uint2 entityID;
      Int4 max_label;

      MapPosPtr mpp;
      Int4 scale;
      Int4 left;

      BioseqPtr bsp;
      ValNodePtr aligns;
      ValNodePtr anp_list;
      Uint1 db_type;

      ValNodePtr contig_slp_list, list;   /*stores the mapped loc for each contig*/


      if(slp_list == NULL || cyto_bsp == NULL || pic_width < 100)
            return NULL;
      entityID = ObjMgrGetEntityIDForPointer((Pointer)cyto_bsp);
      if(entityID == 0)
            return NULL;
      
      if(image_list != NULL)
            *image_list = NULL;
      cyto_slp = NULL;
      for(curr = slp_list; curr != NULL; curr = curr->next)
      {
            slp = curr->data.ptrvalue;
            if(BioseqMatch(cyto_bsp, SeqLocId(slp)))
            {
                  cyto_slp = SeqLocIntNew(SeqLocStart(slp), SeqLocStop(slp), 
                        Seq_strand_plus, SeqLocId(slp));
                  len = SeqLocLen(cyto_slp);
                  sint = cyto_slp->data.ptrvalue;
                  sint->from = MAX(0, sint->from - len/2);
                  sint->to = MIN(cyto_bsp->length-1, sint->to + len/2);
                  off_start = SeqLocStart(slp) - sint->from;
                  off_stop = sint->to - SeqLocStop(slp);
                  break;
            }
      }

      if(cyto_slp == NULL)
            return NULL;

      /*calculate the scale for the picture*/
      SelectFont(systemFont); 
      max_label = StringWidth("XXXXXXXXXX");
      pic_width -= max_label;
      if(pic_width < 50)
      {
            SeqLocFree(cyto_slp);
            return NULL;
      }
      len = SeqLocLen(cyto_slp);
      scale = len/pic_width;
      if(len%pic_width > 0)
            scale += 1;

      /*collect information about the cytogenetic band*/
      setup_option_for_cytomap(&cs_option);
      features = CollectItemForSeqLoc(cyto_slp, entityID, 0, FALSE, NULL, 
            &cs_option, NULL, 0);
      if(features == NULL)
      {
            SeqLocFree(cyto_slp);
            return NULL;
      }

      pic = CreatePicture();
      /*draw the cytogenetic map*/
      mpp = MemNew(sizeof(MapPos));
      mpp->slp = cyto_slp;
      mpp->left = 0;
      mpp->right = len;
      mpp->top  = 0;
      mpp->bottom = 0;
      mpp->seq_top = 0;
      mpp->seq_bottom = 0;
      mpp->entityID = entityID;
      if(!DrawCytoMap(&features, mpp, scale, pic))
      {
            FreeFeatureList(features);
            MemFree(mpp);
            SeqLocFree(cyto_slp);
            DeletePicture(pic);
            return NULL;
      }
      add_seq_label_for_map(&features, mpp, pic);
      FreeFeatureList(features);
      SeqLocFree(cyto_slp);

      /*draw the contigs */
      *pscale = scale;
      set_option_for_collect_align(&ca_option, cs_option.label_size, COLLECT_HISTORY);
      ca_option.only_history = FALSE;
      ca_option.map_insert = TRUE;
      ca_option.slabel_format = cs_option.slabel_format;
      ca_option.nointerval = cs_option.nointerval;
      ca_option.no_sort = FALSE;
      ca_option.flat_insert = FALSE;

      anp_list = NULL;
      contig_slp_list = NULL;
      for(curr = slp_list; curr != NULL; curr = curr->next)
      {
            slp = curr->data.ptrvalue;
            /*check if it is the WI map or the Eric Green map*/
            if((db_type = FindContigDB(SeqLocId(slp))) != 0)
            {
                  sint = slp->data.ptrvalue;
                  left = 0;
                  bsp = BioseqFind(sint->id);
                  if(bsp != NULL)
                  {
                        t_slp = SeqLocIntNew(SeqLocStart(slp), SeqLocStop(slp), 
                              Seq_strand_plus, SeqLocId(slp));
                        sint = t_slp->data.ptrvalue;
                        if(off_start > 0)
                        {
                              sint->from -= off_start;
                              if(sint->from < 0)
                              {
                                    left = ABS(sint->from);
                                    sint->from = 0;
                              }
                        }
                        if(off_stop > 0)
                        {
                              sint->to += off_stop;
                              if(sint->to > bsp->length -1 )
                                    sint->to = bsp->length;
                        }

                        aligns = collect_anpnode_with_option(&ca_option, t_slp, entityID, 
                              COLLECT_HISTORY, OBJ_SEQALIGN, NULL, NULL, FALSE);
                        if(aligns != NULL)
                              CleanUnanchoredContig(&aligns);
                        if(aligns != NULL)
                        {
                              list = FindContigList(t_slp);
                              ValNodeLink(&contig_slp_list, list);
                              if(left > 0)
                                    AddAlignNodeOffset(aligns, left);
                              AddYACAnnotInfo(&aligns, db_type);
                              ValNodeLink(&anp_list, aligns);
                        }
                        SeqLocFree(t_slp);
                  }
            }
      }

      if(anp_list != NULL)
      {
            len = StringWidth("XXXXXX");
            DrawMPAlignment(anp_list, mpp->left-len*scale, mpp->right + 8*scale, NULL, mpp->entityID, scale, &(mpp->bottom), MSM_SEQHIST, FALSE, pic);
            if(image_list != NULL)
            {
                  load_align_label_rectangle(anp_list, image_list, scale, FALSE);
                  /*add the label so that a Seq-id can be made from link_db+label*/
                  add_label_to_image(image_list,anp_list);
                  /*add the real interval in a contig sequence*/
                  AddIntervalForImage(contig_slp_list, *image_list);
            }
            FreeAlignNode(anp_list);
      }
      MemFree(mpp);
      free_slp_list(contig_slp_list);

      return pic;
}


            






Generated by  Doxygen 1.6.0   Back to index