void SeekerUpdate()

in layer3/Seeker.cpp [1186:2171]


void SeekerUpdate(PyMOLGlobals * G)
{
  /*  CObject *o = NULL;
     int s; */

  void *hidden = NULL;
  AtomInfoType *ai;
  ObjectMolecule *obj;
  int nRow = 0;
  int label_mode = 0;
  int codes = 0;
  int max_row = 50;
  int default_color = 0;
  int align_sele = -1;          /* alignment selection */
  const int MAXCONSECUTIVEGAPS = 10;
  CSeqRow *row_vla, *row, *lab = NULL;
  row_vla = VLACalloc(CSeqRow, 10);
  /* FIRST PASS: get all the residues represented properly */
  label_mode = SettingGetGlobal_i(G, cSetting_seq_view_label_mode);

  align_sele = ExecutiveGetActiveAlignmentSele(G);

  while(ExecutiveIterateObjectMolecule(G, &obj, &hidden)) {
    if(obj->Obj.Enabled && (SettingGet_b(G, obj->Obj.Setting, NULL, cSetting_seq_view)) &&
       (obj->Obj.Name[0] != '_')) {
      int a;
      AtomInfoType *last = NULL, *last_segi = NULL, *last_chain = NULL;
      CoordSet *last_disc = NULL;
      int last_state;
      int last_abbr = true;
      int last_spacer = false;
      int nCol = 0;
      int nListEntries = 1;     /* first list starts at 1 always... */
      int est_col = obj->NAtom / 5 + 1;
      int est_char = obj->NAtom * 4;
      int first_atom_in_label;
      int missing_color = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_fill_color);
      CoordSet *cs = obj->DiscreteFlag ? NULL : ObjectMoleculeGetCoordSet(obj, std::max(0, obj->getState()));
      bool atom_in_state;

      int gapMode = SettingGet_i(G, obj->Obj.Setting, nullptr, cSetting_seq_view_gap_mode);
      int min_pad = -1;
      CSeqCol *r1 = NULL, *l1 = NULL;   /* *col */

      if(nRow >= max_row)
        break;

      codes = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_format);
      if(obj->DiscreteFlag && SettingGet_b(G,
                                           obj->Obj.Setting,
                                           NULL, cSetting_seq_view_discrete_by_state))
        codes = 4;
      default_color = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_color);

      /* allocate a row for labels, if present
         the text for the labels and the residues will line up exactly 
       */

      VLACheck(row_vla, CSeqRow, nRow);
      if((label_mode == 2) || ((label_mode == 1) && (!nRow))) {
        lab = row_vla + nRow++;
        lab->txt = VLAlloc(char, est_char);
        lab->col = VLACalloc(CSeqCol, est_col);
        lab->label_flag = true;
      } else {
        lab = NULL;
      }

      VLACheck(row_vla, CSeqRow, nRow);

      row = row_vla + nRow;
      if(lab)
        lab = row - 1;          /* critical! */
      row->txt = VLAlloc(char, est_char);
      row->col = VLACalloc(CSeqCol, est_col);
      row->fill = VLACalloc(CSeqCol, est_col / 8);
      row->atom_lists = VLACalloc(int, obj->NAtom + est_col + 1);
      row->atom_lists[0] = -1;  /* terminate the blank listQ (IMPORTANT!) */
      row->char2col = VLACalloc(int, est_char);
      row->obj = obj;
      strcpy(row->name, obj->Obj.Name);
      row->color = obj->Obj.Color;
      ai = obj->AtomInfo;

      /* copy object name onto label row */

      if(lab) {

        int st_len;
        /* copy label text */

        VLACheck(lab->col, CSeqCol, nCol);
        l1 = lab->col + nCol;
        l1->start = lab->len;
        UtilConcatVLA(&lab->txt, &lab->len, "/");
        UtilConcatVLA(&lab->txt, &lab->len, obj->Obj.Name);
        l1->stop = lab->len;
        st_len = l1->stop - l1->start;

        if(label_mode == 2) {
          /* blank equivalent text for sequence row below the fixed label */
          VLACheck(row->col, CSeqCol, nCol);
          r1 = row->col + nCol;
          r1->start = row->len;
          UtilFillVLA(&row->txt, &row->len, ' ', st_len);
          r1->stop = row->len;
          r1->spacer = true;
          nCol++;
        }
      }
      if(label_mode < 2) {      /* no label rows, so put object name into left-hand column */

        /* copy label text */

        VLACheck(row->col, CSeqCol, nCol);
        r1 = row->col + nCol;
        r1->start = row->len;
        UtilConcatVLA(&row->txt, &row->len, "/");
        UtilConcatVLA(&row->txt, &row->len, obj->Obj.Name);
        r1->stop = row->len;
        r1->spacer = true;
        row->column_label_flag = true;
        row->title_width = row->len;
        nCol++;
      } else if(label_mode == 3) {      /* otherwise just insert a blank zero-length column */
        VLACheck(row->col, CSeqCol, nCol);
        r1 = row->col + nCol;
        r1->start = row->len;
        UtilConcatVLA(&row->txt, &row->len, "");
        r1->stop = row->len;
        r1->spacer = true;
        nCol++;
      }

      if(lab) {

        int st_len;
        /* copy label text */

        VLACheck(lab->col, CSeqCol, nCol);
        l1 = lab->col + nCol;
        l1->start = lab->len;
        if(obj->NAtom) {
          UtilConcatVLA(&lab->txt, &lab->len, "/");
          UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->segi));
          UtilConcatVLA(&lab->txt, &lab->len, "/");
          UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->chain));
          UtilConcatVLA(&lab->txt, &lab->len, "/");
        } else {
          UtilConcatVLA(&lab->txt, &lab->len, "///");
        }

        l1->stop = lab->len;
        st_len = l1->stop - l1->start;

        last_segi = ai;
        last_chain = ai;
        /* blank equivalent text for sequence row below the fixed label */
        VLACheck(row->col, CSeqCol, nCol);
        r1 = row->col + nCol;
        r1->start = row->len;
        UtilFillVLA(&row->txt, &row->len, ' ', st_len);
        r1->stop = row->len;
        r1->spacer = true;
        nCol++;
      } else {                  /* if no labels, just insert a space in row below */
        VLACheck(row->col, CSeqCol, nCol);
        r1 = row->col + nCol;
        r1->start = row->len;
        UtilConcatVLA(&row->txt, &row->len, " ");
        r1->stop = row->len;
        r1->spacer = true;
        nCol++;
      }

      last_state = -1;
      for(a = 0; a < obj->NAtom; a++) {
        first_atom_in_label = false;
        if(lab && !AtomInfoSameSegmentP(G, last_segi, ai)) {

          int st_len;

          if(row->len < min_pad) {
            row->len = min_pad;
          }
          min_pad = -1;

          /* copy label text */

          VLACheck(lab->col, CSeqCol, nCol);
          l1 = lab->col + nCol;
          l1->start = lab->len;
          UtilConcatVLA(&lab->txt, &lab->len, "/");
          UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->segi));
          UtilConcatVLA(&lab->txt, &lab->len, "/");
          UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->chain));
          UtilConcatVLA(&lab->txt, &lab->len, "/");
          l1->stop = lab->len;
          st_len = l1->stop - l1->start;

          /* blank equivalent text for sequence row */
          VLACheck(row->col, CSeqCol, nCol);
          r1 = row->col + nCol;
          r1->start = row->len;
          UtilFillVLA(&row->txt, &row->len, ' ', st_len);
          r1->stop = row->len;
          r1->spacer = true;
          nCol++;

          last_abbr = false;
          last_spacer = true;
          last_segi = ai;
          last_chain = ai;

        } else if(lab && !AtomInfoSameChainP(G, last_chain, ai)) {

          int st_len;

          if(row->len < min_pad) {
            row->len = min_pad;
          }
          min_pad = -1;

          /* copy label text */

          VLACheck(lab->col, CSeqCol, nCol);
          l1 = lab->col + nCol;
          l1->start = lab->len;
          UtilConcatVLA(&lab->txt, &lab->len, "/");
          UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->chain));
          UtilConcatVLA(&lab->txt, &lab->len, "/");
          l1->stop = lab->len;
          st_len = l1->stop - l1->start;

          /* blank equivalent text for sequence row */
          VLACheck(row->col, CSeqCol, nCol);
          r1 = row->col + nCol;
          r1->start = row->len;
          UtilFillVLA(&row->txt, &row->len, ' ', st_len);
          r1->stop = row->len;
          r1->spacer = true;
          nCol++;

          last_abbr = false;
          last_spacer = true;
          last_chain = ai;
        }

        if(min_pad < 0)
        {
          min_pad = row->len + 1; // + strlen(resi)
          for (int v = ai->resv; v; v /= 10) {
            min_pad++;
          }
          if (ai->inscode) {
            min_pad++;
          }
        }

        atom_in_state = (cs && a < cs->NAtIndex && cs->AtmToIdx[a] >= 0);

        int gapsNeeded{0};

        if(gapMode != GapMode::NONE
           && AtomInfoSameChainP(G, ai, last)
           && (ai->flags & last->flags & cAtomFlag_polymer)
           && align_sele < 0){
            gapsNeeded = ai->resv - last->resv - 1;

            if(gapsNeeded > 1 && gapMode == GapMode::SINGLE){
              gapsNeeded = 1;
            }
        }

        auto push_gap = [&](const std::string& str)
          {
            UtilConcatVLA(&row->txt, &row->len, str.c_str());
            VLACheck(row->col, CSeqCol, nCol + str.size());
            r1 = row->col + nCol;
            for(int i = 0; i < str.size(); i++){
                r1->color = missing_color;
                r1->spacer = true;
                r1->stop = r1->start + 1;
                auto lastStop = r1->stop;
                nCol++;
                r1 = row->col + nCol;
                r1->start = lastStop;
            }
          };
        switch (codes) {
        case 0:                /* one letter residue codes */
          if(!AtomInfoSameResidueP(G, last, ai)) {
            char abbr[2] = "1";
            last = ai;

            VLACheck(row->col, CSeqCol, nCol);
            r1 = row->col + nCol;
            r1->start = row->len;

            //Only include non-consecutive gaps when not doing alignment
            if(gapsNeeded > 0 && gapsNeeded <= MAXCONSECUTIVEGAPS){
              for(int g = 0; g < gapsNeeded; ++g){
                push_gap("-");
              }
            }
            else if(gapsNeeded > MAXCONSECUTIVEGAPS){
                push_gap("---...---");
            }

            if(obj->DiscreteFlag)
              r1->state = ai->discrete_state;

            first_atom_in_label = true;

            // single letter codes for polymer/solvent
            if (!(ai->flags & (cAtomFlag_organic | cAtomFlag_inorganic))) {
              abbr[0] = SeekerGetAbbr(G, LexStr(G, ai->resn), 'O', 0);
            } else {
              abbr[0] = 0;
            }

            r1->hint_no_space = last_abbr || last_spacer;

            if(!abbr[0]) {
              if(last_abbr) {
                UtilConcatVLA(&row->txt, &row->len, " ");
                r1->start = row->len;
              }

              if(ai->resn)
                UtilConcatVLA(&row->txt, &row->len, LexStr(G, ai->resn));
              else
                UtilConcatVLA(&row->txt, &row->len, "''");

              r1->stop = row->len;

              UtilConcatVLA(&row->txt, &row->len, " ");
            } else {
              UtilConcatVLA(&row->txt, &row->len, abbr);
              r1->is_abbr = true;
              r1->stop = row->len;
            }

            if(!atom_in_state)
              r1->color = missing_color;
            else if(default_color < 0)
              r1->color = SeekerFindColor(G, ai, obj->NAtom - a);
            else
              r1->color = default_color;
            if(align_sele >= 0) {
              r1->tag = SeekerFindTag(G, ai, align_sele, codes, obj->NAtom - a);
            } else {
              r1->tag = 0;
            }
            nCol++;
            last_abbr = abbr[0];
          }

          break;
        case 1:                /* explicit residue codes */
          if(!AtomInfoSameResidueP(G, last, ai)) {
            last = ai;

            VLACheck(row->col, CSeqCol, nCol);
            r1 = row->col + nCol;
            r1->start = row->len;
            if(obj->DiscreteFlag)
              r1->state = ai->discrete_state;
            first_atom_in_label = true;

            //Only include non-consecutive gaps when not doing alignment
            if(gapsNeeded > 0 && gapsNeeded <= MAXCONSECUTIVEGAPS){
              for(int g = 0; g < gapsNeeded; ++g){
                  push_gap("--- ");
              }
            }
            else if(gapsNeeded > MAXCONSECUTIVEGAPS){
                push_gap("---...--- ");
            }

            if(ai->resn)
              UtilConcatVLA(&row->txt, &row->len, LexStr(G, ai->resn));
            else
              UtilConcatVLA(&row->txt, &row->len, "''");
            r1->stop = row->len;

            if(!atom_in_state)
              r1->color = missing_color;
            else if(default_color < 0)
              r1->color = SeekerFindColor(G, ai, obj->NAtom - a);
            else
              r1->color = default_color;
            if(align_sele >= 0) {
              r1->tag = SeekerFindTag(G, ai, align_sele, codes, obj->NAtom - a);
            } else {
              r1->tag = 0;
            }
            UtilConcatVLA(&row->txt, &row->len, " ");
            nCol++;
          }
          break;
        case 2:                /* atom names */
          VLACheck(row->col, CSeqCol, nCol);
          r1 = row->col + nCol;
          r1->start = row->len;
          first_atom_in_label = true;
          if(ai->name)
            UtilConcatVLA(&row->txt, &row->len, LexStr(G, ai->name));
          else
            UtilConcatVLA(&row->txt, &row->len, "''");
          r1->stop = row->len;

          if(!atom_in_state)
            r1->color = missing_color;
          else if(default_color < 0)
            r1->color = ai->color;
          else
            r1->color = default_color;
          if(align_sele >= 0) {
            r1->tag = SeekerFindTag(G, ai, align_sele, codes, obj->NAtom - a);
          } else {
            r1->tag = 0;
          }
          UtilConcatVLA(&row->txt, &row->len, " ");
          nCol++;
          break;
        case 3:                /* chains */
          if(!AtomInfoSameChainP(G, last, ai)) {
            last = ai;

            VLACheck(row->col, CSeqCol, nCol);
            r1 = row->col + nCol;
            r1->start = row->len;
            first_atom_in_label = true;

            if(ai->chain)
              UtilConcatVLA(&row->txt, &row->len, LexStr(G, ai->chain));
            else
              UtilConcatVLA(&row->txt, &row->len, "''");
            r1->stop = row->len;
            if(default_color < 0)
              r1->color = SeekerFindColor(G, ai, obj->NAtom - a);
            else
              r1->color = default_color;
            if(align_sele >= 0) {
              r1->tag = SeekerFindTag(G, ai, align_sele, codes, obj->NAtom - a);
            } else {
              r1->tag = 0;
            }
            UtilConcatVLA(&row->txt, &row->len, " ");
            nCol++;
          }
          break;
        case 4:                /* state names */
          if(obj->DiscreteFlag) {
            CoordSet *cs;
            WordType buf1;
            if((cs = obj->DiscreteCSet[a]) != last_disc) {
              last_disc = cs;
              if(cs) {
                default_color = SettingGet_i(G, cs->Setting, obj->Obj.Setting,
                                             cSetting_seq_view_color);
                VLACheck(row->col, CSeqCol, nCol);
                r1 = row->col + nCol;
                r1->start = row->len;
                r1->color = default_color;
                first_atom_in_label = true;
                if(cs->Name[0])
                  UtilConcatVLA(&row->txt, &row->len, cs->Name);
                else {
                  sprintf(buf1, "%d", ai->discrete_state);
                  UtilConcatVLA(&row->txt, &row->len, buf1);
                }
                r1->stop = row->len;
                r1->state = ai->discrete_state;
                UtilConcatVLA(&row->txt, &row->len, " ");
                nCol++;
              }
            }
          } else {
            /* non-discrete objects simply get their states enumerated
               without selections */

            if(last_state < 0) {
              int b;
              CoordSet *cs;
              WordType buf1;
              last_state = 1;
              first_atom_in_label = true;
              for(b = 0; b < obj->NCSet; b++) {
                cs = obj->CSet[b];
                if(cs) {
                  default_color = SettingGet_i(G, cs->Setting, obj->Obj.Setting,
                                               cSetting_seq_view_color);

                  VLACheck(row->col, CSeqCol, nCol);
                  r1 = row->col + nCol;
                  r1->state = b + 1;
                  r1->start = row->len;
                  r1->atom_at = nListEntries + 1;       /* tricky & dangerous */
                  r1->color = default_color;
                  if(cs->Name[0])
                    UtilConcatVLA(&row->txt, &row->len, cs->Name);
                  else {
                    sprintf(buf1, "%d", b + 1);
                    UtilConcatVLA(&row->txt, &row->len, buf1);
                  }
                  r1->stop = row->len;
                  UtilConcatVLA(&row->txt, &row->len, " ");
                  nCol++;
                }
              }
            }
          }
          break;
        case 5:                /* movie frames */
          break;
        }

        if(first_atom_in_label) {
          if(nCol > 1) {        /* terminate current list, if any */
            VLACheck(row->atom_lists, int, nListEntries);
            row->atom_lists[nListEntries] = -1;
            nListEntries++;
          }
          if(r1) {
            r1->atom_at = nListEntries;
          }
        }

        VLACheck(row->atom_lists, int, nListEntries);
        row->atom_lists[nListEntries] = a;
        nListEntries++;
        ai++;
      }

      if(lab) {
        /*        if(lab->len<row->len) {
           lab->len = row->len;
           } */
        VLASize(lab->txt, char, lab->len + 1);
        lab->txt[lab->len] = 0;
        VLACheck(lab->col, CSeqCol, nCol);      /* make sure we've got column records for labels too */
        lab->nCol = nCol;

        /*if(row->len<lab->len) {
           row->len = lab->len;
           } */
      }

      VLASize(row->txt, char, row->len + 1);
      row->txt[row->len] = 0;

      row->nCol = nCol;

      /* terminate last atom list */
      VLACheck(row->atom_lists, int, nListEntries);
      row->atom_lists[nListEntries] = -1;
      nListEntries++;
      nRow++;
    }
  }

  /* SECOND PASS: align columns to reflect current alignment and fixed labels */
  if(nRow) {
    int a, b;
    int nCol;
    int maxCol = 0;
    int done_flag = false;
    /* find out the maximum number of columns */

    for(a = 0; a < nRow; a++) {
      row = row_vla + a;
      nCol = row->nCol;
      row->accum = 0;           /* initialize the accumulators */
      row->current = 0;
      if(maxCol < nCol)
        maxCol = nCol;
    }

    if(align_sele < 0) {
      /* in the simplest mode, just start each sequence in the same column */

      b = 0;
      while(!done_flag) {
        int max_offset = 0;
        done_flag = true;
        for(a = 0; a < nRow; a++) {
          row = row_vla + a;
          if(!row->label_flag) {
            if(b < row->nCol) {
              CSeqCol *r1 = row->col + b;
              done_flag = false;

              r1->offset = r1->start + row->accum;
              if(max_offset < r1->offset)
                max_offset = r1->offset;
            }
          }
        }
        for(a = 0; a < nRow; a++) {
          row = row_vla + a;
          if(!row->label_flag) {
            if(b < row->nCol) {
              CSeqCol *r1 = row->col + b;
              if(b < 3) {
                if(r1->offset < max_offset) {
                  row->accum += max_offset - r1->offset;
                }
              }
              r1->offset = r1->start + row->accum;
            }
          }
        }
        b++;
      }
    } else {
      /* in alignment mode, line up the tags */
      int stagger = false;
      /* intialize current columns and get the starting character */
      int current = 0;
      int first = true;
      switch (SettingGetGlobal_i(G, cSetting_seq_view_unaligned_mode)) {
      case 0:
      case 1:
      case 2:
        stagger = false;
        break;
      default:
        stagger = true;
        break;
      }
      for(a = 0; a < nRow; a++) {
        row = row_vla + a;
        row->cCol = 0;
        if((!row->label_flag) && ((row->cCol < row->nCol))) {
          if(current < row->accum)
            current = row->accum;
        }
      }
      done_flag = false;
      while(!done_flag) {
        int hint_tagged_no_space = true;
        done_flag = true;
        {
          {
            /* insert untagged entries into their own columns */
            int untagged_flag = true;
            int saw_untagged_no_abbr = false;
            int hint_untagged_space = false;
            while(untagged_flag) {
              int space_added = false;
              int max_width = 0;
              untagged_flag = false;

              /* first get the spaces in... */

              for(a = 0; a < nRow; a++) {
                row = row_vla + a;
                if((!row->label_flag) && (row->cCol < row->nCol)) {
                  CSeqCol *r1 = row->col + row->cCol;
                  if(!r1->tag) {        /* not aligned */
                    int text_len = (r1->stop - r1->start);
                    if((!first) && (!space_added) && (row->cCol > 2) &&
                       (codes || (((!r1->is_abbr) && (!r1->spacer))) ||
                        hint_untagged_space || (r1->is_abbr && (!r1->hint_no_space)))) {
                      /* insert space */
                      current++;
                      space_added = true;
                    }
                    if(max_width < text_len)
                      max_width = text_len;
                  }
                }
              }

              /* then do the rest */

              for(a = 0; a < nRow; a++) {
                row = row_vla + a;
                if((!row->label_flag) && (row->cCol < row->nCol)) {
                  CSeqCol *r1 = row->col + row->cCol;
                  if(!r1->tag) {        /* not aligned */
                    int text_len = (r1->stop - r1->start);
                    untagged_flag = true;
                    done_flag = false;
                    saw_untagged_no_abbr |= (!r1->is_abbr) && (!r1->spacer);

                    first = false;
                    r1->offset = current;
                    r1->unaligned = true;

                    if(!r1->spacer) {
                      int aa;

                      for(aa = 0; aa < nRow; aa++) {    /* infill populate other rows with dashes */
                        if(aa != a) {
                          CSeqRow *row2 = row_vla + aa;
                          if(!row2->label_flag) {
                            if(row2->cCol < row2->nCol) {
                              CSeqCol *r2 = row2->col + row2->cCol;
                              if(stagger || r2->tag || r2->spacer) {
                                VLACheck(row2->fill, CSeqCol, row2->nFill);
                                r2 = row2->fill + row2->nFill;
                                r2->stop = text_len;
                                r2->offset = current;
                                row2->nFill++;
                              }
                            } else {
                              CSeqCol *r2 = row2->col + row2->cCol;
                              VLACheck(row2->fill, CSeqCol, row2->nFill);
                              r2 = row2->fill + row2->nFill;
                              r2->stop = text_len;
                              r2->offset = current;
                              row2->nFill++;
                            }
                          }
                        }
                      }
                    }
                    if(stagger)
                      current += text_len;
                    else if(max_width < text_len)
                      max_width = text_len;
                  }
                }
              }
              if(!stagger)
                current += max_width;
              if(saw_untagged_no_abbr) {
                hint_untagged_space = true;
                hint_tagged_no_space = false;
              } else {
                hint_untagged_space = false;
                hint_tagged_no_space = true;
              }
              saw_untagged_no_abbr = false;
              for(a = 0; a < nRow; a++) {
                row = row_vla + a;
                if((!row->label_flag) && (row->cCol < row->nCol)) {
                  CSeqCol *r1 = row->col + row->cCol;
                  if(!r1->tag) {
                    row->cCol++;
                  }
                }
              }
            }
          }
        }

        {
          /* next insert match-tagged entries into the same column */
          int min_tag = 0;
          for(a = 0; a < nRow; a++) {
            row = row_vla + a;
            if((!row->label_flag) && (row->cCol < row->nCol)) {
              CSeqCol *r1 = row->col + row->cCol;
              if(r1->tag && ((min_tag > r1->tag) || (!min_tag))) {
                min_tag = r1->tag;
              }
            }
          }
          if(min_tag) {
            int width, max_width = 0;
            int space_added = false;
            int rep;
            for(rep = 0; rep < 2; rep++)
              for(a = 0; a < nRow; a++) {
                row = row_vla + a;
                if((!row->label_flag) && (row->cCol < row->nCol)) {
                  CSeqCol *r1 = row->col + row->cCol;
                  if(r1->tag == min_tag) {
                    if((!first) && (!space_added) &&
                       (codes || (((!r1->is_abbr) && (!r1->spacer))) ||
                        (r1->is_abbr
                         && (!(r1->hint_no_space || hint_tagged_no_space))))) {
                      /* insert space */
                      current++;
                      space_added = true;
                    }
                    done_flag = false;
                    first = false;
                    r1->offset = current;
                    width = (r1->stop - r1->start);
                    if(max_width < width)
                      max_width = width;
                    /*   row->cCol++;  */
                  }
                }
              }
            {
              int aa;
              for(aa = 0; aa < nRow; aa++) {    /* infill populate other rows with dashes */
                CSeqRow *row2 = row_vla + aa;
                if(!row2->label_flag) {
                  if(row2->cCol < row2->nCol) {
                    CSeqCol *r1 = row2->col + row2->cCol;
                    if(r1->tag != min_tag) {
                      CSeqCol *r2;
                      VLACheck(row2->fill, CSeqCol, row2->nFill);
                      r2 = row2->fill + row2->nFill;
                      r2->stop = max_width;
                      r2->offset = current;
                      row2->nFill++;
                    }
                  } else {
                    CSeqCol *r2;
                    VLACheck(row2->fill, CSeqCol, row2->nFill);
                    r2 = row2->fill + row2->nFill;
                    r2->stop = max_width;
                    r2->offset = current;
                    row2->nFill++;
                  }
                }
              }
            }
            for(a = 0; a < nRow; a++) {
              row = row_vla + a;
              if((!row->label_flag) && (row->cCol < row->nCol)) {
                CSeqCol *r1 = row->col + row->cCol;
                if(r1->tag == min_tag) {
                  row->cCol++;
                }
              }
            }
            current += max_width;
          }
        }
      }
    }

    for(a = 0; a < nRow; a++) {
      row = row_vla + a;
      nCol = row->nCol;
      if(row->label_flag)
        lab = row;
      else {
        for(b = 0; b < nCol; b++) {
          CSeqCol *r1 = row->col + b, *l1 = NULL;
          if(lab) {
            l1 = lab->col + b;  /* if a fixed label is present, 
                                   get the final offset from the residue line */
            if(l1->stop)
              l1->offset = r1->offset;
          }
        }
        lab = NULL;
      }
    }

  }

  /* THIRD PASS: fill in labels, based on actual residue spacing */

  if(nRow && (codes != 4)) {
    int a, b, c;
    int nCol;
    for(a = 0; a < nRow; a++) {
      lab = row_vla + a;

      if(lab->label_flag) {
        int next_open = 0;
        int *atom_list;
        int st_len;
        int div, sub;
        int draw_it;
        int n_skipped = 0;
        int last_resv = -1;
        AtomInfoType *last_ai = NULL;
        ObjectMolecule *obj;
        AtomInfoType *ai;
        row = lab + 1;
        nCol = row->nCol;
        obj = row->obj;
        div = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_label_spacing);
        sub = SettingGet_i(G, obj->Obj.Setting, NULL, cSetting_seq_view_label_start);

        for(b = 0; b < nCol; b++) {
          CSeqCol *r1 = row->col + b;
          CSeqCol *l1 = lab->col + b;

          ai = NULL;
          if(r1->atom_at) {
            atom_list = row->atom_lists + r1->atom_at;
            if(*atom_list >= 0)
              ai = obj->AtomInfo + (*atom_list);        /* get first atom in list */
          }
          if(l1->stop) {        /* if label is already present, just line it up */
            l1->offset = r1->offset;
          } else if((r1->offset >= next_open) && ai) {
            if((div > 1) && (codes != 2)) {
              if(!((ai->resv - sub) % div))
                draw_it = true;
              else
                draw_it = false;
            } else {
              draw_it = true;
            }
            if(ai->resv != (last_resv + 1))     /* gap in sequence?  then draw label ASAP */
              draw_it = true;
            if(n_skipped >= (div + div))        /* don't skip too many without a label! */
              draw_it = true;

            if(AtomInfoSameResidueP(G, last_ai, ai))    /* don't ever draw a residue label twice */
              draw_it = false;

            if(draw_it) {
              n_skipped = 0;
              last_ai = ai;
              l1->start = lab->len;
              if(codes == 2) {
                UtilConcatVLA(&lab->txt, &lab->len, LexStr(G, ai->resn));
                UtilConcatVLA(&lab->txt, &lab->len, "`");
              }

              {
                char resi[8];
                AtomResiFromResv(resi, sizeof(resi), ai);
                UtilConcatVLA(&lab->txt, &lab->len, resi);
              }
              l1->stop = lab->len;
              st_len = l1->stop - l1->start + 1;
              l1->offset = r1->offset;
              next_open = r1->offset + st_len;

              /* make sure this label doesn't conflict with a fixed label */

              for(c = b + 1; c < nCol; c++) {
                CSeqCol *l2 = lab->col + c;
                if(l2->offset && (l2->offset < next_open)) {
                  l1->start = 0;
                  l1->stop = 0;
                  break;
                }
                if((c - b) > st_len)    /* only search as many columns as characters */
                  break;
              }
            } else
              n_skipped++;
          }

          if(ai)
            last_resv = ai->resv;
        }
      }
    }
  }

  /* FOURTH PASS: simply fill in character offsets */
  if(nRow) {
    int a, b;
    int nCol;
    int start, stop;
    for(a = 0; a < nRow; a++) {
      row = row_vla + a;
      row->ext_len = 0;

      if(!row->label_flag) {
        nCol = row->nCol;

        for(b = 0; b < nCol; b++) {
          CSeqCol *r1 = row->col + b;
          stop = r1->offset + (r1->stop - r1->start);
          if(row->ext_len < stop)
            row->ext_len = stop;
        }
        VLACheck(row->char2col, int, row->ext_len);
        UtilZeroMem(row->char2col, row->ext_len);
        for(b = 0; b < nCol; b++) {
          CSeqCol *r1 = row->col + b;
          int c;
          start = r1->offset;
          stop = r1->offset + (r1->stop - r1->start);
          for(c = start; c < stop; c++)
            row->char2col[c] = b + 1;
        }
      }
    }
  }
  G->Seeker->handler.fClick = SeekerClick;
  G->Seeker->handler.fRelease = SeekerRelease;
  G->Seeker->handler.fDrag = SeekerDrag;
  G->Seeker->handler.fRefresh = SeekerRefresh;
  SeqSetRowVLA(G, row_vla, nRow);
  SeqSetHandler(G, &G->Seeker->handler);
}