/*****************************************************************************
#   Copyright (C) 1994-2008 by David Gordon.
#   All rights reserved.                           
#                                                                           
#   This software is part of a beta-test version of the Consed/Autofinish
#   package.  It should not be redistributed or
#   used for any commercial purpose, including commercially funded
#   sequencing, without written permission from the author and the
#   University of Washington.
#   
#   This software is provided ``AS IS'' and any express or implied
#   warranties, including, but not limited to, the implied warranties of
#   merchantability and fitness for a particular purpose, are disclaimed.
#   In no event shall the authors or the University of Washington be
#   liable for any direct, indirect, incidental, special, exemplary, or
#   consequential damages (including, but not limited to, procurement of
#   substitute goods or services; loss of use, data, or profits; or
#   business interruption) however caused and on any theory of liability,
#   whether in contract, strict liability, or tort (including negligence
#   or otherwise) arising in any way out of the use of this software, even
#   if advised of the possibility of such damage.
#
#   Building Consed from source is error prone and not simple which is
#   why I provide executables.  Due to time limitations I cannot
#   provide any assistance in building Consed.  Even if you do not
#   modify the source, you may introduce errors due to using a
#   different version of the compiler, a different version of motif,
#   different versions of other libraries than I used, etc.  For this
#   reason, if you discover Consed bugs, I can only offer help with
#   those bugs if you first reproduce those bugs with an executable
#   provided by me--not an executable you have built.
# 
#   Modifying Consed is also difficult.  Although Consed is modular,
#   some modules are used by many other modules.  Thus making a change
#   in one place can have unforeseen effects on many other features.
#   It may takes months for you to notice these other side-effects
#   which may not seen connected at all.  It is not feasable for me to
#   provide help with modifying Consed sources because of the
#   potentially huge amount of time involved.
#
#*****************************************************************************/
//
// ContigWin.cpp
//
// implementation for ContigWin object
//
// NOTE: this is not the main module.  the main() procedure is
// in file main.cpp.
//
//




// standard or system include files
#include    <iostream.h>
#include    <stdlib.h>
#include    <stdio.h>

// miscellaneous application include files
#include    "numutil.h"

#include    "mbt_errors.h"
#include    "mbt_exception.h"

// contig and related data structure include files
#include    "ntide.h" 
#include    "locatedFragment.h"
#include    "contig.h"
#include    "contigview.h"
#include    "assembly.h"
   
#include    "guiapp.h"
#include    "consed.h"
#include    "guicontigwin.h"
#include    "colormode.h"
#include    "tedwin.h"
#include    "textbox.h"
#include    "consed.h"
#include    "gotoList.h"
#include    "lowconsqual.h"
#include    "highQualityDiscrepancyGotoList.h"
#include    "editedGotoList.h"
#include    "editedButNotNsGotoList.h"
#include    "tagsGotoList.h"
#include    "fileselector.h"
#include    "filename.h"
#include    "edit_complement_contig.h"
#include    "please_wait.h"
#include    "filePopupAndGetFilename.h"
#include    "teditor.h"
#include    "compareContigs.h"
#include    "consedParameters.h"
#include    "tag.h"
#include    "tagTypes.h"
#include    "guiPopupTagInfo.h"
#include    "guiGetTagType.h"
#include    "guiteditor.h"
#include    "bErrorMessageIfNoPhd.h"
#include    "bIsNumericMaybeWithWhitespace.h"
#include    "pickPrimers.h"
#include    "editAddConsensusTag.h"
#include    "editAddReadTag.h"
#include    "guiMultiContigNavigator.h"
#include    "createUnalignedHighQualityNavigator.h"
#include    "editMakeHighQuality.h"
#include    "popupErrorMessage2.h"
#include    "popupInfoMessage.h"
#include    "multiProblemGotoList.h"
#include    "gotoListType.h"
#include    "guiTearContig.h"
#include    "popupErrorMessage.h"
#include    "oligoTag.h"
#include    "library.h"
#include    "editChangeToXs.h"
#include    "guiGetReferenceRead.h"
#include    "soGetErrno.h"
#include    "soAddCommas.h"
#include    "editChangeToNs.h"
#include    "editOverstrikeAllReadsAtConsPos.h"
#include    "bGuiGetAnswerYesNo.h"
#include    "editAddTagsToAllReadsAtConsPos.h"
#include    "editOverstrikeConsensus.h"
#include    "nNumberFromBase4.h"



static int nLastUsedUnpaddedConsPos = 0;




//
// ctor for ContigWin object
//
ContigWin :: ContigWin ( ) 
                      :
                      pContig_(0),
                      pContigView_( 0 ),
                      pTeditor_( NULL ),
                      bButton1PressedInReadBasesArea_( false ),
                      bButton1PressedInConsensusBasesArea_( false ),
                      pGuiMultiContigNav_( NULL ),
                      nOldConsPosOfPointer_( -666 ),
                      nOldReadLineOfPointer_( -666 ),
                      bDrewEverythingSinceLastDrewVerticalLine_( false ),
                      nCurrentVerticalLineScreenCharPos_( -666 )
{
   //
   // create the companion GuiContigWin object
   //
   pGcw_ = new GuiContigWin( this );  // GuiContigWin will call ContigWin mem funs


   //
   // initialize pointer to current color mode.
   // this may eventually become an array of color modes.
   // for an extensive discussion of ColorModes, see
   // colormode.h
   //


   pColorMode_ = GAPP->pColorMeansQualityAndTags_;

   // initialize various ContigWin & GuiContigWin states
   nLeftEdgeConsensusPosition_ = 1;   // first base in consensus  
   emptySlider();
   emptyVerticalScrollBar();

   pGcw_->makeVisible();

   // if you got here, it's safe to tell the ConsEd object
   // you exist
   ConsEd::pGetConsEd()->addNewContigWin(this);

}

// this is put here so that ~ContigWin is not called directly, but rather 
// to check if it is ok to delete it

void ContigWin :: deleteIfOK() {
   if (ConsEd::pGetConsEd()->bIsThisContigWinOnAnyCompareContigsWindow( this ) ) {
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                             "You must first dismiss the Compare Contigs Window since it refers to this Aligned Reads Window with contig %s",
                          (char*) pContig_->soGetName().data() );

      InputDataError ide("");
      // flag the exception as already having been reported
      ide.setUserNotified();
      throw ide;  // we're outta here
   }

   delete this;
}



// this is here to avoid circular includes
ContigWin :: ~ContigWin() { 
   delete pTeditor_;
   delete pGcw_;
   ConsEd::pGetConsEd()->removeContigWin(this); 
}  // tell ConsEd you died



// this member function receives notification from the GuiContigWin
// that the user has pressed a char
void ContigWin :: charPress(const char c) {
   GuiApp::beep();
}

// member function for buttonpress events
void ContigWin :: buttonPress(int nButton, 
                         int bupd,
                         int nXCharPos,
                         int nYCharPos) {
   
   // paranoia made necessary by lack of typedef
   assert ((bupd == GedButtonDown) || (bupd == GedButtonUp));

   GuiApp::beep();
   if (bupd == GedButtonDown) {
      // cout << "user pressed button ";
   }  
   else {
      // cout << "user released button ";
   }
   // cout << nButton << '\n';  cout.flush();
}


// This is just for switching between the two ways of sorting the
// reads
void ContigWin :: getNewContigView() {

   if (!pContig_ ) return;

   assert( nLeftEdgeConsensusPosition_ <= nGetRightEdgeConsensusPosition() );

   pContigView_ = pContig_->pGetContigView( nLeftEdgeConsensusPosition_,
                                            nGetRightEdgeConsensusPosition() );

   resetRoomForReadNames();
}









void ContigWin :: horizontalScrollBarMoved( int nNewScrollBarPosition ) {
   int      nError;

   // set private member data to new scroll bar position
   // the slider position equals the position within the
   // entire displayable region of the contig - consensus based

   nLeftEdgeConsensusPosition_ = nNewScrollBarPosition;

   // handle startup
   if (!pContig_) return;
   
   assert( nLeftEdgeConsensusPosition_ <= nGetRightEdgeConsensusPosition() );

   pContigView_ = pContig_->pGetContigView(nLeftEdgeConsensusPosition_,
                                            nGetRightEdgeConsensusPosition() );

   

   // handle startup
   if (!pContigView_ ) return;

   maybeSortReadsAtCursor();
   displayHowReadsAreSorted();

   resetRoomForReadNames();

   //   nIndexOfTopVisibleRead_ = 0;
   // setTopAndBottomVisibleFragments();

   pGcw_->guiResetVerticalScrollBar( nIndexOfTopVisibleRead_, // val
                            0, // min
                            pContigView_->nGetNumberOfFragments() - 1, // max
                            pGcw_->nGetDisplayedReadsHigh());   // size


   drawBasesAndConsensus( true ); // erase first

}



void ContigWin :: windowResized() {

   if (!pContig_) return;

   // nLeftEdgeConsensusPosition_ remains unchanged


   assert( nLeftEdgeConsensusPosition_ <= nGetRightEdgeConsensusPosition() );

   int nOldNumberOfReads = pContigView_->nGetNumberOfFragments();

   char cOldSort = pContigView_->cHowAreReadsSorted_;
   int nOldConsPosOfSortWindow = pContigView_->nConsPosOfSortWindow_;


   pContigView_ = pContig_->pGetContigView(nLeftEdgeConsensusPosition_,
                                            nGetRightEdgeConsensusPosition() );



   // try to preserve the sort of the reads before the resize
   // (this is a little fishy--I can't see how the resize could change
   // the sort?  DG 6/2010)

   if ( nLeftEdgeConsensusPosition_ <= nOldConsPosOfSortWindow &&
        nOldConsPosOfSortWindow <= nGetRightEdgeConsensusPosition() ) {
      pContigView_->maybeSortReadsAtCursor( nOldConsPosOfSortWindow );
   }


   displayHowReadsAreSorted();



   bool bEraseFirst = 
      ( 
       ( nOldNumberOfReads == pContigView_->nGetNumberOfFragments() ) &&
       ( cOldSort == pContigView_->cHowAreReadsSorted_ ) &&
       ( nOldConsPosOfSortWindow == pContigView_->nConsPosOfSortWindow_ )
       ? false : true );

   resetRoomForReadNames();

   //   nIndexOfTopVisibleRead_ = 0;

   pGcw_->guiResetVerticalScrollBar( nIndexOfTopVisibleRead_, // val
                            0, // min
                            pContigView_->nGetNumberOfFragments() - 1, // max
                            pGcw_->nGetDisplayedReadsHigh());   // size

   // adjust the scroll bar slider size

   pGcw_->guiResizeSlider();



   drawBasesAndConsensus( bEraseFirst ); 

}


// for use by program control of vertical scroll bar
void ContigWin :: verticalScrollBarMoved( int nNewScrollBarPosition ) {
   int      nError;

   // set private member data to new scroll bar position
   // the slider position equals the position within the
   // entire displayable region of the contig - consensus based

   nIndexOfTopVisibleRead_ = nNewScrollBarPosition;
   pGcw_->guiSetSliderPosOfVerticalScrollBar( nNewScrollBarPosition );

   bool bHighlight = ( nIndexOfTopVisibleRead_ == 0 ) ? false : true;
   pGcw_->highlightVerticalScrollBar( bHighlight );


   drawBasesAndConsensus( true ); // erase first
}



void ContigWin :: verticalScrollToMakeThisReadVisible( 
                                       LocatedFragment* pLocFrag ) {
   
   int nReadLine = nGetReadLineFromLocatedFragment( pLocFrag );

   if ( nReadLine == -1 ) return;

   if ( nIndexOfTopVisibleRead_ > nReadLine ) {
      verticalScrollBarMoved( nReadLine );
   }
   else {
      // now we are concerned about the case in which the read
      // is below the bottom of the aligned reads window

      if ( pGcw_->bReadLineIsBelowBottomOfWindow( nReadLine ) ) {

         ++nIndexOfTopVisibleRead_;
         while( pGcw_->bReadLineIsBelowBottomOfWindow( nReadLine ) ) {
            ++nIndexOfTopVisibleRead_;
         }

         verticalScrollBarMoved( nIndexOfTopVisibleRead_ );
      }
   }
}



// for scrolling not directly initiated by user
void ContigWin :: scrollToConsensusPos( const int nNewPos ) {
   int nPos = nNewPos;  // preserve const param

   // don't scroll past the displayable ends
   if (nPos < pContig_->nGetFirstDisplayableContigPos()) {
      nPos =  pContig_->nGetFirstDisplayableContigPos();
   }
   else {
      // how far can we scroll and not run off the end of the consensus?
      int nMaxAllowedContigPos = 
        pContig_->nGetLastDisplayableContigPos() 
          - nGetDisplayedBasesWide() + 1;

      if ( nPos > nMaxAllowedContigPos) {
         nPos = nMaxAllowedContigPos;
      }
   }

   // this will set ContigWin member data and redraw (calling 
   // GuiContigWin routines)
   horizontalScrollBarMoved(nPos);

   // tell GuiContigWin to set the slider appropriately
   pGcw_->guiSetSliderPos(nPos);
}




// for scrolling by navigator
// will put nNewPos in the center, if possible

void ContigWin :: scrollToConsensusPosInCenter( const int nNewPos ) {

   // if the position is already on the screen, don't move

   if ( (nLeftEdgeConsensusPosition_ <= nNewPos) &&
        (nNewPos <= nGetRightEdgeConsensusPosition() ) )
      return;



   int nBasesWide = nGetDisplayedBasesWide();

   int nLeftEdgePosition = nNewPos - ( nBasesWide / 2 ) + 1;

   scrollToConsensusPos( nLeftEdgePosition );
}


void ContigWin :: scrollToConsensusPosInCenterAndSetCursorOnConsensus( const int nNewCursorConsPos ) {

   // handle startup 
   if ( !pContig_ ) return;

   assert( pContig_->bIsOnConsensus( nNewCursorConsPos ) );

   // if the position is already on the screen, don't move

   bool bNeedToScroll = true;
   if ( ( nLeftEdgeConsensusPosition_ <= nNewCursorConsPos ) &&
        ( nNewCursorConsPos <= nGetRightEdgeConsensusPosition() ) )
      bNeedToScroll = false;


   if ( bNeedToScroll ) {
      int nBasesWide = nGetDisplayedBasesWide();

      int nNewLeftEdgePosition = nNewCursorConsPos - ( nBasesWide / 2 ) + 1;

      // now check that this position is ok

      // don't scroll past the displayable ends--code from scrollToConsensusPos

      if (nNewLeftEdgePosition < pContig_->nGetFirstDisplayableContigPos()) {
         nNewLeftEdgePosition =  pContig_->nGetFirstDisplayableContigPos();
      }
      else {
         // how far can we scroll and not run off the end of the consensus?
         int nMaxAllowedContigPos = 
            pContig_->nGetLastDisplayableContigPos() 
            - nGetDisplayedBasesWide() + 1;

         if ( nNewLeftEdgePosition > nMaxAllowedContigPos) {
            nNewLeftEdgePosition = nMaxAllowedContigPos;
         }
      }

   
      // code from horizontalScrollBarMoved

      nLeftEdgeConsensusPosition_ = nNewLeftEdgePosition;

      assert( nLeftEdgeConsensusPosition_ <= nGetRightEdgeConsensusPosition() );

      // if we scrolled, we need to get a new batch of reads
      pContigView_ = pContig_->pGetContigView( nLeftEdgeConsensusPosition_,
                                               nGetRightEdgeConsensusPosition() );
   }

   // handle startup
   if ( !pContigView_ ) return;

   bool bMustResetVerticalScrollBar = false;

   // scroll back to top each time we navigate to a new positions
   if ( nIndexOfTopVisibleRead_ != 0 ) {
      nIndexOfTopVisibleRead_ = 0;
      pGcw_->highlightVerticalScrollBar( false ); // turn off green
      bMustResetVerticalScrollBar = true;
   }


   // we cannot use:
   // maybeSortByQualityAtCursor();
   // because the cursor is not yet set


   pContigView_->maybeSortReadsAtCursor( nNewCursorConsPos );
   displayHowReadsAreSorted();

   if ( bNeedToScroll ) {

      resetRoomForReadNames();

      bMustResetVerticalScrollBar = true;
   

      // tell GuiContigWin to set the slider appropriately
      pGcw_->guiSetSliderPos( nLeftEdgeConsensusPosition_ );
   }

   if ( bMustResetVerticalScrollBar ) {
      pGcw_->guiResetVerticalScrollBar( 
                                       nIndexOfTopVisibleRead_, // val
                                       0, // min
                                       pContigView_->nGetNumberOfFragments() - 1, // max
                                       pGcw_->nGetDisplayedReadsHigh() ); // size
   }



   // now do the code for setting the cursor


   // clear the cursor from all child tedwins prior to move
   drawTeditorCursors( false );

   // remove old cursor from this ContigWin
   drawCursor( false );

   // record cursor position (doesn't draw anything)
   editCursor_.setCursorOnCons( pContig_, nNewCursorConsPos );

   // also scroll traces 
   if ( pTeditor_ ) {
      
      // don't scroll the teditor if it contains all the traces--
      // it will take forever.  Instead, recreate it.

      if ( ! pTeditor_->pGuiTeditor_->bShowTracesInScrollingWindow_ ) {
         
         TedWin* pTedWin = pTeditor_->pGetGuiTedWinScrollMaster()->pTedWinGet();

         pTedWin->scrollToConsPosAndRefresh( nNewCursorConsPos - nOffsetToPutBaseInTheMiddle );
      }
   }


   // finally!
   drawBasesAndConsensus( true );

   drawCursor( true );

   drawVerticalLineAtCursorIfNecessary();
}
   
   




void ContigWin :: initializeContigWinForNewContig( 
                                const int nStartLeftEdgeConsPos,
                                const bool bPutInCenter ) {


   if (bPutInCenter ) {
      nLeftEdgeConsensusPosition_ = nStartLeftEdgeConsPos -
         nGetDisplayedBasesWide() / 2 ;
   }
   else
      nLeftEdgeConsensusPosition_ = nStartLeftEdgeConsPos;
      

   nLeftEdgeConsensusPosition_ = MAX( nLeftEdgeConsensusPosition_,
                                      pContig_->nGetFirstDisplayableContigPos()
                                      );

   // added 5/13/99 since if give cons pos off right end of
   // reads, loadGuiColorTextArraysForConsensusPositions will crash

   nLeftEdgeConsensusPosition_ = MIN( nLeftEdgeConsensusPosition_,
                                      pContig_->nGetLastDisplayableContigPos()
                                      );

   int nViewRightEdge = 
     nLeftEdgeConsensusPosition_ +
       MIN( nGetDisplayedBasesWide(), pContig_->nGetPaddedConsLength() );
   

   assert( nLeftEdgeConsensusPosition_ <= nViewRightEdge );

   pContigView_ = 
     pContig_->pGetContigView(nLeftEdgeConsensusPosition_,
                               nViewRightEdge);

   // I don't see how the cursor could be set yet.. (DG, 6/2010)
   //   maybeSortByQualityAtCursor();
   displayHowReadsAreSorted();
   
   resetRoomForReadNames();

   setTopAndBottomVisibleFragments();

   // update the label telling current contig
   pGcw_->setContigLabel( pContig_->soGetFancyName() );

   // adjust the scroll bar to reflect the new contig
   setSliderForNewContig();

   setVerticalScrollBarForNewContig();

   // activate the infobuttons that only apply to an active contig
   pGcw_->setButtonStatesForActiveContig();


   drawBasesAndConsensus( false ); // do not erase first
   
   displayContigErrorRate();


}


// pop up a dibox with information about the contig
void ContigWin :: showContigInfo() {
   if (! ConsEd::pGetAssembly() ) return;

   if (! pGetContig()) return;

   TextBox* pTB = new TextBox( "Contig Information");

   pContig_->setHighQualitySegment();

   if ( pContig_->bIsThereAHighQualitySegment() ) {
      pTB->appendWithArgs( "high quality segment from unpadded pos %d to %d\n",
                           pContig_->nUnpaddedHighQualitySegmentStart_,
                           pContig_->nUnpaddedHighQualitySegmentEnd_ );
   }
   else
      pTB->append( "The quality of this contig is so poor that it has no high quality segment\n" );
      

   // get ptr to view, an array of pointer to FragAttach from
   ContigView* cvp = pContig_->view(0, pContig_->nGetPaddedConsLength() - 1);

   int nFrag = cvp->nGetNumberOfFragments();
   pTB->appendWithArgs("number of reads:  %d\n", nFrag );

   // dump each fragment in view, aligned by char
   for (int i = 0; i < nFrag; i++) {
      // here's the attachment object
      LocatedFragment* pLF = cvp->pLocatedFragmentGet(i);
      // display fragment name, alignment, etc.
      pTB->append( pLF->soGetFragmentName() );
      if (pLF->bComp()) {
         pTB->append( " C " );
      }
      else {
         pTB->append( "   " );
      }
      pTB->appendWithArgs(" length %d  %d  %d \n",
                          pLF->nGetPaddedFragLength(),
                          pLF->nGetAlignStart(),
                          pLF->nGetAlignEnd() );
   }

   pTB->makeVisible();
}





void ContigWin :: createMultiProblemNavigator() {
   
   PleaseWait*   pPleaseWait = 
      new PleaseWait( pGcw_->widGetGuiContigWinTopLevel() );


   multiProblemGotoList* pMultiProblemGotoList =
      new multiProblemGotoList( 
                               lowConsensusQualityType |
                               highQualityDiscrepancyWithoutCompressionsAndG_dropoutsType |
                               singleStrandedRegionsType |
                               singleSubcloneRegionsType |
                               unalignedHighQualityRegionsType,
                               pContig_
                               );

   
   RWCString soColumnHeadings1 = "Contig      Read                   Consensus ";
   RWCString soColumnHeadings2 = "Name        Name                   Positions";

   char* szTitle = "Low Cons Qual/High Quality Discrep (no comp/G_dropouts)/Single Stranded and Chem/Single Subclone/Unaligned High Qual (no chimeric)";

   pGuiMultiContigNav_ =
      new guiMultiContigNavigator( szTitle,
                                   soColumnHeadings1,
                                   soColumnHeadings2,   
                                   100,
                                   "",
                                   NULL,
                                   NULL,
                                   pGcw_->widGetGuiContigWinTopLevel(),
                                   pMultiProblemGotoList,
                                   this );


   delete pPleaseWait;
}





void ContigWin :: createLowConsensusQualityNavigator() {
   // create the gotoList.  The navigator is currently responsible
   // for destroying gotoList
   LowConsQualGotoList* pGotoList = 
     new LowConsQualGotoList(pContig_);

   RWCString soColumnHeadings1 = "Contig      Read                   Consensus ";
   RWCString soColumnHeadings2 = "Name        Name                   Positions";

   char szTitle[200];
   sprintf( szTitle,  "Low consensus quality (<=%d or 98)",
            consedParameters::pGetConsedParameters()->nQualityThresholdForLowConsensusQuality_ );
   
   pGuiMultiContigNav_ =
      new guiMultiContigNavigator( szTitle,
                                   soColumnHeadings1,
                                   soColumnHeadings2,
                                   100,
                                   "",
                                   NULL,
                                   NULL,
                                   pGcw_->widGetGuiContigWinTopLevel(),
                                   pGotoList,
                                   this );
}



void ContigWin :: createHighQualityDiscrepancyNavigator( 
                         const bool bExcludeInCompressionTags,
                         const bool bExcludeMostPads ) {

   PleaseWait*   pPleaseWait = 
      new PleaseWait( pGcw_->widGetGuiContigWinTopLevel() );

   // create the gotoList.  The navigator is currently responsible
   // for destroying gotoList
   highQualityDiscrepancyGotoList* pGotoList = 
     new highQualityDiscrepancyGotoList(pContig_, 
                                        bExcludeInCompressionTags,
                                        bExcludeMostPads );

   RWCString soColumnHeadings1 = "Contig      Read                   Consensus ";
   RWCString soColumnHeadings2 = "Name        Name                   Positions";


   char szTitle[200];

   sprintf( szTitle, "High quality (>=%d) discrepanices.  (Ignore if within %d bases of unaligned region) %s",
            consedParameters::pGetConsedParameters()->nQualityThresholdForFindingHighQualityDiscrepancies_,
            consedParameters::pGetConsedParameters()->nIgnoreHighQualityDiscrepanciesThisManyBasesFromEndOfAlignedRegion_,
            ( bExcludeInCompressionTags ? " exclude in compression tags" : "" )
            );

   pGuiMultiContigNav_ =
      new guiMultiContigNavigator( szTitle,
                                   soColumnHeadings1,
                                   soColumnHeadings2,
                                   100,
                                   "",
                                   NULL,
                                   NULL,
                                   pGcw_->widGetGuiContigWinTopLevel(),
                                   pGotoList,
                                   this );

   delete pPleaseWait;
}


void ContigWin :: createEditedNavigator() {
   // create the gotoList.  The navigator is currently responsible
   // for destroying gotoList
   editedGotoList* pGotoList = 
     new editedGotoList(pContig_);

   RWCString soColumnHeadings1 = "Contig      Read                   Consensus ";
   RWCString soColumnHeadings2 = "Name        Name                   Positions";

   pGuiMultiContigNav_ =
      new guiMultiContigNavigator( "Edits",
                                   soColumnHeadings1,
                                   soColumnHeadings2,
                                   100,
                                   "",
                                   NULL,
                                   NULL,
                                   pGcw_->widGetGuiContigWinTopLevel(),
                                   pGotoList,
                                   this );
}


void ContigWin :: createEditedButNotNsNavigator() {
   // create the gotoList.  The navigator is currently responsible
   // for destroying gotoList
   editedButNotNsGotoList* pGotoList = 
     new editedButNotNsGotoList(pContig_);


   RWCString soColumnHeadings1 = "Contig      Read                   Consensus ";
   RWCString soColumnHeadings2 = "Name        Name                   Positions";

   pGuiMultiContigNav_ =
      new guiMultiContigNavigator( "Edits But Not N's",
                                   soColumnHeadings1,
                                   soColumnHeadings2,
                                   100,
                                   "",
                                   NULL,
                                   NULL,
                                   pGcw_->widGetGuiContigWinTopLevel(),
                                   pGotoList,
                                   this );
}


void ContigWin :: createTagsNavigator() {
   // create the gotoList.  The navigator is currently responsible
   // for destroying gotoList

   // handle case of empty window
   if (!pContig_ ) return;

   bool bUserPushedCancel = false;
   RWCString soTemp;

   guiGetTagType* pGuiGetTagType = new guiGetTagType();
   
   RWCString soTagTypeSelectedByUser;
   pGuiGetTagType->popupBoxAndWaitForUserToSelectTagType(
                               soTagTypeSelectedByUser,
                               bUserPushedCancel,
                               false,    // include all tag types
                               tagType::both,  // allow navigation
   // on both read and consensus tags
                               false, // bAllowUserToEnterComment
                               soTemp,
                               soTemp );

   delete pGuiGetTagType;

   if (bUserPushedCancel ) return;

   tagsGotoList* pGotoList = 
     new tagsGotoList(pContig_, soTagTypeSelectedByUser );


   RWCString soColumnHeadings1;
   RWCString soColumnHeadings2;

   tagTypes::pGetTagTypes()->getColumnHeadingsForNavigatorBox( 
                                              soTagTypeSelectedByUser,
                                              soColumnHeadings1,
                                              soColumnHeadings2 );

   pGuiMultiContigNav_ =
      new guiMultiContigNavigator( soTagTypeSelectedByUser + " tags",
                                   soColumnHeadings1,
                                   soColumnHeadings2,
                                   100, // width in chars
                                   "",
                                   NULL,
                                   NULL,
                                   pGcw_->widGetGuiContigWinTopLevel(),
                                   NULL,
                                   this );

   // if you create the XmSelectionBox with the list already there, the
   // selection box will resize itself to the size of the largest entry. 
   // In the case of autoFinishExpTag's, this is huge--larger than the 
   // largest monitor.  But the method below works--it thus respects the
   // "100" above and doesn't resize it.

   for( int nGotoItem = 0; nGotoItem < pGotoList->nGetNumGotoItems(); ++nGotoItem  ) {
      gotoItem* pGotoItem = pGotoList->pGetGotoItem( nGotoItem );
      pGuiMultiContigNav_->appendToList( pGotoItem );
   }

   pGuiMultiContigNav_->displayList( false );  // don't re-sort
}


void ContigWin :: createUnalignedHighQualityNavigator2() {

   createUnalignedHighQualityNavigator( 
                        pGcw_->widGetGuiContigWinTopLevel(),
                        pContig_,
                        this );
}




void ContigWin :: emptySlider() {
   // dummy values to create "full" slider
   pGcw_->guiResetSlider(0,  // val = leftmost pos
                         0,  // minimum
                         10, // max
                         10); // slider size 
}


void ContigWin :: emptyVerticalScrollBar() {

   pGcw_->guiResetVerticalScrollBar(0,  // val 
                         0,  // minimum
                         10, // max
                         10); // slider size 
}


void ContigWin :: setSliderForNewContig() {
   if (!pContig_) {
      emptySlider();
   }
   else {
      pGcw_->guiResetSlider(nLeftEdgeConsensusPosition_, // val
                            pContig_->nGetFirstDisplayableContigPos(), // min
                            pContig_->nGetLastDisplayableContigPos(), // max
                            nGetDisplayedBasesWide());   // size
   }
}


void ContigWin :: setVerticalScrollBarForNewContig() {
  if (!pContig_) 
    emptyVerticalScrollBar();
  else
    pGcw_->guiResetVerticalScrollBar( nIndexOfTopVisibleRead_, // val
                            0, // min
                            pContigView_->nGetNumberOfFragments() - 1, // max
                            pGcw_->nGetDisplayedReadsHigh());   // size
}




// used, for example, when adding a column of pads
void ContigWin :: resetSliderMax() {
   pGcw_->guiResetSliderMax( pContig_->nGetLastDisplayableContigPos() );
}


//////////////////////////////////////////////////////////////////////
//
// raw callbacks supplied to GuiContigWin, MenuMainWin, etc.
// these are all friends of the ContigWin class.
//
// this is yet another layer of indirection applied to 
// callbacks.  these routines serve only to convert the client data
// pointer returned in ContigWin callbacks into pointers to the 
// ContigWin objects that they apply to.  This allows the GuiContigWin
// to remain (largely) ignorant of the ContigWin.
//
//////////////////////////////////////////////////////////////////////


void ContigWin :: colorMeansMatch() {
   pColorMode_ = GAPP->pColorMeansMatch_;
   refreshContigWinAndTeditor();
}



void ContigWin :: colorMeansQuality() {
   pColorMode_ = GAPP->pColorMeansQuality_;
   refreshContigWinAndTeditor();
}


void ContigWin :: colorMeansEdited() {
   pColorMode_ = GAPP->pColorMeansEdited_;
   refreshContigWinAndTeditor();
}


void ContigWin :: colorMeansQualityAndTags() {
   pColorMode_ = GAPP->pColorMeansQualityAndTags_;
   refreshContigWinAndTeditor();
}



void ContigWin :: colorMeansEditedAndTags() {
   pColorMode_ = GAPP->pColorMeansEditedAndTags_;
   refreshContigWinAndTeditor();
}


// callback for File menu "Export consensus" button
void cbExportConsensus(void* clientData) {
   ContigWin* pCw = (ContigWin* )clientData;

   // is there something to save?
   if (! (ConsEd::pGetAssembly() && pCw->pGetContig()) ) {
      // no, pointers are null.  describe the problem
      InputDataError ide("There is no contig being edited.");
      // tell the user
      popupErrorMessage2( pCw->pGcw_->widGetGuiContigWinTopLevel(),
                          ide.szGetDesc());
      // flag the exception as already having been reported
      ide.setUserNotified();
      throw ide;  // we're outta here
   }

   // build file name for dir mask & selection box default
   FileName 
     soAssemblyDirName( ConsEd::pGetAssembly()->soGetAceFileName().soGetDirectory());
   FileName soDirMask = soAssemblyDirName + "*.fasta";
   FileName soTryOutFileName = 
     soAssemblyDirName + pCw->pGetContig()->soGetName() + ".fasta";

   FileName soUserOutFileName =
     GuiApp::popupFileSelector("Save consensus to file", 
                               soDirMask,
                               soTryOutFileName);

   // if user cancelled or did not supply name, bail now
   if (soUserOutFileName.isNull()) { return; }

   bool bGoAhead = true; 
   if (soUserOutFileName.bFileByThisNameExists()) {
      bGoAhead = 
        GuiApp::popupDecisionMessage("File %s exists.  Save anyway?",
                                     (const char*)soUserOutFileName);
   }
   if (bGoAhead) {
      pCw->pGetContig()->writeConsensusToFastaFile(soUserOutFileName);
   }
}


////////////////////////////////////////////////////////////
//
// PURPOSE:  Draws the scale, fragment bases, and consensus in
//  the cases of refresh, scrolling, etc
//
// HOW TO USE IT:
//
// 
// INPUTS: nix
//
// OUTPUTS: nix
//
// REV: Jan 26, 1995 (David Gordon)
//
////////////////////////////////////////////////////////////


void ContigWin :: drawBasesAndConsensus( const bool bEraseFirst )
{
   int nError;

   // not sure I always want to clear the window--perhaps only in the
   // case of scrolling

   if ( bEraseFirst )
      pGcw_->guiClearContigWindow();

   if (! pContig_) return; // nothing to draw

   drawScale();


   if ( consedParameters::pGetConsedParameters()->nShowProteinTranslation_
        != nDontShowProteinTranslation )
      drawProteinTranslation();

   drawConsensus();

   if ( bDrawSeparatorBetweenTopAndBottomStrandReads() ) {
      pGcw_->nGuiDrawSepLine();
   }

   //   drawReadWRItems();

   drawFragmentNames();

   drawBases();

   drawHighlightedBases();

   bDrewEverythingSinceLastDrewVerticalLine_ = true;

   drawVerticalLineAtCursorNoRedraw();
}


void    ContigWin :: drawScale() {
   if (consedParameters::pGetConsedParameters()->bCountPads_  ) 
     drawScalePadded( );
   else
     drawScaleUnpadded( );

}


void    ContigWin :: drawScaleUnpadded() {

   int  nCon;
   int  nCharScreenPosition;
   char szLabel[50];


   // find unpadded consensus index from padded position.
   // this is possibly expensive, so we call it only for the
   // first base, and then use the ntides bIsPad() fun on subsequent
   // bases, keeping our own count as we iterate through the
   // window.

   // convert to a biased number based on any tags.  (E.g., if you
   // are displaying a region of a chromosome and want to display the
   // numbers in chromosome positions.)

   int nUnpadPos = pContig_->nUnpaddedIndexStartingAtUserDefinedPosition( nLeftEdgeConsensusPosition_ );

   bool bFirstTimeThrough = true;
   for(nCon = nLeftEdgeConsensusPosition_;
       nCon <= nGetRightEdgeConsensusPosition();
       ++nCon ) {

      // is the current consensus position a pad ?
      bool bConIsPad = 
        pContig_->ntGetCons(nCon).bIsPad();

      // if not first time through, update the unpadded position
      if (! bFirstTimeThrough) {
         // if this consensus pos is not a base, increment unpadded pos
         if ( ! bConIsPad ) { nUnpadPos++; }
      }
      else {
         bFirstTimeThrough = false;
      }

      // screen position depends on all displayed bases, including pads
      nCharScreenPosition = nConsensusPositionToScreenChar( nCon );


      // only draw a tickmark if the current consensus position 
      // is not a pad

      if (! bConIsPad ) {

         // draw big ticks every 5

         if (nUnpadPos % 5 == 0) {
            pGcw_->guiDrawScaleBigTick( nCharScreenPosition );

            const int nWriteEvery10IfSmallerThanThis = 1000000;

            bool bDrawNumber = false;
            if ( ( nUnpadPos < nWriteEvery10IfSmallerThanThis ) && 
                 ( nUnpadPos % 10 == 0  ) ) 
               bDrawNumber = true;
            else if ( ( nUnpadPos >= nWriteEvery10IfSmallerThanThis ) && 
                      ( nUnpadPos % 20 == 0 ) )
               bDrawNumber = true;

            if ( bDrawNumber ) {
               RWCString soUnpadded;
               if ( nUnpadPos >= 10000 ) {
                  soUnpadded = soAddCommas( nUnpadPos );
               }
               else {
                  soUnpadded = RWCString( (long) nUnpadPos );
               }
               pGcw_->guiDrawScaleNumber( 
                              nCharScreenPosition, 
                              soUnpadded.data() );

               pGcw_->guiDrawScaleBigBigTick( nCharScreenPosition );
            }
         }
         else {
            pGcw_->guiDrawScaleLittleTick( nCharScreenPosition );
         }
      }
   }
   
   pGcw_->guiDrawScaleHorizontalLine();
}





void    ContigWin :: drawScalePadded() {

   int  nCon;
   int  nCharScreenPosition;
   char szLabel[50];


   for(nCon = nLeftEdgeConsensusPosition_;
       nCon <= nGetRightEdgeConsensusPosition();
       ++nCon ) {


      // screen position depends on all displayed bases, including pads
      nCharScreenPosition = nConsensusPositionToScreenChar( nCon );



      // draw big ticks every 5
      
      if (nCon % 5 == 0) {
         pGcw_->guiDrawScaleBigTick( nCharScreenPosition );
      
         if (nCon % 10 == 0) {
            sprintf(szLabel, "%d", nCon );
            pGcw_->guiDrawScaleNumber( nCharScreenPosition, szLabel );
         }
      }
      else {
         pGcw_->guiDrawScaleLittleTick( nCharScreenPosition );
      }
   }
   
   pGcw_->guiDrawScaleHorizontalLine();
}




////////////////////////////////////////////////////////////
//
// PURPOSE:  Draws the bases of the fragments (not the consensus)
//
// REV: Feb 2, 1995 (David Gordon)
//
////////////////////////////////////////////////////////////

void ContigWin :: drawBases()
{
   int  nTryScreenCharStart = 0;
   GuiColorText* pGctToUse;

   char szCharsToDraw[1000];
   int  nNumberOfCharsToDraw;
   int  nActualScreenCharStart;
   int  nError;


   int nMaxReadLine = 
	MIN( pContigView_->nGetNumberOfFragments() - 1,
	     pGcw_->nGetDisplayedReadsHigh() + nIndexOfTopVisibleRead_ );


   for(int nReadLine = nIndexOfTopVisibleRead_; 
       nReadLine <= nMaxReadLine; ++nReadLine) {

      LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet(nReadLine);

      // this must be reloaded for each 
      if ( bMustLoadGuiColorTextArrayForTags() ) {
         loadGuiColorTextArrayForTags( pLocFrag );
      }

      nTryScreenCharStart = 0;
      while( bGetNextColorWordForRead( pLocFrag,
                               nTryScreenCharStart,
                               nGetDisplayedBasesWide() - 1,
                               nActualScreenCharStart,
                               szCharsToDraw,
                               nNumberOfCharsToDraw,
                               pGctToUse )
            ) {
         pGcw_->guiDrawReadColorWord(nActualScreenCharStart, 
                                 nReadLine,
                                 szCharsToDraw, 
                                 nNumberOfCharsToDraw, 
                                 pGctToUse );
      }

      if ( bMustLoadGuiColorTextArrayForTags() ) {

         // draw tag color

         int nNumberOfCharsWide;
         GuiColorText* pGctToUseForTag;

         nTryScreenCharStart = 0;
         while ( bGetNextTagOnLine(  true, // read tag
                                     pLocFrag, 
                                     nTryScreenCharStart,
                                     nGetDisplayedBasesWide() - 1,
                                     nActualScreenCharStart,
                                     nNumberOfCharsWide,
                                     pGctToUseForTag ) ) {

            pGcw_->guiDrawHalfCharacterTag( true, // read tag 
                                            nActualScreenCharStart,
                                            nReadLine,
                                            nNumberOfCharsWide,
                                            pGctToUseForTag );
         }

         // the tag color will have covered up the letter
         // so draw the letter again

         RWCString soBasesToDraw;
         nTryScreenCharStart = 0;
         while( bGetNextTaggedBasesForRead( pLocFrag,
                                           nTryScreenCharStart,
                                           nGetDisplayedBasesWide() - 1,
                                           nActualScreenCharStart,
                                           soBasesToDraw,
                                           pGctToUse ) ) {

            pGcw_->guiDrawBasesWithoutBackground( 
                                         true, // on read
                                         nActualScreenCharStart,
                                         nReadLine,
                                         soBasesToDraw,
                                         pGctToUse );
         }
                                                  
      }
   }      

   // if the cursor is currently on a fragment, draw it.
   if (editCursor_.bCursorOnFrag()) {
      drawCursor(editCursor_.bGetBlinkState());
   }
}




void ContigWin :: drawProteinTranslation() {
   
   char szLabel[300];

   for( int nReadingFrame = 1; nReadingFrame <= 3; ++nReadingFrame ) {  

      proteinTranslationArrayType* pProteinTranslationArray =
         pContigView_->pProteinTranslation_[ nReadingFrame ];
      proteinTranslationScreenCharXArrayType* pProteinTranslationScreenCharXArray =
         pContigView_->pProteinTranslationScreenCharX_[ nReadingFrame ];


      if ( consedParameters::pGetConsedParameters()->nShowProteinTranslation_
           == nShowProteinTranslationTopStrand )
         sprintf( szLabel, "Reading Frame %d", nReadingFrame );
      else
         sprintf( szLabel, "Reading Frame %d", -nReadingFrame );

      pGcw_->guiDrawProteinTranslationLabel( szLabel, nReadingFrame );

      assert( pProteinTranslationArray->length() == pProteinTranslationScreenCharXArray->length() );

      for( int nAminoAcid = 0;
           nAminoAcid < pProteinTranslationArray->length(); 
           ++nAminoAcid ) {

         char cAminoAcid = (*pProteinTranslationArray)[ nAminoAcid ];
         int nScreenCharX = (*pProteinTranslationScreenCharXArray)[ nAminoAcid ];

         GuiColorText* pGctToUse;
         if ( cAminoAcid == 'M' )
            pGctToUse = pGcw_->pGctProteinStartCodon_;
         else if ( cAminoAcid == '*' )
            pGctToUse = pGcw_->pGctProteinStopCodon_;
         else
            pGctToUse = pGcw_->pGctProteinTranslation_;


         pGcw_->guiDrawAminoAcid( cAminoAcid, 
                                 nScreenCharX, 
                                 nReadingFrame, 
                                 pGctToUse );

      }
   }
}



////////////////////////////////////////////////////////////
//
// PURPOSE: draws the consensus
//
// REV: Feb 2, 1995 (David Gordon)
//
////////////////////////////////////////////////////////////

void ContigWin :: drawConsensus()
{

   pGcw_->guiDrawConsensusLabel();


   char szCharsToDraw[1000];
   int  nNumberOfCharsToDraw;
   GuiColorText* pGuiColorTextToUse;
   int  nActualScreenCharStart;


   int nTryScreenCharStart = 0;
   while( bGetNextColorWordForConsensus(
                  nTryScreenCharStart,
                  nGetDisplayedBasesWide(),
                  nActualScreenCharStart,
                  szCharsToDraw,
                  nNumberOfCharsToDraw,
                  pGuiColorTextToUse     
                  ) ) {
      
      
      pGcw_->guiDrawConsensusColorWord( nActualScreenCharStart,
                                        szCharsToDraw,
                                        nNumberOfCharsToDraw,
                                        pGuiColorTextToUse );
   }

   // quality in shown in all colormodes.  Hence show tags in all colormodes
   // except the one that isn't supposed to show tags.

   if ( pColorMode_ != GAPP->pColorMeansQuality_ ) {


      loadGuiColorTextArrayForConsensusTags();

      // draw tag color

      bool bSomeTagsWereDrawn = false;
      int nNumberOfCharsWide;
      GuiColorText* pGctToUseForTag;

      nTryScreenCharStart = 0;
      while ( bGetNextTagOnLine(     false, // consensus tags
                                     0, // doesn't matter for consensus tags
                                     nTryScreenCharStart,
                                     nGetDisplayedBasesWide() - 1,
                                     nActualScreenCharStart,
                                     nNumberOfCharsWide,
                                     pGctToUseForTag ) ) {

        
         pGcw_->guiDrawHalfCharacterTag( false,  // consensus tag
                                         nActualScreenCharStart,
                                         0, // ignored for consensus tags
                                         nNumberOfCharsWide,
                                         pGctToUseForTag );
         bSomeTagsWereDrawn = true;
      }

      if (bSomeTagsWereDrawn ) {


         // the tag color will have covered up the letter
         // so draw the letter again

         RWCString soBasesToDraw;
         int nActualScreenCharStart;
         bool bThereAreVisibleConsensusBases = 
            bGetEntireVisibleConsensusBases( soBasesToDraw, 
                                             nActualScreenCharStart );

         if ( bThereAreVisibleConsensusBases ) {

            GuiColorText* pGctToUse = GAPP->pColorMeansQuality_->pColorConsensus99_;  
            // just using forground
            // color (the color of the letter), so any base will do.


            pGcw_->guiDrawBasesWithoutBackground( 
                                         false, // consensus tag
                                         nActualScreenCharStart,
                                         0,  // not used when drawing consensus
                                         soBasesToDraw,
                                         pGctToUse );
         }
      }

   }


   // draw the cursor if on consensus
   if (editCursor_.bCursorOnCons()) { 
      drawCursor(editCursor_.bGetBlinkState()); 
   }


   pGcw_->guiDrawConsensusSeparatorLine();
   
}


// void ContigWin :: drawReadWRItems() {

//    if ( pCP->nNumberOfColumnsBeforeReadNameInAlignedReadsWindow_ <= 0 )
//       return;

//    for(int nReadLine = nIndexOfTopVisibleRead_; 
//        nReadLine < pContigView_->nGetNumberOfFragments();
//        ++nReadLine) {

//       LocatedFragment* pLocFrag = 
//          pContigView_->pLocatedFragmentGet( nReadLine );

//       RWCString soAbbreviation;
//       pLocFrag->getAbbreviationForWholeReadItems( soAbbreviation,
//                                                   soColor );
      
//       if ( !soAbbreviation.isNull() )
//          pGcw_->guiDrawAbbreviationForReadWRItem( soAbbreviation,
//                                                   soColor );

//    }
// }
   


////////////////////////////////////////////////////////////
//
// PURPOSE: draws the fragment names and direction
//
// REV: Feb 2, 1995 (David Gordon)
//
////////////////////////////////////////////////////////////

void ContigWin :: drawFragmentNames()
{
   int  nError;

   int nMaxReadLine = 
	MIN( pContigView_->nGetNumberOfFragments() - 1,
	     pGcw_->nGetDisplayedReadsHigh() + nIndexOfTopVisibleRead_ );
		


   for(int nReadLine = nIndexOfTopVisibleRead_; 
       nReadLine <= nMaxReadLine; ++nReadLine) {

      LocatedFragment* pLocFrag = 
         pContigView_->pLocatedFragmentGet( nReadLine );


      if ( !pLocFrag->soReadPrefix_.isNull() ) {
         pGcw_->guiDrawReadPrefix( nReadLine,
                                   pLocFrag->soReadPrefix_,
                                   pLocFrag->pGctForReadPrefix_ );

      }
      

      bool bIsHighlighted = pLocFrag->bReadCurrentlyHighlighted_;
      RWCString soReadName = pLocFrag->soGetFragmentName();
      
      nError = 
        pGcw_->nGuiDrawFragmentName( nReadLine, 
            soReadName,
            bIsHighlighted );                                     

      
      bool bThisReadsTraceIsUp = bIsThisTraceDisplayed( pLocFrag );

      nError = 
        pGcw_->nGuiDrawSequencingDirectionArrow( nReadLine,
                                                 pLocFrag->bComp(),
                                                 bThisReadsTraceIsUp
                                                );

   }
}



////////////////////////////////////////////////////////////
//
// PURPOSE: Allows drawing a bunch of bases at once with a single
//      XDrawString call, rather than making an X call for each base.
//      This is made possible by getting a contiguous string of bases
//      that are to be drawn in a single color.
//
// HOW TO USE IT: 
//
// 
// INPUTS:
//      pLocatedFragment, pointer to the located fragment being displayed 
//          on the current line
//      nTryScreenCharStart, 'give me a color word that starts at or after
//            this position.' (also output)
//
//      nRightMostScreenChar, don't bother extending the color word past here,
//              even if there are more bases in the same color, since they
//              would be off the screen
// OUTPUTS:
//      function return: true if there was another color word. Otherwise
//          false
//      nTryScreenCharStart, positioned at the location after the color word
//          word returned
//      nActualScreenCharStart, this may differ from the initial value of
//          nTryScreenCharStart because the former may be a position before
//          the fragment actually begins.  In such a case, 
//          nActualScreenCharStart would be set to the beginning of the
//          fragment
//      pGctToUse, points to a GuiColorText object with the 
//          graphics context of the appropriate color
//
// REV: Feb 2, 1995 (David Gordon)
//
////////////////////////////////////////////////////////////

bool    ContigWin :: bGetNextColorWordForRead(     
                                    LocatedFragment* pLocatedFragment,
                                    int& nTryScreenCharStart,
                                    const int  nRightMostScreenChar,
                                    int& nActualScreenCharStart,
                                    char *szCharsToDraw,
                                    int& nNumberOfCharsToDraw,
                                    GuiColorText*& pGctToUse )
{
   int nLeftIntersectScreenChar;
   int nRightIntersectScreenChar;
   char    cBase;
   bool bSameColor;
   int     nConsensusPosition;
   int     nPossibleEndOfColorWord;
   GuiColorText*    pGctTemp;

   bool bThereIsAnotherColorWord = 
     bIntersect( 
                nTryScreenCharStart,
                nRightMostScreenChar,
                nConsensusPositionToScreenChar(
                   pLocatedFragment->nGetAlignStart() ),
                nConsensusPositionToScreenChar(
                   pLocatedFragment->nGetAlignEnd() ), 
                nLeftIntersectScreenChar,
                nRightIntersectScreenChar
                );

   if (!bThereIsAnotherColorWord) goto error_return;

   /*------------------------------------------------------------*/
   /* handle the case in which the fragment may extend beyond the*/
   /* end of the consensus                     length - 1 is the */
   /* last consensus subscript                                   */
   /* Don't display any portion of the read that extends beyond  */
   /* the ends of the consensus.
   /*------------------------------------------------------------*/

   bThereIsAnotherColorWord = 
     bIntersect( nLeftIntersectScreenChar,
                nRightIntersectScreenChar,
                0,
                nConsensusPositionToScreenChar(
                   pContig_->nGetLastDisplayableContigPos() ),
                nActualScreenCharStart,
                nPossibleEndOfColorWord );
   
   if (!bThereIsAnotherColorWord) goto error_return;

   nConsensusPosition = 
     nScreenCharToConsensusPosition( nActualScreenCharStart );

   GetColorAndBaseForRead( pLocatedFragment, nConsensusPosition,
                   pGctToUse, cBase);    
   
   
   szCharsToDraw[0] = cBase;
   nNumberOfCharsToDraw = 1;

   bSameColor = true;
   while( bSameColor ) {
      ++nConsensusPosition;
      if (nConsensusPosition <= 
          nScreenCharToConsensusPosition( nPossibleEndOfColorWord ) )
        {
           GetColorAndBaseForRead( pLocatedFragment, nConsensusPosition,
                           pGctTemp, cBase );

           if ( pGctTemp == pGctToUse ) {

              /*------------------------------------------------------------*/
              /* the number of characters is 1 greater than the highest     */
              /* subscript                                                  */
              /*------------------------------------------------------------*/

              szCharsToDraw[nNumberOfCharsToDraw] = cBase;
              ++nNumberOfCharsToDraw;            
           }
           else
             bSameColor = false;
        }            
      else
        bSameColor = false;
   }
   
   nTryScreenCharStart = nConsensusPositionToScreenChar( nConsensusPosition );
   bThereIsAnotherColorWord = true;
 error_return:
   return( bThereIsAnotherColorWord ) ;
}    


// pack as many bases as possible into the same XDraw call that
// are all the same color (and thus use the same graphics context)

bool    ContigWin :: bGetNextColorWordForConsensus(     
                                    int& nTryScreenCharStart,
                                    const int  nRightMostScreenChar,
                                    int& nActualScreenCharStart,
                                    char *szCharsToDraw,
                                    int& nNumberOfCharsToDraw,
                                    GuiColorText*& pGctToUse )
{
   int nLeftIntersectScreenChar;
   int nRightIntersectScreenChar;
   int nRightMostConsPosOnScreen;
   char    cBase;
   bool bSameColor;
   int     nConsensusPosition;
   GuiColorText*    pGctTemp;

   bool bThereIsAnotherColorWord = 
     bIntersect( 
                nTryScreenCharStart,
                nRightMostScreenChar,
                nConsensusPositionToScreenChar(
                   pContig_->nGetStartConsensusIndex()
                                               ),
                nConsensusPositionToScreenChar(
                   pContig_->nGetEndConsensusIndex()
                                               ),
                nLeftIntersectScreenChar,
                nRightIntersectScreenChar
                );

   if (!bThereIsAnotherColorWord) goto error_return;

   nActualScreenCharStart = nLeftIntersectScreenChar;   

   nConsensusPosition = 
     nScreenCharToConsensusPosition( nActualScreenCharStart );

   nRightMostConsPosOnScreen = nScreenCharToConsensusPosition( nRightIntersectScreenChar );

   GetColorAndBaseForConsensus( nConsensusPosition,
                   pGctToUse, cBase);    
   
   
   szCharsToDraw[0] = cBase;
   nNumberOfCharsToDraw = 1;

   // now see if there are other subsequent bases in the same color
   
   bSameColor = true;
   while( bSameColor ) {
      ++nConsensusPosition;
      if (nConsensusPosition <= 
          nRightMostConsPosOnScreen )
        {
           GetColorAndBaseForConsensus( nConsensusPosition,
                           pGctTemp, cBase );

           if ( pGctTemp == pGctToUse ) {

              /*------------------------------------------------------------*/
              /* the number of characters is 1 greater than the highest     */
              /* subscript                                                  */
              /*------------------------------------------------------------*/

              szCharsToDraw[nNumberOfCharsToDraw] = cBase;
              ++nNumberOfCharsToDraw;            
           }
           else
             bSameColor = false;
        }            
      else
        bSameColor = false;
   }
   
   nTryScreenCharStart = nConsensusPositionToScreenChar( nConsensusPosition );
   bThereIsAnotherColorWord = true;
 error_return:
   return( bThereIsAnotherColorWord ) ;
}    





////////////////////////////////////////////////////////////
//
// PURPOSE: returns a single base and a single color (guicolortext
//          object) for that base
//
// REV: Feb 2, 1995 (David Gordon)
//
////////////////////////////////////////////////////////////

void ContigWin :: GetColorAndBaseForRead( LocatedFragment* pLocatedFragment,
                               const int nConsensusPosition, 
                               GuiColorText*& pGctForBase,
                               char&   cBase )
{
   Ntide nt = pLocatedFragment->ntGetFragFromConsPos(nConsensusPosition);

   cBase = nt.cGetBaseUpperOrLower();

   pGctForBase = pColorMode_->pGuiColorTextGet( pLocatedFragment,
                                                nConsensusPosition);
}



// This is used for coloring the consensus bases

void ContigWin :: GetColorAndBaseForConsensus( 
                               const int nConsensusPosition, 
                               GuiColorText*& pGctForBase,
                               char&   cBase )
{

   Ntide nt = pContig_->ntGetCons( nConsensusPosition );

   cBase = nt.cGetBaseUpperOrLower();

   // always color the consensus using the color means quality color scheme
   pGctForBase = 
      GAPP->pColorMeansQuality_->pGetGuiColorTextForConsensus( pContig_,
                                                nConsensusPosition);
}


// if passed true, draw the cursor at the current position
// if passed false, draw the normal (non-cursor) background again
void ContigWin :: drawCursor(const bool bMakeVisible) {

   // may not be one.
   if (! editCursor_.bCursorValid()) { return; }

   // is the cursor in the current view, and if so where
   int nScreenCharPos;
   int nFragLine;
   bool bOnFrag;
   bool bCursorInView = 
     editCursor_.bGetViewPos(this,
                             nScreenCharPos,
                             bOnFrag,
                             nFragLine);

   if (! bCursorInView) { return; }  // done
   
   if (bOnFrag) {
      
      // which located fragment is the cursor on
      LocatedFragment* pLocFrag = editCursor_.pLocFragGet(); 

      Ntide nt = 
        pLocFrag->ntGetFragFromConsPos(editCursor_.nConsPosGet());

      // get the character representation of the base
      char szBase[2];
      szBase[0] = nt.cGetBaseUpperOrLower();
      szBase[1] = '\0';  // draw routine wants null terminated string
      RWCString soBase = szBase;

      GuiColorText* pGuiColorText;
      // which color depends on whether you're drawing or undrawing
      // the cursor
      if (bMakeVisible && editCursor_.bGetBlinkState()) {
         pGuiColorText = pColorMode_->pGuiColorTextCursorGet();

         // tell the GuiContigWin to draw a single char at cursor pos
         pGcw_->guiDrawReadColorWord(nScreenCharPos,
                              nFragLine,
                              szBase,  // null terminated
                              1, // number of chars to draw
                              pGuiColorText );

      }
      else {

         // draw base without cursor

         pGuiColorText = pColorMode_->pGuiColorTextGet(pLocFrag,
                                               editCursor_.nConsPosGet() );

         pGcw_->guiDrawReadColorWord( nScreenCharPos,
                                      nFragLine,
                                      soBase,
                                      1, // number of chars to draw
                                      pGuiColorText );

         if ( bMustLoadGuiColorTextArrayForTags() ) {

            loadGuiColorTextArrayForTags( pLocFrag );
            GuiColorText* pGuiColorTextForTag = 
               pGetGuiColorTextForTag( editCursor_.nConsPosGet() );

            if ( pGuiColorTextForTag ) {
               
               pGcw_->guiDrawHalfCharacterTag( 
                                           true, // on read
                                           nScreenCharPos,
                                           nFragLine,
                                           1, // 1 character
                                           pGuiColorTextForTag );

               pGcw_->guiDrawBasesWithoutBackground(
                                           true, // on read
                                           nScreenCharPos,
                                           nFragLine,
                                           soBase,
                                           pGuiColorText );
            }
         }  // if ( bMustLoadGuiColorTextArrayForTags() )
      }  // if (bMakeVisible && ... ) else 

   }
   else { 
      // on consensus

      // get the char representation
      char cBase;

      // and draw it on the consensus line as cursor or background

      if ( bMakeVisible && editCursor_.bGetBlinkState() ) {

         GuiColorText* pGctToUse = pGcw_->pGetGctConsensusCursor();
         Ntide nt = pContig_->ntGetCons(editCursor_.nConsPosGet());
         cBase = nt.cGetBaseUpperOrLower();
         pGcw_->guiDrawOneConsensusChar(cBase, 
                                     nScreenCharPos,
                                     pGctToUse );

      }
      else {
         // draw base without cursor

         int nConsensusPosition = editCursor_.nConsPosGet();

         GuiColorText* pGctForBaseWithoutTag;

         GetColorAndBaseForConsensus( nConsensusPosition,
                   pGctForBaseWithoutTag, cBase);    

         pGcw_->guiDrawOneConsensusChar(cBase, 
                                     nScreenCharPos,
                                     pGctForBaseWithoutTag );

         if ( pColorMode_ != GAPP->pColorMeansQuality_ ) {
            loadGuiColorTextArrayForConsensusTags();
            
            GuiColorText* pGuiColorTextForTag = 
               pGetGuiColorTextForTag( editCursor_.nConsPosGet() );

            if ( pGuiColorTextForTag ) {
               
               pGcw_->guiDrawHalfCharacterTag( 
                                           false, // on consensus
                                           nScreenCharPos,
                                           0, // ignored for consensus
                                           1, // 1 character
                                           pGuiColorTextForTag );

               pGcw_->guiDrawBasesWithoutBackground(
                                           false, // on consensus
                                           nScreenCharPos,
                                           0, // ignored for consensus
                                           cBase,
                                           pGctForBaseWithoutTag );
            }

         } 
      } //       if ( bMakeVisible && editCursor_.bGetBlinkState() ) { else

   } //  if (bOnFrag) { else

}



void ContigWin :: popupTracesByConsensusPosition( 
                            LocatedFragment* pLocatedFragment,
                            const int nConsensusPosition ) 
{

   if ( bErrorMessageIfNoPhd() ) return;


   try {
      // read ABI file (if it hasn't already been read )
     pLocatedFragment->readTraces( true ); // ask user for pathname of traces
     // if can't find them
   }
   catch(SysRequestFailed srf ) {
      throw srf;
   }
   catch (ExceptionBase eb) {
      // failed for whatever reason.  tell the user
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                          eb.szGetDesc());
      eb.setUserNotified();  // will not be reported again
      throw eb;  // in case anybody else cares
   }


   // if there is a teditor up that has a scrolling list of all traces,
   // bring it down for now and recreate it with just the trace we
   // are trying to bring up


   if ( pTeditor_ ) {
     if ( pTeditor_->pGuiTeditor_->bShowTracesInScrollingWindow_ )
       delete pTeditor_;
   }



   if (pTeditor_ == NULL ) {

      // this exception handling is to catch the case of 
      // the user trying to bring up a trace when he started
      // consed -nophd

      try {
         pTeditor_ = new Teditor( this, 
                                  1,  // number of traces for teditor
				  false // not in scrolling window
                                  );
      }
      catch( ExceptionBase eb ) {
         // failed for whatever reason.  tell the user
         if ( !eb.bUserNotified() ) {
            popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                                eb.szGetDesc());
            eb.setUserNotified();  // will not be reported again
         }
         throw eb;  // in case anybody else cares
      }
   }
   else {
      const int nNumberOfTracesToAdd = 1;
      pTeditor_->deleteTedWinsIfTooMany( nNumberOfTracesToAdd );
      pTeditor_->pGuiTeditorGet()->enlargeTeditor( nNumberOfTracesToAdd );
      pTeditor_->pGuiTeditorGet()->repartitionTraceWindow( nNumberOfTracesToAdd );
      pTeditor_->pGuiTeditorGet()->moveDownExistingTraces( nNumberOfTracesToAdd );
   }


   TedWin* pNewTedWin;
   try {
      pNewTedWin = 
         new TedWin( pTeditor_,  
                     nConsensusPosition,
                     pLocatedFragment,
                     0, // position this trace at the top
                     false ); // bAddToScrollingWindow 
   }
   catch (...) {
      // failed for any reason
      // do not re-throw.  assume it's been dealt with and carry on.


      // if we just brought up a Teditor for this failed TedWin,
      // the bring down the Teditor
      if (pTeditor_->nNumberOfTedWins() == 0 )
         delete pTeditor_;
     
   }

   pTeditor_->alignToThisTraceIfScrollingTogether( pNewTedWin );
   pTeditor_->raiseTracesWindow();
}



////////////////////////////////////////////////////////////
//
// PURPOSE:  to popup the traces for a particular fragment
// 
//
// HOW TO USE IT: called from gui side, which translates all
//          pixel coordinates to character positions
//
// 
// INPUTS: 0-based x and y character positions within the 
//          fragment bases portion of the screen
//
// OUTPUTS: nix
//
// REV: Feb 28, 1995 (David Gordon)
//
////////////////////////////////////////////////////////////


void ContigWin :: popupTracesByCursorPosition( const int nScreenCharX, 
                                               const int nReadLine) 
{
   LocatedFragment  *pLocatedFragment;
   int              nConsensusPosition;


   pLocatedFragment = 
     pContigView_->pLocatedFragmentGet( nReadLine ); 

   // did they miss the fragment?  i.e. there is a fragment on that
   // line but no base at that position?
   nConsensusPosition = nScreenCharToConsensusPosition( nScreenCharX );
   if ((nConsensusPosition < pLocatedFragment->nGetAlignStart()) ||
       (nConsensusPosition > pLocatedFragment->nGetAlignEnd())) {
      return;
   }

   // whether or not they get the trace file, the cursor is moved here
   moveCursorToFragChar(nScreenCharX, nReadLine );

   scrollExistingTracesOrPopupIfNecessary( pLocatedFragment, nConsensusPosition );

}
   


void ContigWin :: popup4BestTracesByCursorPosition( const int nScreenCharX ) 
{
   int              nConsensusPosition;


   // did they miss the fragment?  i.e. there is a fragment on that
   // line but no base at that position?
   nConsensusPosition = nScreenCharToConsensusPosition( nScreenCharX );
   if ( !pContig_->bIsOnConsensus( nConsensusPosition ) ) {
      return;
   }


   // whether or not they get the trace file, the cursor is moved here
   moveCursorToConsPos( nConsensusPosition );

   popup4BestTracesByConsensusPosition( nConsensusPosition );
}
   


void ContigWin :: popup4BestTracesByConsensusPosition( 
                                    const int nConsPos ) 
{

   if ( bErrorMessageIfNoPhd() ) return;


   LocatedFragment* pLocFrag[4];

   find4BestReads( nConsPos, 
                   pLocFrag[0],
                   pLocFrag[1],
                   pLocFrag[2],
                   pLocFrag[3] );


   int nNumberOfTracesToAdd = 0;

   try {
      for( int nRead = 0; nRead < 4; ++nRead ) {
         // read ABI file (if it hasn't already been read )
         if (pLocFrag[ nRead ] ) {
            pLocFrag[ nRead ]->readTraces( true ); 
               // ask user for pathname if can't open traces
            ++nNumberOfTracesToAdd;
         }
      }
   }
   catch(SysRequestFailed srf ) {
      throw srf;
   }
   catch (ExceptionBase eb) {
      // failed for whatever reason.  tell the user
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                          eb.szGetDesc());
      eb.setUserNotified();  // will not be reported again
      throw eb;  // in case anybody else cares
   }



   // if there is a teditor up that has a scrolling list of all traces,
   // bring it down for now and recreate it with just the trace we
   // are trying to bring up


   if ( pTeditor_ ) {
     if ( pTeditor_->pGuiTeditor_->bShowTracesInScrollingWindow_ )
       delete pTeditor_;
   }


   if (pTeditor_ == NULL ) {

      // this exception handling is to catch the case of 
      // the user trying to bring up a trace when he started
      // consed -nophd

      try {
         pTeditor_ = new Teditor( this, nNumberOfTracesToAdd,
				  false ); // not in scrolling window
      }
      catch( ExceptionBase eb ) {
         // failed for whatever reason.  tell the user
         if ( !eb.bUserNotified() ) {
            popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                                eb.szGetDesc());
            eb.setUserNotified();  // will not be reported again
         }
         throw eb;  // in case anybody else cares
      }
   }
   else {
      pTeditor_->deleteTedWinsIfTooMany( nNumberOfTracesToAdd );
      pTeditor_->pGuiTeditorGet()->enlargeTeditor( nNumberOfTracesToAdd );
      pTeditor_->pGuiTeditorGet()->repartitionTraceWindow( nNumberOfTracesToAdd );
      pTeditor_->pGuiTeditorGet()->moveDownExistingTraces( nNumberOfTracesToAdd );
      pTeditor_->raiseTracesWindow();
   }      

   TedWin* pNewTedWin[4];

   int nPositionOnScreen = nNumberOfTracesToAdd - 1;
   for( int nRead = 3; nRead >= 0; --nRead ) {

      // there may be some reads that don't exist (e.g., there may
      // be no read that disagrees with the consensus on the top strand )

      if (pLocFrag[ nRead ] ) {

         try {
            pNewTedWin[ nRead ] = 
               new TedWin( pTeditor_,  
                           nConsPos,
                           pLocFrag[nRead],
                           nPositionOnScreen,  // position here
                           false ); // bAddToScrollingWindow
            --nPositionOnScreen;
         }
         catch (...) {
            // failed for any reason
            // do not re-throw.  assume it's been dealt with and carry on.
         }
      }
   }   

   // set cursor on one of the 4 reads


   if ( pLocFrag[0] && pLocFrag[0]->bIsInRead( nConsPos ) ) {
      moveCursorToFragPos( nConsPos, pLocFrag[0] );
      pTeditor_->alignToThisTraceIfScrollingTogether( pNewTedWin[0] );
   }
   else if ( pLocFrag[2] && pLocFrag[2]->bIsInRead( nConsPos ) ) {
      moveCursorToFragPos( nConsPos, pLocFrag[2] );
      pTeditor_->alignToThisTraceIfScrollingTogether( pNewTedWin[2] );
   }
   else if ( pLocFrag[1] && pLocFrag[1]->bIsInRead( nConsPos )) {
      moveCursorToFragPos( nConsPos, pLocFrag[1] );
      pTeditor_->alignToThisTraceIfScrollingTogether( pNewTedWin[1] );
   }
   else if ( pLocFrag[3] && pLocFrag[3]->bIsInRead( nConsPos )) {
      moveCursorToFragPos( nConsPos, pLocFrag[3] );
      pTeditor_->alignToThisTraceIfScrollingTogether( pNewTedWin[3] );
   }
   

   // if we just brought up a Teditor for this failed TedWin,
   // the bring down the Teditor
   if (pTeditor_->nNumberOfTedWins() == 0 )
      delete pTeditor_;


}



void ContigWin :: scrollExistingTracesOrPopupIfNecessary( 
                                           LocatedFragment* pLocatedFragment,
                                           const int nConsensusPosition ) {


  bool bPopupTraces = false;
  if ( pTeditor_ ) {
    if ( pTeditor_->pGuiTeditor_->bShowTracesInScrollingWindow_ )
      bPopupTraces = true;
    else {
      TedWin* pTedWin = pGetTedWinByLocatedFragment( pLocatedFragment );
      if ( pTedWin ) {
	pTeditor_->raiseTracesWindow();
	pTedWin->scrollToConsPosAndRefresh(nConsensusPosition - nOffsetToPutBaseInTheMiddle );
	bPopupTraces = false;
      }
      else
	bPopupTraces = true;
    }
  } //   if ( pTeditor_ ) {
  else
    bPopupTraces = true;


  if ( bPopupTraces )
    popupTracesByConsensusPosition( pLocatedFragment,
                                    nConsensusPosition );
}




TedWin* ContigWin :: pGetTedWinByLocatedFragment( 
                                        LocatedFragment* pLocatedFragment ) {

  TedWin* pTedWin = NULL;

  if (pTeditor_ ) {
     // NULL is returned if no TedWin contains this located fragment
     pTedWin = pTeditor_->pGetTedWinByLocatedFragment( pLocatedFragment );
  }

  // return NULL if no TedWin contains this located fragment
  return( pTedWin );
}







////////////////////////////////////////////////////////////
//
// PURPOSE: 
//
// HOW TO USE IT:  must be called whenever a new contig view is created
//
// INPUTS: nix
//
// OUTPUTS: nix 
//
// REV: (David Gordon)
//
////////////////////////////////////////////////////////////

void ContigWin :: setTopAndBottomVisibleFragments() {
   nIndexOfTopVisibleRead_ = 0;
}


// this is called as a result of the comp contig button being pressed.
// affects the Contig, LocatedFragments, etc.  but does not
// cause refresh or repositioning.  that gets done when a refresh
// message comes back from the ConsEd.
void    ContigWin :: complementContig() {
   EditAction   *pEditAction = new EditComplementContig( pContig_ );
   
   PleaseWait* pPleaseWait = new PleaseWait( pGcw_->widGetGuiContigWinTopLevel() );
   

   ConsEd::pGetConsEd()->doEditAction( pEditAction, true );

   delete pPleaseWait;
}


// called by the ConsEd to inform the ContigWin of edit action
// that has completed and will now (most likely) require a 
// refresh of the ContigWin and its child teditor
//
// now deactivates undo button on any windows other than
// the one the editaction came from
void ContigWin :: editNotify(EditAction* pEdAct) {
   // call the virtual function to refresh a ContigWin
   // this does seem a little strange...
   pEdAct->refreshContigWin(this);


   if (pTeditor_ ) {
      pEdAct->refreshTeditor( pTeditor_ );

      // tell the Teditor to appropriately set the undo buttons on 
      // all its child tedwins to be active or not, according
      // to whether or not they were the one that initiated the
      // edit action
      pTeditor_->setTedWinsUndoButtonState();
   }

   // that didn't hurt a bit, now did it?
}



void ContigWin :: refreshContigWinAndTeditor() {


   drawBasesAndConsensus( true ); // erase first

   if (pTeditor_ ) 
      pTeditor_->refreshAllTedWins( false ); // no need to recalculate 
   // base positions
}






// called by the complement edit action's refresh routine.
// scroll the contig win so that it points to the same
// strech of dna (now at the other "end") after complementation.
void ContigWin :: refreshAfterCompContig() {   


   // right edge becomes left edge


   // int nNewLeftEdgeConsPos = nGetRightEdgeConsensusPosition();
   // this didn't work because nGetRightEdgeConsensusPosition() uses
   // pContig_->nGetLastDisplayableContigPos() which has already been
   // modified and thus is no longer valid.  So I will have to calculate 
   // directly:

   int nNewLeftEdgeConsPos = nLeftEdgeConsensusPosition_ +
      nGetDisplayedBasesWide() - 1;  // this might be further to the
   // right than any read, so need to do a check below after complementing
   // this coordinate.


   // transform the coordinate
   pContig_->complement_coordinate(nNewLeftEdgeConsPos);


   // now check that this is within the range of 
   // nFirstDisplayableContigPos_ to nLastDisplayableContigPos_

   if ( nNewLeftEdgeConsPos < pContig_->nFirstDisplayableContigPos_ )
      nNewLeftEdgeConsPos = pContig_->nFirstDisplayableContigPos_;

   if ( pContig_->nLastDisplayableContigPos_ < nNewLeftEdgeConsPos )
      nNewLeftEdgeConsPos = pContig_->nLastDisplayableContigPos_;


   // transform the cursor position
   editCursor_.complementPosition();

   // since slider positions are done in terms of the consensus
   // positions and not 0 based, the slider must be reset to
   // reflect the new max and min vals, etc.
   setSliderForNewContig();

   // now scroll to the new location.
   // scrolling routine will make sure we're not off either edge.  
   scrollToConsensusPos(nNewLeftEdgeConsPos);
}


int     ContigWin :: nGetDisplayedBasesWide() const 
    { return pGcw_->nGetDisplayedBasesWide(); }


// moves the cursor to the passed consensus relative position
// or as close to it as can get.  Sets the editCursor_ 
// member to reflect the new position.
void ContigWin :: moveCursorToConsPos(const int nConsPos ) {

   if ( !pContig_->bIsOnConsensus( nConsPos ) ) 
      return;
   

   // clear the cursor from all child tedwins prior to move
   drawTeditorCursors(false);
   // remove old cursor from this ContigWin
   drawCursor(false);


   // move the cursor to its new position
   editCursor_.setCursorOnCons(pContig_, nConsPos);


   pContigView_->maybeSortReadsAtCursor( nConsPos );
   displayHowReadsAreSorted();


   // also scroll traces if move the cursor
   if ( pTeditor_ ) {

      // don't scroll the teditor if it contains all the traces--it
      // will take forever.  Instead, recreate it.

      if ( ! pTeditor_->pGuiTeditor_->bShowTracesInScrollingWindow_ ) {

      
      //      pTeditor_->drawVerticalLineThroughTracesOfAllTedWins( nConsPos );

         TedWin* pTedWin = pTeditor_->pGetGuiTedWinScrollMaster()->pTedWinGet();

         pTedWin->scrollToConsPosAndRefresh(nConsPos - nOffsetToPutBaseInTheMiddle );
      }
   }

   // if there is a vertical line, when the cursor is moved, we must
   // erase the window first and redraw all of the reads
   if ( pCP->bPutVerticalLineAtCursor_ ) {

      drawBasesAndConsensus( true );
   }


   // draw it on this ContigWin
   drawCursor(true);



   // and draw it on all child tedwins, if appropriate
   drawTeditorCursors(true);

   drawVerticalLineAtCursorIfNecessary();
   
}


// moves the cursor to the passed screen relative position
// or as close to it as can get.  Sets the editCursor_ 
// member to reflect the new position.
void ContigWin :: moveCursorToConsCharXPos(const int nScreenCharXPos) {
   // convert screen coordinate to consensus position
   int nConsPos = nScreenCharToConsensusPosition(nScreenCharXPos);
   // and call above

   if (! pContig_->bIsOnConsensus( nConsPos ) ) {
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                          "Cursor must be set on consensus or fragment bases" );
      return;
   }

   moveCursorToConsPos(nConsPos );
}


// moves the cursor to the passed screen relative position
// or as close to it as can get.
// Sets 
// the editCursor_ member to reflect the new position.

void ContigWin :: moveCursorToFragChar(const int nScreenCharXPos,
                                       const int nFragLine) {
   // convert screen coordinate to consensus position
   int nConsPos = nScreenCharToConsensusPosition(nScreenCharXPos);

   // get the located fragment pointer for that screen line
   if (nFragLine >= pContigView_->nGetNumberOfFragments()) {
      return;  // do nothing
   }

   LocatedFragment* pLocFrag = 
     pContigView_->pLocatedFragmentGet(nFragLine);
   
   // does the frag have a base at that location ?
   if ((nConsPos < pLocFrag->nGetAlignStart()) ||
       (nConsPos > pLocFrag->nGetAlignEnd())) {
      return;
   }

   // clear the cursor from all child tedwins prior to move
   drawTeditorCursors(false);
   // remove old cursor from this ContigWin
   drawCursor(false);


   // move the cursor to its new position
   editCursor_.setCursorOnFrag(pLocFrag, nConsPos);



   // also scroll traces if move the cursor
   if ( pTeditor_ ) {
      pTeditor_->reAlignTracesSinceCursorChanged( pLocFrag );

      TedWin* pTedWin = pTeditor_->pGetGuiTedWinScrollMaster()->pTedWinGet();

      pTedWin->scrollToConsPosAndRefresh(nConsPos - nOffsetToPutBaseInTheMiddle );      
   }


   // if there is a vertical line, when the cursor is moved, we must
   // erase the window first and redraw all of the reads
   if ( pCP->bPutVerticalLineAtCursor_ ) {
      drawBasesAndConsensus( true );
   }


   // draw it on this ContigWin
   drawCursor(editCursor_.bGetBlinkState());
   // and draw it on all child tedwins
   drawTeditorCursors(true);

   drawVerticalLineAtCursorIfNecessary();
}


// this version of above is called by the child TedWins,
// and does the same thing only takes arguments of
// the pointer to the located fragment and the
// consensus relative position in the fragment
void ContigWin :: moveCursorToFragPos(const int nConsPos,
                                      LocatedFragment* pLocFrag) {
   // does the frag have a base at that location ?
   if ((nConsPos < pLocFrag->nGetAlignStart()) ||
       (nConsPos > pLocFrag->nGetAlignEnd())) {
      assert (false);
   }


   scrollToConsensusPosInCenter( nConsPos );

   verticalScrollToMakeThisReadVisible( pLocFrag );

   // clear the cursor from all child tedwins prior to move
   drawTeditorCursors(false);
   // remove old cursor from this ContigWin
   drawCursor(false);
   // move the cursor to its new position
   editCursor_.setCursorOnFrag(pLocFrag, nConsPos);




   // draw it on this ContigWin
   drawCursor( true );

   if ( pTeditor_ ) {
      // do *NOT* change the master trace just because the cursor
      // moves if the user moves the cursor in the
      // show all traces modeu
      if ( !pTeditor_->pGuiTeditor_->bShowTracesInScrollingWindow_ )
         pTeditor_->reAlignTracesSinceCursorChanged( pLocFrag );
   }

   // and draw it on all child tedwins
   drawTeditorCursors(true);

   drawVerticalLineAtCursorIfNecessary();

}




// this version of above is called by the child TedWins,
// and does the same thing only takes arguments of
// the pointer to the located fragment and the
// consensus relative position in the fragment
void ContigWin :: moveCursorToNextReadPos( LocatedFragment* pLocFrag ) {

   // clear the cursor from all child tedwins prior to move
   drawTeditorCursors(false);
   // remove old cursor from this ContigWin
   drawCursor(false);

   // move the cursor to its new position
   editCursor_.advanceCursorAlongRead();

   // draw it on this ContigWin
   drawCursor( true );

   if ( pTeditor_ ) {
      pTeditor_->reAlignTracesSinceCursorChanged( pLocFrag );
   }


   // and draw it on all child tedwins
   drawTeditorCursors(true);

   drawVerticalLineAtCursorIfNecessary();
}


// sends a message to Teditor to draw 
// its cursor if appropriate
void ContigWin :: drawTeditorCursors(const bool bMakeVisible) {
   if (pTeditor_ ) 
      pTeditor_->drawCursors(bMakeVisible);
}
           

void ContigWin :: clearAllCursors() {
   drawCursor( false );  // redraw base without red

   // redraw all these bases without the red
   if ( pTeditor_ ) {
      for( int nTw = 0; nTw < pTeditor_->dapTedWin_.length(); ++nTw ) {
         pTeditor_->dapTedWin_[ nTw ]->drawCursor( false );
      }
   }

   // there is only one EditCursor object for the Aligned Reads Window
   // and all trace windows
   editCursor_.unsetCursor(); // clear cursor so subsequent blinks have no effect
}
   



void ContigWin :: setAceFileLabel( const RWCString& soAssemblyName ) {
   pGcw_->setAceFileLabel( soAssemblyName );
}




// given a pixel value that we already know falls in
// one of the fragment base lines, which line is it?
// does not include separator between for and rev,
// first visible line is 0
// chrisa 8-mar-95
//
// dg:
//
// the plus 1 is so that it starts on the next pixel
//
// pixel
// -------------------------
//
// descent
// descent + 1  (so zero starts here)
//
//   
// ------------------------- nFragmentBasesBaselinePixelY_
//
//
// descent      (and goes to here)
// descent + 1  (and 1 starts here)

int ContigWin :: nFragLineFromPixelY(const int nPixelY) const {
   

   int nReadLine = nIndexOfTopVisibleRead_;
   bool bFound = false;
   while(! bFound  && nReadLine < pContigView_->nGetNumberOfFragments() ) {
      if ( nPixelY <= 
           ( pGcw_->nFragLineToPixelY( nReadLine ) + 
             GuiApp::pGetGuiApp()->nGetFontDescent() - 1
         ) ) {
         bFound = true;
      }
      else
         ++nReadLine;
   }

   if (bFound) {
      return( nReadLine );
   }
   else
      return( -1 );

}


int ContigWin :: nGetFragmentBasesMaxPixelY() {
   int nIndexOfBottomRead = pContigView_->nGetNumberOfFragments() - 1;

   int nPixelBaseline = pGcw_->nFragLineToPixelY( nIndexOfBottomRead );

   int nPixelBottom = nPixelBaseline + GuiApp::nGetFontDescent() - 1;
   
   return( nPixelBottom );
}







bool ContigWin :: bTagIsOnScreen( tag* pTag, 
                                  LocatedFragment* pLocFrag,
                                  int& nLeftIntersectConsPos, 
                                  int& nRightIntersectConsPos ) {

   int nLeftConsPos = pTag->nPaddedConsPosStart_;
   int nRightConsPos = pTag->nPaddedConsPosEnd_;

   return( 
          bIsOnScreen( nLeftConsPos, 
                      nRightConsPos, 
                      nLeftIntersectConsPos, 
                      nRightIntersectConsPos ) 
          );
}




// checks whether a segment, from nLeftConsPos to nRightConsPos, is
// on the screen

bool ContigWin :: bIsOnScreen( const int nLeftConsPos, const int nRightConsPos,
                               int& nLeftIntersectConsPos, 
                               int& nRightIntersectConsPos ) { 

   
   int nLeftConsPos2 = nLeftEdgeConsensusPosition_;
   int nRightConsPos2 = nGetRightEdgeConsensusPosition();

   bool bIsOnScreenn = bIntersect( 
                                    nLeftConsPos, 
                                    nRightConsPos, 
                                    nLeftConsPos2, 
                                    nRightConsPos2,
                                    nLeftIntersectConsPos, 
                                    nRightIntersectConsPos );
   
   return( bIsOnScreenn );

}



void ContigWin :: popupTagInfoForTagsAtThisLocation( const int nScreenCharX, 
                                          const int nFragLine ) {

   int nConsPos = nScreenCharToConsensusPosition( nScreenCharX );

   LocatedFragment* pLocFrag = 
      pContigView_->pLocatedFragmentGet(nFragLine);
   

   int nNumberOfTagsPoppedUp = 0;
   for( int n = 0; n < pLocFrag->nGetNumberOfTags(); ++n ) {
      tag* pTag = pLocFrag->pGetTag( n );

      if (pTag->bTouchesThisConsPos( nConsPos ) ) {
         pTag->popupTagInfoForOneTag( nNumberOfTagsPoppedUp,
                                      (void*) pGcw_,
                                      true); // bIsGuiContigWin
         ++nNumberOfTagsPoppedUp;
      }
   }
}




void ContigWin :: popupTagInfoForConsensusTagsAtThisLocation( 
                                   const int nScreenCharX ) {

   int nConsPos = nScreenCharToConsensusPosition( nScreenCharX );

   int nNumberOfTagsPoppedUp = 0;
   for( int n = 0; n < pContig_->nGetNumberOfTags(); ++n ) {
      tag* pTag = pContig_->pGetTag( n );

      if (pTag->bTouchesThisConsPos( nConsPos ) ) {
         pTag->popupTagInfoForOneTag( nNumberOfTagsPoppedUp,
                                      (void*) pGcw_,
                                      true); // bIsGuiContigWin
         ++nNumberOfTagsPoppedUp;
      }
   }
}




void ContigWin :: loadGuiColorTextArrayForTags( LocatedFragment* pLocFrag )  {


   int nScreenConsPosMin = nLeftEdgeConsensusPosition_;
   int nScreenConsPosMax = nGetRightEdgeConsensusPosition();

   aGuiColorTextArray_.resize( nScreenConsPosMax - nScreenConsPosMin + 1 );

   for( int nConsPos = nScreenConsPosMin; nConsPos <= nScreenConsPosMax; ++nConsPos ) {
      aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] = 0;
   }

   for( int nTag = 0; nTag < pLocFrag->nGetNumberOfTags(); ++nTag ) {
      tag* pTag = pLocFrag->pGetTag( nTag );

      if ( !tagTypes::pGetTagTypes()->bCurrentlyShowAllTagTypes_ ) {

         if ( tagTypes::pGetTagTypes()->bAreTagsOfThisTypeHidden( 
                             pTag->soType_
                             ) )
            continue;
      }
      

      int nDrawTagConsPosMin;
      int nDrawTagConsPosMax;

      if (pTag->bTagIsOnScreen( nScreenConsPosMin, nScreenConsPosMax, 
                          nDrawTagConsPosMin, nDrawTagConsPosMax ) ) {
         
         GuiColorText* pGctToUse = 
            tagTypes::pGetTagTypes()->pGetGuiColorTextForTag( pTag );

         for( int nConsPos = nDrawTagConsPosMin; nConsPos <= nDrawTagConsPosMax;
              ++nConsPos ) {
            
            if ( aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] ) 
               aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] =
                  tagTypes::pGetTagTypes()->pGetGuiColorTextForTagsOverlap();
            else
               aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] = pGctToUse;
         }
      } // if (bTagIsOnScreen ...
   } // for (nTag = 0 ...

}



void ContigWin :: loadGuiColorTextArrayForConsensusTags( )  {


   int nScreenConsPosMin = nLeftEdgeConsensusPosition_;
   int nScreenConsPosMax = nGetRightEdgeConsensusPosition();

   aGuiColorTextArray_.resize( nScreenConsPosMax - nScreenConsPosMin + 1 );

   for( int nConsPos = nScreenConsPosMin; nConsPos <= nScreenConsPosMax; ++nConsPos ) {
      aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] = 0;
   }

   for( int nTag = 0; nTag < pContig_->nGetNumberOfTags(); ++nTag ) {
      tag* pTag = pContig_->pGetTag( nTag );

      if ( !tagTypes::pGetTagTypes()->bCurrentlyShowAllTagTypes_ ) {

         if ( tagTypes::pGetTagTypes()->bAreTagsOfThisTypeHidden( 
                             pTag->soType_
                             ) )
            continue;
      }
      

      int nDrawTagConsPosMin;
      int nDrawTagConsPosMax;

      if (pTag->bTagIsOnScreen( nScreenConsPosMin, nScreenConsPosMax, 
                          nDrawTagConsPosMin, nDrawTagConsPosMax ) ) {
         

         for( int nConsPos = nDrawTagConsPosMin; nConsPos <= nDrawTagConsPosMax;
              ++nConsPos ) {
            
            if ( aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] ) 
               aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] =
                  tagTypes::pGetTagTypes()->pGetGuiColorTextForTagsOverlap();
            else {
               bool bLoadedBase = false;
               if ( pTag->soType_ == "oligo" ) {
                  if ( ((oligoTag*) pTag)->bIsThisThe3PrimeEnd( nConsPos ) ) {
                     aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] =
                        tagTypes::pGetTagTypes()->pGetGuiColorTextForTagType( "oligo3PrimeEnd" );
                     bLoadedBase = true;
                  }
               }
               
               if ( !bLoadedBase ) {
                  aGuiColorTextArray_[ nConsPos - nScreenConsPosMin ] =
                     tagTypes::pGetTagTypes()->pGetGuiColorTextForTag( 
                                                                      pTag );
               }
            }
         }
      } // if (bTagIsOnScreen ...
   } // for (nTag = 0 ...

}



GuiColorText* ContigWin :: pGetGuiColorTextForTag( const int nConsPos ) {

   int nScreenChar = nConsensusPositionToScreenChar( nConsPos );
   
   return( aGuiColorTextArray_[ nScreenChar ] );
}



bool ContigWin :: bGetNextTagOnLine( const bool bReadNotConsensusTag, 
                                     LocatedFragment* pLocFrag, 
                                     int& nTryScreenCharStart,
                                     const int nRightMostScreenChar,
                                     int& nActualScreenCharStart,
                                     int&  nNumberOfCharsWide,
                                     GuiColorText*& pGctToUseForTag )  {


   int nScreenCharSequenceStart;
   int nScreenCharSequenceEnd;
   
   if ( bReadNotConsensusTag ) {
      nScreenCharSequenceStart = nConsensusPositionToScreenChar(
                   pLocFrag->nGetAlignStart() );
      nScreenCharSequenceEnd = nConsensusPositionToScreenChar(
                   pLocFrag->nGetAlignEnd() );
   }
   else {
      nScreenCharSequenceStart = nConsensusPositionToScreenChar(
                   pContig_->nGetStartConsensusIndex() );
      nScreenCharSequenceEnd = 
         nConsensusPositionToScreenChar( pContig_->nGetEndConsensusIndex() );
   }


   int nLeftIntersectScreenChar;
   int nRightIntersectScreenChar;

   bool bThereIsAnotherColorWord = 
     bIntersect( 
                nTryScreenCharStart,
                nRightMostScreenChar,
                nScreenCharSequenceStart,
                nScreenCharSequenceEnd,
                nLeftIntersectScreenChar,
                nRightIntersectScreenChar
                );

   if (!bThereIsAnotherColorWord) return( false );



   // find first base for which there is a tag
   
   nActualScreenCharStart = nLeftIntersectScreenChar;

   bool bFound = false;
   while( !bFound && nActualScreenCharStart <= nRightIntersectScreenChar ) {
      if ( aGuiColorTextArray_[ nActualScreenCharStart ] )
         bFound = true;
      else
         ++nActualScreenCharStart;
   }


   // return if no more tags on this line
   if (!bFound ) return( false );


   pGctToUseForTag = aGuiColorTextArray_[ nActualScreenCharStart ];

   // now see how long this tag color lasts

   int nCurrentScreenPosition = nActualScreenCharStart + 1;
   bool bSame = true;
   while( bSame && nCurrentScreenPosition <= nRightIntersectScreenChar ) {
      if ( aGuiColorTextArray_[ nCurrentScreenPosition ] == pGctToUseForTag )
         ++nCurrentScreenPosition;
      else
         bSame = false;
   }

   // nCurrentScreenPosition is now 1 too big--it is either on a different
   // tag color, or else it is past the rightmost position on the screen

   nNumberOfCharsWide = nCurrentScreenPosition - nActualScreenCharStart;

   
   // set up for next time
   nTryScreenCharStart = nCurrentScreenPosition;
   return( true );
}    








bool ContigWin :: bGetNextTaggedBasesForRead( LocatedFragment* pLocFrag, 
                                     int& nTryScreenCharStart,
                                     const int nRightMostScreenChar,
                                     int& nActualScreenCharStart,
                                     RWCString& soBasesToDraw,
                                     GuiColorText*& pGctToUse )  {


   
   int nLeftIntersectScreenChar;
   int nRightIntersectScreenChar;

   bool bThereIsAnotherColorWord = 
     bIntersect( 
                nTryScreenCharStart,
                nRightMostScreenChar,
                nConsensusPositionToScreenChar(
                   pLocFrag->nGetAlignStart() ),
                nConsensusPositionToScreenChar(
                   pLocFrag->nGetAlignEnd() ), 
                nLeftIntersectScreenChar,
                nRightIntersectScreenChar
                );

   if (!bThereIsAnotherColorWord) return( false );




   // find first base for which there is a tag
   
   nActualScreenCharStart = nLeftIntersectScreenChar;

   bool bFound = false;
   while( !bFound && nActualScreenCharStart <= nRightIntersectScreenChar ) {
      if ( aGuiColorTextArray_[ nActualScreenCharStart ] )
         bFound = true;
      else
         ++nActualScreenCharStart;
   }


   // return if no more tags on this line
   if (!bFound ) return( false );


   int nConsPos = 
     nScreenCharToConsensusPosition( nActualScreenCharStart );

   pGctToUse = pColorMode_->pGuiColorTextGetForeground(
                                             pLocFrag,
                                             nConsPos );


   soBasesToDraw = 
      pLocFrag->ntGetFragFromConsPos( nConsPos ).cGetBaseUpperOrLower();

   // now see how long this tag color lasts

   int nCurrentScreenPosition = nActualScreenCharStart + 1;
   bool bSame = true;
   while( bSame && nCurrentScreenPosition <= nRightIntersectScreenChar ) {
      if ( aGuiColorTextArray_[ nCurrentScreenPosition ] ) {

         nConsPos = 
            nScreenCharToConsensusPosition( nCurrentScreenPosition );

         GuiColorText* pGctTemp = pColorMode_->pGuiColorTextGetForeground(
                                             pLocFrag,
                                             nConsPos );
         if ( pGctToUse == pGctTemp ) {
            soBasesToDraw += 
               pLocFrag->ntGetFragFromConsPos( nConsPos ).cGetBaseUpperOrLower();

            ++nCurrentScreenPosition;
         }
         else
            bSame = false;
      }
      else
         bSame = false;
   }

   
   // set up for next time
   nTryScreenCharStart = nCurrentScreenPosition;
   return( true );
}    






void ContigWin :: userReleasedMouseButton1( const int nPixelX, 
                                            const int nPixelY ) {


   // must stop automatic scrolling, if it is still going on

   pGcw_->stopAutomaticScrollingIfNecessary();


   selection* pSelection = ConsEd::pGetConsEd()->pGetSelection();


   int      nConsPosLeft;
   int      nConsPosRight;
   LocatedFragment* pLocFrag;
   bool     bSelectionActive;
   Contig*  pContig;


   pSelection->getSelection(nConsPosLeft,
                            nConsPosRight,
                            pLocFrag,
                            pContig,
                            bSelectionActive);

   if ( !bSelectionActive ) return;

   if ( pSelection->bIsOnConsensusNotRead() ) {
      makeSelectionFromConsensus( nConsPosLeft,
                                  nConsPosRight );
   }
   else {
      makeSelectionFromARead( pLocFrag,
                              nConsPosLeft,
                              nConsPosRight );
   }
}



void ContigWin :: clearHighlight() {

   selection* pSelection = ConsEd::pGetConsEd()->pGetSelection();

   if ( pSelection->bSelectionActive_ ) {
      if ( pSelection->bIsOnConsensusNotRead() )
         drawConsensus();
      else
         drawBases();
   }

   pSelection->bSelectionActive_ = false;
}



      
void ContigWin :: startHighlight( const int nScreenCharX, 
                                  const bool bConsensusNotRead,
                                  const int nReadLine ) {

   
   // unhighlight any old highlighted bases:
   clearHighlight();

   int nConsPos = nScreenCharToConsensusPosition( nScreenCharX );

   if (bConsensusNotRead ) {

      if ( nConsPos < pContig_->nGetStartIndex() ||
           pContig_->nGetEndIndex() < nConsPos ) {
         
         popupErrorMessage( "If you are highlighting the consensus, you must be on the consensus" );
         return;
      }

      ConsEd::pGetConsEd()->pGetSelection()->startSelection( NULL,
                                                             pContig_,
                                                             nConsPos );
   }
   else {

      LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet(nReadLine);      
      if ( !pLocFrag->bIsInRead( nConsPos ) ) {
         
         popupErrorMessage( "If you are highlighting the read, you must be on the read bases" );
         return;
      }


      ConsEd::pGetConsEd()->pGetSelection()->startSelection( pLocFrag,
                                                             NULL,
                                                             nConsPos );
   }
}





// just called via a motion event
void ContigWin :: continueHighlight( const int nPixelX, const int nPixelY ) {

   pGcw_->handleMovingMouseOffOrBackIntoWindow( nPixelX );

   int nScreenCharX = pGcw_->nCharXFromPixel( nPixelX );

   //   if ( nScreenCharX == nButton1PressedScreenX_ ) return;

   int nConsPos =
            nScreenCharToConsensusPosition( nScreenCharX );

   continueHighlight3( nConsPos );

}   
         


// can be called either via a motion event or a timer expiring

void ContigWin :: continueHighlight3( const int nConsPos ) {

   selection* pSelection = ConsEd::pGetConsEd()->pGetSelection();

   if ( !pSelection->bSelectionActive_ ) return;

   if ( pSelection->bIsOnConsensusNotRead() ) {
      // do not extend consensus selections beyond the end of the consensus

      if ( nConsPos < pContig_->nGetStartIndex() ||
           pContig_->nGetEndIndex() < nConsPos ) {
         GuiApp::beep();
         return;
      }
   }
   else {
      // do not extend consensus selections beyond the end of the read

      if ( nConsPos < pSelection->pLocatedFragment_->nGetAlignStart() ||
           pSelection->pLocatedFragment_->nGetAlignEnd() < nConsPos ) {
         GuiApp::beep();
         return;
      }
   }



   if ( pSelection->nExtendSelection( nConsPos ) 
        != ERR_NO_NEED_TO_EXTEND ) {

      if ( pSelection->bIsOnConsensusNotRead() ) {
         drawConsensus();
      }
      else {
         drawBases();
      }

      drawHighlightedBases();

   }
}   
         






void ContigWin :: highlightFinished() {

   int      nConsensusPosLeft;
   int      nConsensusPosRight;
   LocatedFragment* pDummyLocatedFragment;
   bool     bSelectionActive;
   Contig*  pContig;


   // must stop automatic scrolling, if it is still going on

   pGcw_->stopAutomaticScrollingIfNecessary();

   ConsEd::pGetConsEd()->pGetSelection()->getSelection(nConsensusPosLeft,
                                               nConsensusPosRight,
                                               pDummyLocatedFragment,
                                               pContig,
                                               bSelectionActive);

   // case in which someone presses down button outside the consensus and 
   // then finishes inside the consensus

   if ( ! bSelectionActive ) return;

   if ( ConsEd::pGetConsEd()->pGetSelection()->bIsOnConsensusNotRead() ) {

      assert( pContig );

      guiGetTagType* pGuiGetTagType = new guiGetTagType();
      
      bool bUserPushedCancel;
      RWCString soComment;
      RWCString soContigName;
      RWCString soTagType;

      tagType::readOrConsensusType readOrConsensusTypeUserWants;

      pGuiGetTagType->popupBoxAndWaitForUserToSelectTagType2( 
                                            soTagType, 
                                            bUserPushedCancel,
                                            true,  // only tag types
                                            // that can be
                                            // created by swiping
                                            readOrConsensusTypeUserWants,
                                            true, // bAllowUserToEnterComment
                                            soComment,
                                            soContigName );
                                            
      delete pGuiGetTagType;

      if ( bUserPushedCancel ) {
         // clear selection and unhighlight the region
         ConsEd::pGetConsEd()->pGetSelection()->unsetSelection();
         drawConsensus();
         return;
      }

      if ( readOrConsensusTypeUserWants == tagType::consensus ) {

         tag* pTag = 
            tagTypes::pGetTagTypes()->pCreateConsensusTag( 
                                                          soTagType,
                                                          soComment,
                                                          nConsensusPosLeft,
                                                          nConsensusPosRight,
                                                          pContig,
                                                          false, // no NoTrans
                                                          bUserPushedCancel,
                                                          soContigName );
      
         if ( bUserPushedCancel ) {
            ConsEd::pGetConsEd()->pGetSelection()->unsetSelection();
            drawConsensus();
            return;
         }

         ConsEd::pGetConsEd()->pGetSelection()->unsetSelection();

         EditAction* pEditAction = new editAddConsensusTag( pTag );

         ConsEd::pGetConsEd()->doEditAction( pEditAction, true );
      } //  if ( readOrConsensusTypeUserWants == readType::consensus ) {
      else {
         // tag all reads

         ConsEd::pGetConsEd()->pGetSelection()->unsetSelection();

         EditAction* pEditAction = 
            new editAddTagsToAllReadsAtConsPos( pContig,
                                                nConsensusPosLeft,
                                                nConsensusPosRight,
                                                soTagType,
                                                soComment );

         ConsEd::pGetConsEd()->doEditAction( pEditAction, true );

      } //  if ( readOrConsensusTypeUserWants == readType::consensus ) { else
   }
}

      


// what happens when scrolling timer expires
void ContigWin :: automaticScrollingTimer() {
   automaticallyScroll();

   int nExtendToConsPos;
   if ( pGcw_->bAutomaticallyScrollLeftNotRight_ )
      nExtendToConsPos = nLeftEdgeConsensusPosition_;
   else
      nExtendToConsPos = nGetRightEdgeConsensusPosition();

   continueHighlight3( nExtendToConsPos );
}

   
void ContigWin :: automaticallyScroll() {

   // where is the mouse?  Can I interrogate it to make sure it is still
   // off the window?  It must be, or else I would have received a motion 
   // event.  The button must still be pressed down, or else I would
   // have received a button release event.

   int nNewConsPos = nLeftEdgeConsensusPosition_ +
      ( pGcw_->bAutomaticallyScrollLeftNotRight_ ?
        -pCP->nWhenUserScrollsOffWindowBasesToScrollEachTime_ :
        pCP->nWhenUserScrollsOffWindowBasesToScrollEachTime_ );

   scrollToConsensusPos( nNewConsPos );
}

   
   
   

void ContigWin :: makeSelectionFromConsensus( const int nConsPosLeft,
                                              const int nConsPosRight ) {

   RWCString soSelectedBases;
   for( int nConsPos = nConsPosLeft; nConsPos <= nConsPosRight; ++nConsPos ) {
      Ntide nt = pContig_->ntGetCons( nConsPos );
      if ( !nt.bIsPad() ) {
         RWCString soBase( nt.cGetBase() );
         soSelectedBases += soBase;
      }
   }
      
   GuiApp::pGetGuiApp()->makeSelectionForPasting( soSelectedBases );
}



void ContigWin :: makeSelectionFromARead( LocatedFragment* pLocFrag,
                                          const int nConsPosLeft,
                                          const int nConsPosRight ) {

   RWCString soSelectedBases;
   for( int nConsPos = nConsPosLeft; nConsPos <= nConsPosRight; ++nConsPos ) {

      Ntide nt = pLocFrag->ntGetFragFromConsPos( nConsPos );
      if ( !nt.bIsPad() ) {
         RWCString soBase( nt.cGetBase() );
         soSelectedBases += soBase;
      }
   }

   GuiApp::pGetGuiApp()->makeSelectionForPasting( soSelectedBases );
}



void ContigWin :: toggleHighlightOfReadName( const int nPixelX, 
                                             const int nPixelY ) {

   // find which read has been clicked

   int nReadLine = nFragLineFromPixelY( nPixelY );

   LocatedFragment* pLocFrag = 
     pContigView_->pLocatedFragmentGet( nReadLine ); 
   
   // 1-based position starting at left column
   int nScreenCharX = 
      (nPixelX + GuiApp::nGetFontWidth() - 1) / GuiApp::nGetFontWidth();

   RWCString soReadName = pLocFrag->soGetFragmentName();


   // cursor wasn't on the name after all
   if ( soReadName.length() < nScreenCharX ) return;
   
   pLocFrag->bReadCurrentlyHighlighted_ = 
      ! pLocFrag->bReadCurrentlyHighlighted_;

   drawFragmentNames();


   GuiApp::pGetGuiApp()->makeSelectionForPasting( soReadName );
}



void ContigWin :: find4BestReads( const int nConsPos, 
                                  LocatedFragment*& pLocFrag1,
                                  LocatedFragment*& pLocFrag2,
                                  LocatedFragment*& pLocFrag3,
                                  LocatedFragment*& pLocFrag4 ) {

   
   pLocFrag1 = 0;
   pLocFrag2 = 0;
   pLocFrag3 = 0;
   pLocFrag4 = 0;

   int nBestQual1 = -1000;
   int nBestQual2 = -1000;
   int nBestQual3 = -1000;
   int nBestQual4 = -1000;


   for(int nRead = 0;
       nRead < pContigView_->nGetNumberOfFragments();
       ++nRead ) {

      LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet( nRead );

      if ( pLocFrag->bIsInRead( nConsPos ) 
           && !pLocFrag->bIsClipped( nConsPos )) {

         Ntide nt =  pLocFrag->ntGetFragFromConsPos( nConsPos );
         int nQual = nt.qualGetQuality();

      
         if ( ! pLocFrag->bComp() ) {
            if ( tolower( nt.cGetBase() )
                 == tolower( pContig_->ntGetCons( nConsPos ).cGetBase() ) ) {
            
               // top strand, agrees with consensus

               if (nQual > nBestQual1 ) {
                  nBestQual1 = nQual;
                  pLocFrag1 = pLocFrag;
               }
            }
            else {
            
               // top strand, disagrees with consensus

               if (nQual > nBestQual2 ) {
                  nBestQual2 = nQual;
                  pLocFrag2 = pLocFrag;
               }
            }
         }
         else {
         
            if ( tolower( nt.cGetBase() )
                 == tolower( pContig_->ntGetCons( nConsPos ).cGetBase() ) ) {

               // bottom strand, agrees with consensus

               if (nQual > nBestQual3 ) {
                  nBestQual3 = nQual;
                  pLocFrag3 = pLocFrag;
               }
            }
            else {
            
               // bottom strand, disagrees with consensus

               if (nQual > nBestQual4 ) {
                  nBestQual4 = nQual;
                  pLocFrag4 = pLocFrag;
               }
            }
         }
      }
   }
}         


void ContigWin :: showBaseQuality(const int nScreenCharXPos,
                                       const int nReadIndex,
                                  const bool bReadBaseNotContigBase ) {
   // convert screen coordinate to consensus position
   int nConsPos = nScreenCharToConsensusPosition(nScreenCharXPos);

   Ntide nt;
   LocatedFragment* pLocFrag = NULL;
   if ( bReadBaseNotContigBase ) {

      // get the located fragment pointer for that screen line
      if (nReadIndex >= pContigView_->nGetNumberOfFragments()) {
         return;  // do nothing
      }

      pLocFrag = 
         pContigView_->pLocatedFragmentGet( nReadIndex );
   
      // does the frag have a base at that location ?
      if ((nConsPos < pLocFrag->nGetAlignStart()) ||
          (nConsPos > pLocFrag->nGetAlignEnd())) {
         return;
      }

      nt = pLocFrag->ntGetFragFromConsPos( nConsPos );
   }
   else {
      if ( ( nConsPos < pContig_->nGetStartConsensusIndex() ) ||
           ( pContig_->nGetEndConsensusIndex() < nConsPos ) )
         return;
      nt = pContig_->ntGetCons( nConsPos );
   }

   int nUnpadded = pContig_->nUnpaddedIndexStartingAtUserDefinedPosition( nConsPos );

   int nQuality = nt.qualGetQuality();
   
   RWCString soLine = "qual: " + 
      RWCString( (long) nQuality );
   
   if ( pContig_->bSetStartNumberingUnpaddedConsensusAtUserDefined_ &&
        ( pContig_->nStartNumberingUnpaddedConsensusAtUserDefined_ != 1 ) )
      soLine += ", cons pos (user-defined): ";
   else
      soLine += ", cons pos: ";

   soLine += (
              soAddCommas( nUnpadded ) +
              ", diff from last: " +
              soAddCommas( ABS( nUnpadded - nLastUsedUnpaddedConsPos ) )
              );
   
   // display unpadded read position in direction of sequencing

   if ( bReadBaseNotContigBase ) {
      soLine += ", read pos: ";
      soLine += RWCString( (long) 
             pLocFrag->nOrientedUnpaddedFragPosFromConsPos( nConsPos ) 
                           );
   }

   soLine += ", ";
   soLine += pContigView_->soGetHowReadsAreSorted();


   pGcw_->displayMessageAtBottom( (char*) soLine.data() );

   nLastUsedUnpaddedConsPos = nUnpadded;
}



void ContigWin :: scrollLeft() {

   int nNewLeftEdgeConsPos = nLeftEdgeConsensusPosition_ -
      nGetDisplayedBasesWide()/2;

   scrollToConsensusPos( nNewLeftEdgeConsPos );
}

   

void ContigWin :: scrollLeftLeft() {

   int nNewLeftEdgeConsPos = nLeftEdgeConsensusPosition_ -
      nGetDisplayedBasesWide() + 
      nNumberOfBasesToOverlapWhenPaging;

   scrollToConsensusPos( nNewLeftEdgeConsPos );

}



void ContigWin :: scrollRight() {
   
   int nNewLeftEdgeConsPos = nLeftEdgeConsensusPosition_ +
      nGetDisplayedBasesWide()/2;

   scrollToConsensusPos( nNewLeftEdgeConsPos );
}


void ContigWin :: scrollRightRight() {
   
   int nNewLeftEdgeConsPos = nLeftEdgeConsensusPosition_ +
      nGetDisplayedBasesWide() - 
      nNumberOfBasesToOverlapWhenPaging;

   scrollToConsensusPos( nNewLeftEdgeConsPos );
}

   
void ContigWin :: userTypedInConsPos( const RWCString& soConsPos ) {


   // remove any embedded commas
   RWCString soConsPosTemp = soConsPos;
   soConsPosTemp.removeAllCommas();

   bool bPaddedNotUnpaddedPos = false;
   if ( soConsPosTemp.bStartsWithAndRemove( "p" ) ||
        soConsPosTemp.bStartsWithAndRemove( "P" ) ||
        soConsPosTemp.bStartsWithAndRemove( "*" ) ) {
      bPaddedNotUnpaddedPos = true;
   }


   int nUserTypedUnpaddedConsPos;
   if (!bIsNumericMaybeWithWhitespace( soConsPosTemp, nUserTypedUnpaddedConsPos ) ) {
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                          "consensus position must be numeric, not %s",
                          soConsPosTemp.data() );
      return;
   }


   int nConsPosPadded;
   if ( !bPaddedNotUnpaddedPos ) {

      int nConsPosMin = pGetContig()->nGetFirstDisplayableContigPos();
      int nConsPosMax = pGetContig()->nGetLastDisplayableContigPos();

      int nConsPosMinUnpaddedUserDefined = 
         pContig_->nUnpaddedIndexStartingAtUserDefinedPosition( nConsPosMin );
      int nConsPosMaxUnpaddedUserDefined = 
         pContig_->nUnpaddedIndexStartingAtUserDefinedPosition( nConsPosMax );


      if (!  (nConsPosMinUnpaddedUserDefined <= nUserTypedUnpaddedConsPos &&
              nUserTypedUnpaddedConsPos <= nConsPosMaxUnpaddedUserDefined ) ) {

         RWCString soError = "consensus position must be between ";
         soError += RWCString( (long) nConsPosMinUnpaddedUserDefined );
         soError += " and ";
         soError += RWCString( (long) nConsPosMaxUnpaddedUserDefined );

         if ( pContig_->nStartNumberingUnpaddedConsensusAtUserDefined_ != 1 ) {
            soError += " (note that positions on this contig start at ";
            soError += RWCString( (long) pContig_->nStartNumberingUnpaddedConsensusAtUserDefined_ );
            soError += " according to a startNumberingConsensus tag";
         }

         popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                             soError );
         return;
      }

      int nUnpadded;
      if ( pCP->bNumberUnpaddedConsensusAtUserDefined_ ) 
         nUnpadded = nUserTypedUnpaddedConsPos - 
            pContig_->nStartNumberingUnpaddedConsensusAtUserDefined_ + 1;
      else
         nUnpadded = nUserTypedUnpaddedConsPos;

      nConsPosPadded = pContig_->nPaddedIndex( nUnpadded );
   } //    if ( !bPaddedNotUnpaddedPos ) {
   else {
      nConsPosPadded = nUserTypedUnpaddedConsPos;
   }

   scrollToConsensusPosInCenter( nConsPosPadded );
   moveCursorToConsPos( nConsPosPadded );
}



void ContigWin :: pickPrimer( const int nScreenCharX,
                              const bool bForwardNotReversePrimer,
                              const bool bCloneNotSubcloneTemplate) {

   int nConsPos = nScreenCharToConsensusPosition( nScreenCharX );

   pickPrimers( pContig_, 
                nConsPos, 
                bForwardNotReversePrimer, /* forward primer */
                bCloneNotSubcloneTemplate,
                this );

}




bool ContigWin :: bGetEntireVisibleConsensusBases( 
                           RWCString& soBasesToDraw, 
                           int& nActualScreenCharStart ) 
{
   
   int nLeftIntersectScreenChar;
   int nRightIntersectScreenChar;

   bool bThereIsAnotherColorWord = 
      bIntersect(
                 0, // left-most screen char
                 nGetDisplayedBasesWide() - 1, // right-most screen char
                 nConsensusPositionToScreenChar( pContig_->nGetStartConsensusIndex() ),
                 nConsensusPositionToScreenChar( pContig_->nGetEndConsensusIndex() ),
                 nLeftIntersectScreenChar,
                 nRightIntersectScreenChar );

   if (!bThereIsAnotherColorWord ) return( false );

   nActualScreenCharStart = nLeftIntersectScreenChar;

   int nConsPosStart = nScreenCharToConsensusPosition( nActualScreenCharStart );
   int nConsPosEnd = nScreenCharToConsensusPosition( nRightIntersectScreenChar );
   
   soBasesToDraw = pContig_->soGetPartOfConsensusUpperOrLower( nConsPosStart,
                                                               nConsPosEnd );

   return true;
}

   
void ContigWin :: drawHighlightedBases() {


   selection* pSelection = ConsEd::pGetConsEd()->pGetSelection();


   if ( !pSelection->bSelectionActive_ ) return;

   Contig* pContig;
   LocatedFragment* pLocFrag;
   int nConsPosSelectionStart;
   int nConsPosSelectionEnd;
   bool bSelectionActive;
   
   ConsEd::pGetConsEd()->pGetSelection()->getSelection( 
                            nConsPosSelectionStart,
                            nConsPosSelectionEnd,
                            pLocFrag,
                            pContig,
                            bSelectionActive );

   int nConsPosVisibleHighlightedMin;
   int nConsPosVisibleHighlightedMax;
   
   if ( !bIntersect(
                 nLeftEdgeConsensusPosition_,
                 nGetRightEdgeConsensusPosition(),
                 nConsPosSelectionStart,
                 nConsPosSelectionEnd,
                 nConsPosVisibleHighlightedMin,
                 nConsPosVisibleHighlightedMax ) ) return;
   



   if ( pSelection->bIsOnConsensusNotRead() ) {

      // We now have found a range of consensus positions that are
      // visible on the screen and are highlighted.  Get the sequence.

      RWCString soHighlightedBases = 
         pContig_->soGetPartOfConsensusUpperOrLower( 
                                        nConsPosVisibleHighlightedMin,
                                        nConsPosVisibleHighlightedMax );


      int nScreenCharStart = 
         nConsensusPositionToScreenChar( nConsPosVisibleHighlightedMin );


      pGcw_->guiDrawConsensusColorWord( nScreenCharStart,
                                        (char*) soHighlightedBases.data(),
                                        soHighlightedBases.length(),
                                        pGcw_->pGuiColorTextForHighlightedBases_ 
                                        );
                                        
   } //  if ( pSelection->bIsOnConsensusNotRead() ) {
   else {
      
      int nConsPosStart;
      int nConsPosEnd;
      if ( !bIntersect( nConsPosVisibleHighlightedMin,
                        nConsPosVisibleHighlightedMax,
                        pLocFrag->nGetAlignStart(),
                        pLocFrag->nGetAlignEnd(),
                        nConsPosStart,
                        nConsPosEnd ) ) return;


      RWCString soHighlightedBases =
         pLocFrag->soGetPartOfReadUpperOrLower( nConsPosStart, nConsPosEnd );
      
      int nScreenCharStart =
         nConsensusPositionToScreenChar( nConsPosStart );

      
      int nReadLine = pContigView_->nWhatLineIsReadOn( pLocFrag );
      
      // should never happen
      if ( nReadLine == RW_NPOS ) return;


      pGcw_->guiDrawReadColorWord( nScreenCharStart,
                                   nReadLine,
                                    soHighlightedBases.data(),
                                    soHighlightedBases.length(),
                                    pGcw_->pGuiColorTextForHighlightedBases_ );
   }
}





bool ContigWin :: bIsThisTraceDisplayed( const LocatedFragment* pLocFrag ) {

   // first check if no reads at all are displayed
   if (! pTeditor_ )
      return( false );

   if ( pTeditor_->pGetTedWinByLocatedFragment( pLocFrag ) )
      return( true );
   else 
      return( false );
}

   


int ContigWin :: nGetReadLineFromLocatedFragment( const LocatedFragment* pLocFrag ) {

   
   for(int nReadLine = 0;
       nReadLine < pContigView_->nGetNumberOfFragments();
       ++nReadLine) {

      if ( pLocFrag == 
         pContigView_->pLocatedFragmentGet( nReadLine ) )
         return( nReadLine );
   }

   // indicator that LocatedFragment wasn't found
   return( -1 );
}


void ContigWin :: drawSequencingDirectionArrow( 
                           const LocatedFragment* pLocFrag ) {

   int nReadLine = nGetReadLineFromLocatedFragment( pLocFrag );

   if (nReadLine < nIndexOfTopVisibleRead_ ) return;

   bool bThisReadsTraceIsUp = bIsThisTraceDisplayed( pLocFrag );

   int nError = 
      pGcw_->nGuiDrawSequencingDirectionArrow( nReadLine,
                                               pLocFrag->bComp(),
                                               bThisReadsTraceIsUp
                                               );
}

   
   




   
void ContigWin :: getContigErrorRate( char* szErrors ) {

   // handle case in which the contigwin doesn't have a contig in it
   if (! pContig_ ) {
      szErrors[0] = '\0';
      return;
   }

   double dErrorsPerMegabase = 
      pContig_->dGetExpectedNumberOfErrorsPerMegabaseForConsensus();

   double dErrorsPer10Kb = dErrorsPerMegabase / 100.0;

   if ( dErrorsPer10Kb > 999) {
      sprintf( szErrors, "%6.0f", dErrorsPer10Kb );
   }
   else if (dErrorsPer10Kb < 0 ) {
      strcpy( szErrors, "???" );
   }
   else {
      sprintf( szErrors, "%6.2f", dErrorsPer10Kb );
   }
}



void ContigWin :: displayContigErrorRate() {

   char szErrors[100];
   
   getContigErrorRate( szErrors );

   pGcw_->displayErrorRate( szErrors );

}



void ContigWin :: tellPhrapNotToOverlapReadsDiscrepantAtThisLocation( const int nCharX ) {

   int nConsPosWhereToSplit = nScreenCharToConsensusPosition( nCharX );

   pContigView_->tellPhrapNotToOverlapReadsDiscrepantAtThisLocation( 
                                nConsPosWhereToSplit,
                                true,  // using Gui
                                this );

}

void ContigWin :: makeAllReadsNAtThisLocation( const int nCharX ) {

   int nConsPos = nScreenCharToConsensusPosition( nCharX );

   for( int nRead = 0; nRead < pContigView_->nGetNumberOfFragments();
        ++nRead ) {
      
      LocatedFragment* pLocFrag =
         pContigView_->pLocatedFragmentGet( nRead );

      // reads that aren't aligned against the consensus at this point
      // are not considered by phrap in making joins so don't bother
      // with their quality

      if ( !pLocFrag->bIsInAlignedPartOfRead( nConsPos ) ) continue;

      EditAction* pEditAction = new editChangeToNs( pLocFrag,
                                                    nConsPos,
                                                    nConsPos );

      ConsEd::pGetConsEd()->doEditAction( pEditAction, true );
      
   }
}



void ContigWin :: tearContigs( const int nScreenCharX ) {

   int nConsPosWhereToTear = nScreenCharToConsensusPosition( nScreenCharX );

   // make consed read-only right now
   
   // put up dialog box for user to select reads


   new guiTearContig( 
                this,
                pContig_,
                pContigView_,
                nConsPosWhereToTear );

   // now it is the job of the guiAskUserToSelectWhichReadsShouldGoInWhichContig
   // to actually make the tear
}




void ContigWin :: showAllTraces( const int nScreenCharX ) { 

   int nConsPos = nScreenCharToConsensusPosition( nScreenCharX );

   showAllTracesByConsensusPosition( nConsPos );
}


void ContigWin :: showAllTracesByConsensusPosition( const int nConsPosOfCursor ) { 

   if ( pTeditor_ ) {
      if ( !pTeditor_->pGuiTeditor_->bShowTracesInScrollingWindow_ )
         delete pTeditor_;
      else
         pTeditor_->deleteAllTedWins();
   }

   if ( pContig_->bIsOnConsensus( nConsPosOfCursor ) ) {
      moveCursorToConsPos( nConsPosOfCursor );
   }

   const int nNumberOfTracesToAdd = 3;
   if ( !pTeditor_ ) {
      pTeditor_ = new Teditor( this, 
                               nNumberOfTracesToAdd,
                               true ); // in scrolling window

      pTeditor_->pGuiTeditor_->createScrollingWindowForTraces();
   }


   pTeditor_->pContigView_ = new ContigView( *pContigView_ );


   pTeditor_->showAllTraces( nConsPosOfCursor );

}




void ContigWin :: navigateToNextOrPreviousItem( const bool bNextNotPrevious ) {
   if ( pGuiMultiContigNav_ )
      pGuiMultiContigNav_->gotoNextOrPreviousItem( bNextNotPrevious );
   else if ( ConsEd::pGetConsEd()->aGuiMultiContigNavigator_.length() > 0  )
      ConsEd::pGetConsEd()->aGuiMultiContigNavigator_.last()->gotoNextOrPreviousItem( bNextNotPrevious );
   else
      popupErrorMessage( "There is no active navigator window for this contig" );

}


void ContigWin :: raiseWindow() {

   XRaiseWindow( 
                XtDisplay( pGcw_->widGetGuiContigWinTopLevel() ),
                XtWindow( pGcw_->widGetGuiContigWinTopLevel() )
                );
}
   



void ContigWin :: scrollToLeftOrRightEndOfConsensus( const bool bLeftNotRight ) {

   int nConsPos;
   if ( bLeftNotRight )
      nConsPos = pContig_->nGetStartIndex();
   else
      nConsPos = pContig_->nGetEndIndex();

   
   scrollToConsensusPosInCenter( nConsPos );
}


void ContigWin :: scrollToCursor() {

   if (! editCursor_.bCursorValid()) { 
      popupErrorMessage( "the cursor is not set" );
      return;
   }

   scrollToConsensusPosInCenter( editCursor_.nConsPosGet() );
}



void ContigWin :: userPushedControlA() {
   if ( ! editCursor_.bCursorValid() ) {
      popupErrorMessage( "please set the cursor first so I know whether you want to go to the beginning of the consensus or a read and which read" );
      return;
   }

   if (editCursor_.bCursorOnFrag()) {
      LocatedFragment* pLocFrag = editCursor_.pLocFragGet();

//       // to deliberately force an exception
//       int nPeakPos = pLocFrag->nGetPointPos( -1667 );
//       cerr << nPeakPos << endl;
      
      int nConsPos = pLocFrag->nGetAlignStart();
      scrollToConsensusPosInCenter( nConsPos );
      moveCursorToFragPos( nConsPos, pLocFrag );
   }
   else if ( editCursor_.bCursorOnCons() ) {
      scrollToLeftOrRightEndOfConsensus( true ); // left
   }
}


void ContigWin :: userPushedControlE() {
   if ( ! editCursor_.bCursorValid() ) {
      popupErrorMessage( "please set the cursor first so I know whether you want to go to the end of the consensus or the end of a read and which read" );
      return;
   }

   if ( editCursor_.bCursorOnFrag() ) {
      LocatedFragment* pLocFrag = editCursor_.pLocFragGet();
      int nConsPos = pLocFrag->nGetAlignEnd();
      scrollToConsensusPosInCenter( nConsPos );
      moveCursorToFragPos( nConsPos, pLocFrag );
   }
   else if ( editCursor_.bCursorOnCons() ) {
      scrollToLeftOrRightEndOfConsensus( false ); // right
   }
   
}


const int nHowCloseToEdgeCanCursorGet = 10;



void ContigWin :: userPushedLeftArrow() {
   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2( 
            pGcw_->widGetGuiContigWinTopLevel(),
            "please set the cursor first so I know where to move from" );
      return;
   }

   if ( editCursor_.nConsPosGet() < nLeftEdgeConsensusPosition_ ||
        nGetRightEdgeConsensusPosition() < editCursor_.nConsPosGet() ) {
      GuiApp::beep();
      return;
   }

   int nNewConsPos = editCursor_.nConsPosGet() - 1;

   if ( editCursor_.bCursorOnFrag() ) {
      LocatedFragment* pLocFrag = editCursor_.pLocFragGet();
      if ( nNewConsPos < pLocFrag->nGetAlignStart() ) {
         GuiApp::beep();
         return;
      }

      moveCursorToFragPos( nNewConsPos, pLocFrag );
   }
   else {
      if ( nNewConsPos < pContig_->nGetStartConsensusIndex() ) {
         GuiApp::beep();
         return;
      }


      moveCursorToConsPos( nNewConsPos );
   }

   // now scroll the window if necessary

   if ( editCursor_.nConsPosGet() < nLeftEdgeConsensusPosition_ + 
        nHowCloseToEdgeCanCursorGet ) {
      // scroll.

      scrollToConsensusPos( nLeftEdgeConsensusPosition_ - 1 );

   }
      
      


}



void ContigWin :: userPushedLeftArrowAboveComma() {

   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2(
          pGcw_->widGetGuiContigWinTopLevel(),
          "please set the cursor first so I know where to move from" );

      return;
   }

   if ( editCursor_.nConsPosGet() < nLeftEdgeConsensusPosition_ ||
        nGetRightEdgeConsensusPosition() < editCursor_.nConsPosGet() ) {

      GuiApp::beep();
      return;
   }

   int nNewConsPos = editCursor_.nConsPosGet() - 
      pCP->nAmountToMoveWithBigLeftAndRightArrows_;

   if (editCursor_.bCursorOnFrag() ) {
      LocatedFragment* pLocFrag = editCursor_.pLocFragGet();
      if ( nNewConsPos < pLocFrag->nGetAlignStart() ) {
         GuiApp::beep();
         return;
      }

      moveCursorToFragPos( nNewConsPos, pLocFrag );
   }
   else {
      if ( nNewConsPos < pContig_->nGetStartConsensusIndex() ) {
         GuiApp::beep();
         return;
      }

      moveCursorToConsPos( nNewConsPos );
   }

   // now scroll the window if necessary

   if ( editCursor_.nConsPosGet() < nLeftEdgeConsensusPosition_ +
        nHowCloseToEdgeCanCursorGet ) {
      
      // scroll

      scrollToConsensusPos( nLeftEdgeConsensusPosition_ -
               pCP->nAmountToMoveWithBigLeftAndRightArrows_ );                 

   }

}

         
      






void ContigWin :: userPushedRightArrow() {

   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2(
           pGcw_->widGetGuiContigWinTopLevel(),
           "please set the cursor first so I know where to move from" );
      return;
   }

   if ( editCursor_.nConsPosGet() < nLeftEdgeConsensusPosition_ ||
        nGetRightEdgeConsensusPosition() < editCursor_.nConsPosGet() ) {
      GuiApp::beep();
      return;
   }

   int nNewConsPos = editCursor_.nConsPosGet() + 1;
   
   if ( editCursor_.bCursorOnFrag() ) {
      LocatedFragment* pLocFrag = editCursor_.pLocFragGet();
      if ( pLocFrag->nGetAlignEnd() < nNewConsPos ) {
         GuiApp::beep();
         return;
      }

      moveCursorToFragPos( nNewConsPos, pLocFrag );
      
   }
   else {
      if ( pContig_->nGetEndConsensusIndex() < nNewConsPos ) {
         GuiApp::beep();
         return;
      }

      moveCursorToConsPos( nNewConsPos );
   }

   // now scroll the window if necessary

   if ( nGetRightEdgeConsensusPosition() - nHowCloseToEdgeCanCursorGet 
        < editCursor_.nConsPosGet() ) {

      scrollToConsensusPos( nLeftEdgeConsensusPosition_ + 1 );
   }


}



void ContigWin :: userPushedRightArrowAbovePeriod() {

   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2(
         pGcw_->widGetGuiContigWinTopLevel(),
         "please set the cursor first so I know where to move from" );
      return;
   }

   if ( editCursor_.nConsPosGet() < nLeftEdgeConsensusPosition_ ||
        nGetRightEdgeConsensusPosition() < editCursor_.nConsPosGet() ) {
      GuiApp::beep();
      return;
   }

   int nNewConsPos = editCursor_.nConsPosGet() +
      pCP->nAmountToMoveWithBigLeftAndRightArrows_;

   if ( editCursor_.bCursorOnFrag() ) {
      LocatedFragment* pLocFrag = editCursor_.pLocFragGet();
      if ( pLocFrag->nGetAlignEnd() < nNewConsPos ) {
         GuiApp::beep();
         return;
      }

      moveCursorToFragPos( nNewConsPos, pLocFrag );
   }
   else {
      if ( pContig_->nGetEndConsensusIndex() < nNewConsPos ) {
         GuiApp::beep();
         return;
      }

      moveCursorToConsPos( nNewConsPos );
   }

   // now scroll the window if necessary

   if ( nGetRightEdgeConsensusPosition() - nHowCloseToEdgeCanCursorGet
        < editCursor_.nConsPosGet() ) {
      
      scrollToConsensusPos( nLeftEdgeConsensusPosition_ + 
                            pCP->nAmountToMoveWithBigLeftAndRightArrows_ );
   }
}



   




void ContigWin :: userPushedDownArrow() {

   
   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2(
                  pGcw_->widGetGuiContigWinTopLevel(),
                  "please set the cursor first so I know where to move from" );

      return;
   }

   // check that the cursor is in the window we are currently seeing

   if ( editCursor_.nConsPosGet() < nLeftEdgeConsensusPosition_ ||
        nGetRightEdgeConsensusPosition() < editCursor_.nConsPosGet() ) {

      popupErrorMessage2(
                  pGcw_->widGetGuiContigWinTopLevel(),
                  "please set the cursor first in this window so I know where to move from" );
      
      return;
   }

   int nNewReadLine = -666;
   if ( editCursor_.bCursorOnFrag() ) {
      LocatedFragment* pPreviousRead = editCursor_.pLocFragGet();
      // where is this read in the list of reads displayed?


      int nPreviousReadLine;
      bool bFoundReadLine = false;
      int nReadLine;
      for( nReadLine = 0; 
           nReadLine < pContigView_->nGetNumberOfFragments() 
              && !bFoundReadLine;
           ++nReadLine ) {
         if ( pContigView_->pLocatedFragmentGet( nReadLine ) == 
              pPreviousRead ) {
            nPreviousReadLine = nReadLine;
            bFoundReadLine = true;
         }
      }

      assert( bFoundReadLine );

      nNewReadLine = MIN( nPreviousReadLine + 1,
                          pContigView_->nGetNumberOfFragments() - 1 );
   }
   else {
      nNewReadLine = 0;
   }

   // let's see if this read (or any subsequent read) has bases at 
   // this position

   LocatedFragment* pNewAcceptableRead = NULL;
   bool bFoundAcceptableRead = false;
   for( int nReadLine = nNewReadLine; 
        nReadLine < pContigView_->nGetNumberOfFragments() &&
           !bFoundAcceptableRead; ++nReadLine ) {
   
      LocatedFragment* pNextRead = 
         pContigView_->pLocatedFragmentGet( nReadLine );
      
      if ( pNextRead->nGetAlignStart() <=
           editCursor_.nConsPosGet() &&
           editCursor_.nConsPosGet() <= pNextRead->nGetAlignEnd() ) {
         
         bFoundAcceptableRead = true;
         pNewAcceptableRead = pNextRead;
      }
   }
   
   if ( !bFoundAcceptableRead ) {
      return;
   }
      
   // move cursor to this read
   
   moveCursorToFragPos( editCursor_.nConsPosGet(),
                        pNewAcceptableRead );

}




void ContigWin :: userPushedUpArrow() {
   
   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2(
                  pGcw_->widGetGuiContigWinTopLevel(),
                  "please set the cursor first so I know where to move from" );

      return;
   }

   // check that the cursor is in the window we are currently seeing

   if ( editCursor_.nConsPosGet() < nLeftEdgeConsensusPosition_ ||
        nGetRightEdgeConsensusPosition() < editCursor_.nConsPosGet() ) {

      popupErrorMessage2(
                  pGcw_->widGetGuiContigWinTopLevel(),
                  "please set the cursor first in this window so I know where to move from" );
      
      return;
   }

   if ( !editCursor_.bCursorOnFrag() ) 
      return;

   LocatedFragment* pPreviousRead = editCursor_.pLocFragGet();

   // where is this read in the list of reads displayed?

   int nPreviousReadLine;
   bool bFoundReadLine = false;
   int nReadLine;
   for( nReadLine = 0;
        nReadLine < pContigView_->nGetNumberOfFragments() && !bFoundReadLine;
        ++nReadLine ) {
      if ( pContigView_->pLocatedFragmentGet( nReadLine ) == pPreviousRead ) {
         nPreviousReadLine = nReadLine;
         bFoundReadLine = true;
      }
   }

   assert( bFoundReadLine );

   // see if we can move up to find a read line that has bases
   // at the same consensus position as the cursor

   LocatedFragment* pNewAcceptableRead = NULL;
   bool bFoundAcceptableRead = false;
   for( nReadLine = nPreviousReadLine - 1;
        nReadLine >= 0 && !bFoundAcceptableRead; --nReadLine ) {
      
      LocatedFragment* pNextRead = 
         pContigView_->pLocatedFragmentGet( nReadLine );
      
      if ( pNextRead->nGetAlignStart() <=
           editCursor_.nConsPosGet() &&
           editCursor_.nConsPosGet() <= pNextRead->nGetAlignEnd() ) {
         
         bFoundAcceptableRead = true;
         pNewAcceptableRead = pNextRead;
      }
   }

   if ( bFoundAcceptableRead ) {
      moveCursorToFragPos( editCursor_.nConsPosGet(),
                           pNewAcceptableRead );
   }
   else {
      // let's see if we can move to the consensus

      if ( pContig_->nGetStartIndex() <= editCursor_.nConsPosGet() &&
           editCursor_.nConsPosGet() <= pContig_->nGetEndIndex() ) {

         moveCursorToConsPos( editCursor_.nConsPosGet() );
      }
      // otherwise do nothing
   }
}





static RWTPtrOrderedVector<tag> aTagsAtThisLocation;
static RWCString soToDisplay( (size_t) 200 );


void ContigWin :: pointerMoved( const int nPixelX, const int nPixelY ) {

   int nScreenCharX = pGcw_->nCharXFromPixel( nPixelX );

   int nConsPos = nScreenCharToConsensusPosition( nScreenCharX );

   int nReadLine;
   if ( pGcw_->bIsInFragmentBasesArea( nPixelX, nPixelY ) )
      nReadLine = nFragLineFromPixelY( nPixelY );
   else if ( pGcw_->bIsInConsensusBasesArea( nPixelX, nPixelY ) )
      nReadLine = -2;
   else
      nReadLine = -3;  // means not in either fragment bases area or
                           // consensus bases area


   bool bSameScreenChar = (
                           nConsPos == nOldConsPosOfPointer_ &&
                           nReadLine == nOldReadLineOfPointer_ ) ?
      true : false;

   nOldConsPosOfPointer_ = nConsPos;
   nOldReadLineOfPointer_ = nReadLine;


   if ( bSameScreenChar ) return;
   if ( nReadLine == -3 ) return;  // no tag here


   aTagsAtThisLocation.clear();
   
   if ( pGcw_->bIsInFragmentBasesArea( nPixelX, nPixelY ) ) {
      
      LocatedFragment* pLocFrag = 
         pContigView_->pLocatedFragmentGet( nReadLine ); 

      for( int n = 0; n < pLocFrag->nGetNumberOfTags(); ++n ) {
         tag* pTag = pLocFrag->pGetTag( n );

         if (pTag->bTouchesThisConsPos( nConsPos ) ) {
            aTagsAtThisLocation.append( pTag );
         }
      }
   }
   else if ( pGcw_->bIsInConsensusBasesArea( nPixelX, nPixelY ) ) {
      nOldReadLineOfPointer_ = -2;
      
      for( int nTag = 0; nTag < pContig_->nGetNumberOfTags(); ++nTag ) {
         tag* pTag = pContig_->pGetTag( nTag );
         
         if ( pTag->bTouchesThisConsPos( nConsPos ) )
            aTagsAtThisLocation.append( pTag );
      }
   }

   
   if ( aTagsAtThisLocation.length() ) {
      soToDisplay = "tag types: ";
      int nTag;
      for( nTag = 0; nTag < aTagsAtThisLocation.length(); ++nTag ) {
         tag* pTag = aTagsAtThisLocation[ nTag ];
         soToDisplay += pTag->soType_;
         soToDisplay += " ";
      }
      
      soToDisplay += "tag data:";

      for( nTag = 0; nTag < aTagsAtThisLocation.length(); ++nTag ) {
         tag* pTag = aTagsAtThisLocation[ nTag ];

         if ( !pTag->soComment_.isNull() ) {
            soToDisplay += " ";
            soToDisplay += pTag->soComment_;
         }
         if ( !pTag->soMiscData_.isNull() ) {
            soToDisplay += " ";
            soToDisplay += pTag->soMiscData_;
         }
         if ( pTag->soType_ == "oligo" ) {
            oligoTag* pOligoTag = (oligoTag*) pTag;
            soToDisplay += " ";
            soToDisplay += pOligoTag->soName_;
         }
      }
      pGcw_->displayMessageAtBottom( soToDisplay.data() );
   }
   else {

      bool bLeave = ( ( pGcw_->soDisplayedMessageAtBottom_.length() > 6 )
         &&
                      ( pGcw_->soDisplayedMessageAtBottom_( 0, 6) == "qual: " ) ) ||
         ( ( pGcw_->soDisplayedMessageAtBottom_.length() > 12 ) &&
           ( pGcw_->soDisplayedMessageAtBottom_( 0, 12 ) == "reads sorted" ) )
         || 
         ( pGcw_->soDisplayedMessageAtBottom_.bIsNull() );

      if ( !bLeave ) {
         pGcw_->displayMessageAtBottom("");
      }

   }
}



void ContigWin :: refreshContigName() {
   pGcw_->setContigLabel( pContig_->soGetFancyName() );
}



void ContigWin :: showTemplateInfo() {
   
   PleaseWait* pPleaseWait = 
      new PleaseWait( GuiApp::pGetGuiApp()->widGetTopLevel() );

   ConsEd::pGetAssembly()->setContigTemplateArrays();


   const int nNumberOfRowsVisible = 35;
   TextBox* pTextBox = new TextBox( "Template Info",
                                    nNumberOfRowsVisible );

   pTextBox->append( "\n\n" );
   pTextBox->append( "Name               Left   Right  Size     Library Def size\n" );
   pTextBox->append( "                                 of templ         of lib " );
   pTextBox->append( "\n" );


   for( int nTemplate = 0; nTemplate < pContig_->aSubcloneTemplates_.entries();
        ++nTemplate ) {
      subcloneTTemplate* pSub = pContig_->aSubcloneTemplates_[ nTemplate ];

      pTextBox->appendWithArgs( "%-15s %7d %7d %5d %15s %-5d\n",
                                pSub->soTemplateName_.data(),
                                pSub->nUnpaddedStart_,
                                pSub->nUnpaddedEnd_,

                                pSub->nUnpaddedEnd_ 
                                -
                                pSub->nUnpaddedStart_
                                + 1,

                                pSub->pLibrary_->soName_.data(),
                                pSub->pLibrary_->nAverageInsertSizeToUse_
                                );
      for( int nRead = 0; nRead < pSub->aReads_.length(); ++nRead ) {
         LocatedFragment* pLocFrag = pSub->aReads_[ nRead ];
         pTextBox->appendWithArgs( 
                   "          %s %s from %d to %d in %s\n",
                   pLocFrag->soGetName().data(),
                   ( pLocFrag->bComp() ? "bottom" : "top   " ),
                   pLocFrag->nGetAlignStartUnpadded(),
                   pLocFrag->nGetAlignEndUnpadded(),
                   pLocFrag->pContig_->soGetName().data() );
      }

   }
   
   delete pPleaseWait;

   pTextBox->makeVisible();
}



void ContigWin :: changeToXsToLeftInAllReads() {
   changeToXsInAllReads( 'l' );
}


void ContigWin :: changeToXsToRightInAllReads() {
   changeToXsInAllReads( 'r' );
}


void ContigWin :: changeToXsInAllReads( const char cRightOrLeft ) {

   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                          "you must first set the cursor by clicking on a consensus base" );
      return;
   }


   PleaseWait* pPleaseWait =
      new PleaseWait( pGcw_->widGetGuiContigWinTopLevel() );

   int nCursorPosition = editCursor_.nConsPosGet();

   int nLeftMostPosition;
   int nRightMostPosition;

   if ( cRightOrLeft == 'r' ) {
      // find all read from the cursor position to the end of the contig
      // (including reads that stick out to the right)

      nLeftMostPosition = nCursorPosition;
      nRightMostPosition = pContig_->nGetLastDisplayableContigPos();
   }
   else if ( cRightOrLeft == 'l' ) {
      nLeftMostPosition = pContig_->nGetFirstDisplayableContigPos();
      nRightMostPosition = nCursorPosition;
   }
   else
      assert( false );

   ContigView* pContigView = pContig_->pGetContigView( nLeftMostPosition,
                                                       nRightMostPosition );
   
   // I don't think so--DG, Sept 2008 since the displayed reads
   // are not changing.  Notice that pContigView above is not changing
   // pContigView_
   //   resetRoomForReadNames();

   for( int nRead = 0; nRead < pContigView->nGetNumberOfFragments();
        ++nRead ) {

      LocatedFragment* pLocFrag = pContigView->pLocatedFragmentGet( nRead );

      int nConsPosLeftInRead;
      int nConsPosRightInRead;
      if ( !bIntersect( nLeftMostPosition,
                        nRightMostPosition,
                        pLocFrag->nGetAlignStart(),
                        pLocFrag->nGetAlignEnd(),
                        nConsPosLeftInRead,
                        nConsPosRightInRead ) )
         continue; // I don't see how this could ever happen

      editChangeToXs* pEditChange =
         new editChangeToXs( pLocFrag, 
                             nConsPosLeftInRead,
                             nConsPosRightInRead,
                             cRightOrLeft );

      ConsEd::pGetConsEd()->doEditActionNoRefresh(
                                  pEditChange,
                                  true ); // bWriteToEditHistoryFile
   }


   ConsEd::pGetConsEd()->refreshAllContigWinsAndAllTeditors();

   delete pContigView;
   delete pPleaseWait;
}



void ContigWin :: createCloneEndTag( const bool bInsertToRight ) {

   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                          "you must first set the cursor by clicking on a consensus or read base" );
      return;
   }


   EditAction* pEditAction = NULL;
   bool bUserPushedCancel; // not used

   if ( editCursor_.bCursorOnFrag() ) {


      tag* pTag = tagTypes::pGetTagTypes()->pCreateReadTag(
                       "cloneEnd",
                       "", // soTagComment
                       editCursor_.nConsPosGet(),
                       editCursor_.nConsPosGet(),
                       editCursor_.pLocFragGet(),
                       true, // bWriteToPhdFileNotAceFile
                       bUserPushedCancel );

      if ( bInsertToRight )
         pTag->soMiscData_ = "->";
      else
         pTag->soMiscData_ = "<-";


      pEditAction = new editAddReadTag( pTag );
   }
   else {
      
      tag* pTag = tagTypes::pGetTagTypes()->pCreateConsensusTag(
                       "cloneEnd",
                       "", // soComment
                       editCursor_.nConsPosGet(),
                       editCursor_.nConsPosGet(),
                       pContig_,
                       false, // bNoTrans
                       bUserPushedCancel,
                       pContig_->soGetName() );

      if ( bInsertToRight )
         pTag->soMiscData_ = "->";
      else
         pTag->soMiscData_ = "<-";


      pEditAction = new editAddConsensusTag( pTag );
   }


   ConsEd::pGetConsEd()->doEditAction( pEditAction, 
                                       true ); // bWriteToEditHistoryFile


}



void ContigWin :: resetRoomForReadNames() {

   if ( pCP->bAlignedReadsWindowAutomaticallyExpandRoomForReadNames_ ) {

      pGcw_->nRoomForReadNames_ = 0;

      for( int nRead = 0; nRead < pContigView_->nGetNumberOfFragments();
           ++nRead ) {

         LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet( nRead );

         pGcw_->nRoomForReadNames_ = 
            MAX( pGcw_->nRoomForReadNames_,
                 pLocFrag->soGetName().length() );

      }

      // but in no circumstances do we want to allow the read names
      // to take up more than the width of the screen.  In fact, we
      // need some room for the bases, so only allow it to take up,
      // say, 3/4 of the room on the screen

      // I think I will just give a warning and tell the user what to do:

      if ( pGcw_->nRoomForReadNames_ * GuiApp::pGetGuiApp()->nGetFontWidth()
           + pGcw_->nFragmentNameXPixelPosition() + 
           nPixelMarginAfterFragmentName
           > pGcw_->nGetContigWindowPixelWidth() ) {
         RWCString soError = "your read names are very long and will cause no bases to be displayed.  If you want to have read names this long, then you should set the following in your .consedrc file:  consed.alignedReadsWindowAutomaticallyExpandRoomForReadNames: false\nIf you want more room for your read names, then increase consed.alignedReadsWindowMaxCharsForReadNames: 20";
         cerr << soError << endl;
         popupErrorMessage( soError );
      }
   } //  if ( pCP->bAlignedReadsWindowAutomaticallyExpandRoomForReadNames_ )
   else {
      pGcw_->nRoomForReadNames_ = pCP->nAlignedReadsWindowMaxCharsForReadNames_;
   }
}



void ContigWin :: compareReadsToReference() {

   guiGetReferenceRead* pRefRead =
      new guiGetReferenceRead( pGcw_->widGetGuiContigWinTopLevel(),
                               pContig_,
                               this );
   pRefRead->createWindow();
}



void ContigWin :: userPressedUserDefinedKey( const RWCString& soProgram,
                                             const RWCString& soArgument,
                                             const RWCString& soTagToApply ) {

   
   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                          "You must first set the cursor before pressing this key" );
      return;
   }

   // pick up the cursor location from the EditCursor

   LocatedFragment* pLocFrag = ( editCursor_.bCursorOnCons() ? NULL :
                                 editCursor_.pLocFragGet() );


   // the cursor may not be visible.  In this case do not let the
   // user apply the user-defined key.

   int nConsPos = editCursor_.nConsPosGet();

   if ( nConsPos < nLeftEdgeConsensusPosition_ ||
        nGetRightEdgeConsensusPosition() < nConsPos ) {

      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(), 
                          "use the left mouse button to set the cursor in the aligned reads window before striking this key" );
      return;
   }

   // see if there is a tag to be applied

   if ( soTagToApply != "none" &&
        !tagTypes::pGetTagTypes()->bIsAValidTagType( soTagToApply ) ) {
      RWCString soError = "unrecognized tag type for user defined key: \"";
      soError += soTagToApply;
      soError += "\nSee Info/Show Current Consed Resources:\nconsed.tagsToApplyWithUserDefinedKeys\nTo define tags, click on \"Help\" and look for \"CREATING CUSTOM TAG TYPES\"";
      THROW_ERROR( soError );
   }

   int nUnpaddedSeqPosInDirectionOfSequencing;
   if ( editCursor_.bCursorOnCons() ) {
      nUnpaddedSeqPosInDirectionOfSequencing = -1;
   }
   else {
      nUnpaddedSeqPosInDirectionOfSequencing = 
         pLocFrag->nOrientedUnpaddedFragPosFromConsPos( nConsPos );
   }

   assert( editCursor_.pGetContigEvenIfCursorIsOnRead() == pContig_ );

   int nUnpadded = pContig_->nUnpaddedIndex( nConsPos );

   char cBase;
   int nQuality;
   if ( editCursor_.bCursorOnCons()  ) {
      cBase = pContig_->ntGetCons( nConsPos ).cGetBase();
      nQuality = pContig_->ntGetCons( nConsPos ).qualGetQuality();
   }
   else {
      cBase = pLocFrag->ntGetFragFromConsPos( nConsPos ).cGetBase();
      nQuality = pLocFrag->ntGetFragFromConsPos( nConsPos ).qualGetQuality();
   }

   RWCString soCommandLine = soProgram + " " + 
      soArgument + " " +
      ( editCursor_.bCursorOnCons() ? RWCString( "consensus" ) : pLocFrag->soGetName() ) +
      " " + 
      RWCString( (long) nUnpaddedSeqPosInDirectionOfSequencing ) + " " +
      pContig_->soGetName() + " " + 
      RWCString( (long) nUnpadded ) + " " +
      RWCString( (long) nConsPos ) + " " +
      RWCString( cBase ) + " " +
      RWCString( (long) nQuality ) + " " +
      ConsEd::pGetAssembly()->filGetAceFileFullPathname() + " " +
      "a.r.window";

   int nReturnStatus = system( soCommandLine.data() );

   if ( nReturnStatus != 0 ) {
      RWCString soErrorMessage = "unable to run command ";
      soErrorMessage += soCommandLine;
      soErrorMessage += " because of return status ";
      soErrorMessage += RWCString( (long) nReturnStatus );
      soErrorMessage += " meaning ";
      soErrorMessage += soGetErrno();
      THROW_ERROR( soErrorMessage );
   }

   if ( soTagToApply != "none" ) {
      EditAction* pEditAction = NULL;
      if ( editCursor_.bCursorOnCons() ) {

         bool bUserPushedCancel = false;
         tag* pTag = tagTypes::pGetTagTypes()->pCreateConsensusTag(
              soTagToApply,
              "", // soComment
              nConsPos,
              nConsPos,
              pContig_,
              false, // bNoTrans
              bUserPushedCancel,
              "" ); // only used for contigName tags

         if ( bUserPushedCancel ) return; // I don't think this code
         // path will ever be taken because the user will never be given
         // a chance to cancel

         pEditAction = new editAddConsensusTag( pTag );
      }
      else {

         bool bUserPushedCancel = false;
         tag* pTag = tagTypes::pGetTagTypes()->pCreateReadTag(
              soTagToApply,
              "", // soComment
              nConsPos,
              nConsPos,
              pLocFrag,
              true, // bWriteToPhdFileNotAceFile
              bUserPushedCancel );

         if ( bUserPushedCancel ) return;  // I don't think this code
         // path will ever be taken because the user will never be given
         // a chance to cancel

         pEditAction = new editAddReadTag( pTag );
      }         

      ConsEd::pGetConsEd()->doEditAction( pEditAction, true );
   }   

}

   

   
void ContigWin :: turnOnOrOffStartNumberingConsensusStartingAtUserDefinedPosition() {

   pCP->bNumberUnpaddedConsensusAtUserDefined_ =
      !pCP->bNumberUnpaddedConsensusAtUserDefined_;

   // redraw everything
   ConsEd::pGetConsEd()->refreshAllContigWinsAndAllTeditors();
}


void ContigWin :: showUncalledPeakInfo() {

   if ( !editCursor_.bCursorValid() ) {
      popupErrorMessage2( pGcw_->widGetGuiContigWinTopLevel(),
                          "You must first set the cursor before pressing this key" );
      return;
   }

   PleaseWait* pPleaseWait = 
      new PleaseWait( pGcw_->widGetGuiContigWinTopLevel() );


   RWCString soMessage( (size_t) 10000 );
   
   // get all reads at the cursor position

   int nConsPos = editCursor_.nConsPosGet();

   soMessage = "unpadded pos: ";
   soMessage += RWCString( (long) pContig_->nUnpaddedIndex( nConsPos ) );
   soMessage += "\nread           rel area qual high quality segment\n";
   

   for( int nRead = 0; nRead < pContigView_->nGetNumberOfFragments();
           ++nRead ) {

      LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet( nRead );

      if ( pLocFrag->bIsInAlignedPartOfRead( nConsPos ) ) {

         mbtValVector<float>* pRelativeAreasOfUncalledBases = NULL;

         if ( !pLocFrag->bGetSingleSignalRelativeAreas( pRelativeAreasOfUncalledBases ) ) {
            soMessage += pLocFrag->soGetName();
            soMessage += " no chromat\n";
            continue;
         }

         int n1UnpaddedReadPos = 
            pLocFrag->nOrientedUnpaddedFragPosFromConsPos( nConsPos );

         float fRelativeArea = (*pRelativeAreasOfUncalledBases)[ n1UnpaddedReadPos ];
         delete pRelativeAreasOfUncalledBases;

         RWCString soHQS;
         if ( pLocFrag->bIsInHighQualitySegmentOfRead( nConsPos ) ) {
            soHQS = "hqs";
         }
         else {
            soHQS = "lqs";
         }
            


         RWCString soLine( (size_t) 1000 );
         sprintf( soLine.data(), "%-30s %6.3f %2d %3s\n",
                  pLocFrag->soGetName().data(),
                  fRelativeArea,
                  pLocFrag->ntGetFragFromConsPos( nConsPos ).qualGetQuality(),
                  soHQS.data() );

         soLine.nCurrentLength_ = strlen( soLine.data() );

         soMessage += soLine;
      }
      else {
         soMessage += pLocFrag->soGetName();
         soMessage += "  unaligned at this cons pos\n";
      }
   }

   soMessage.stripTrailingWhitespaceFast();


   int nRowsNeeded = 2 + pContigView_->nGetNumberOfFragments();

   if ( nRowsNeeded > 50 )
      nRowsNeeded = 50;

   TextBox* pTextBox = new TextBox( "quality and uncalled peaks of reads",
                                    nRowsNeeded );

   pTextBox->append( soMessage );
   pTextBox->makeVisible();

   delete pPleaseWait;

}




void ContigWin :: editAllReads(  LocatedFragment* pMasterLocFrag,
                                   const int nConsPos ) {

   bool bGoAhead = true;
   if ( pCP->bWarnUserWhenTryingToEditAllReads_ ) {

      RWCString soWarning = "Warning: You are attempting to change all of the reads at this position at once.  We suggest that you do not do this, but instead open each trace and edit it there.  Do you still want to edit all reads at once? (y/n)";

      bGoAhead = bGuiGetAnswerYesNo( soWarning );

      if ( bGoAhead ) {
         
         bool bTurnOffWarning = bGuiGetAnswerYesNo( "would you prefer to not get this warning in the future?" );

         if ( bTurnOffWarning ) {
            popupInfoMessage( pGcw_->widMainWinTopLevelShell_,
                              "Warning turned off.  If you want to turn it off forever, you can set the .consedrc resource  consed.warnUserWhenTryingToEditAllReads: false" );
            pCP->bWarnUserWhenTryingToEditAllReads_ = false;
         }
      }
   }

   if ( !bGoAhead ) return;


   RWTPtrOrderedVector<LocatedFragment> aReadsToChange( (size_t) pContigView_->nGetNumberOfFragments() );

   for( int nRead = 0; nRead < pContigView_->nGetNumberOfFragments();
           ++nRead ) {

      LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet( nRead );

      if ( pLocFrag->bIsInAlignedPartOfRead( nConsPos ) ) {
         aReadsToChange.insert( pLocFrag );
      }
   }

   editOverstrikeAllReadsAtConsPos* pEdit = 
      new editOverstrikeAllReadsAtConsPos( nConsPos, 
                                           pMasterLocFrag, aReadsToChange );


   ConsEd::pGetConsEd()->doEditAction( pEdit, true );
}



// the reason for have 2 drawVerticalLineAtCursor routines instead of 1 is
// to prevent drawBasesAndConsensus calling drawVerticalLineAtCursor which
// calls drawBasesAndConsensus...
// So current drawBasesAndConsensus calls drawVerticalLineAtCursorNoRedraw
// which does *not* call drawBasesAndConsensus


void ContigWin :: drawVerticalLineAtCursorNoRedraw() {

   if ( !pCP->bPutVerticalLineAtCursor_ ) return;

   if (! editCursor_.bCursorValid()) return;

   // is the cursor in the current view, and if so where
   int nScreenCharPos;
   int nFragLine;
   bool bOnFrag;
   bool bCursorInView = 
      editCursor_.bGetViewPos(this,
                              nScreenCharPos,
                              bOnFrag,
                              nFragLine);

   if (! bCursorInView) return; // done

   pGcw_->guiDrawVerticalLineAtCursor( nScreenCharPos );


   nCurrentVerticalLineScreenCharPos_ = nScreenCharPos;
   bDrewEverythingSinceLastDrewVerticalLine_ = false;

}
   

   
void ContigWin :: drawVerticalLineAtCursorIfNecessary() {

   if ( !pCP->bPutVerticalLineAtCursor_ ) return;

   if (! editCursor_.bCursorValid()) return;

   int nScreenCharPos;
   int nFragLine;
   bool bOnFrag;
   bool bCursorInView = 
      editCursor_.bGetViewPos(this,
                              nScreenCharPos,
                              bOnFrag,
                              nFragLine);

   if ( !bCursorInView ) return;

   if ( !bDrewEverythingSinceLastDrewVerticalLine_ &&
        nScreenCharPos != nCurrentVerticalLineScreenCharPos_ ) {

      drawBasesAndConsensus( true );
      return; // drawBasesAndConsensus will call drawVerticalLineAtCursorNoRedraw
   }


   drawVerticalLineAtCursorNoRedraw();
   
}





void ContigWin :: toggleHorizontalLineAtCursor() {

   pCP->bPutHorizontalLineAtCursor_ = !pCP->bPutHorizontalLineAtCursor_;


   if ( pCP->bPutHorizontalLineAtCursor_ ) {
      if ( editCursor_.bCursorValid() ) {

         int nScreenCharPos;
         int nFragLine;
         bool bOnFrag;
         bool bCursorInView =
            editCursor_.bGetViewPos( this,
                                     nScreenCharPos,
                                     bOnFrag,
                                     nFragLine );

         if ( bCursorInView && bOnFrag ) {
            pGcw_->guiDrawHorizontalLineAtCursor( nFragLine );
         }
      }
   }
   else {
      ConsEd::pGetConsEd()->refreshAllContigWins();
   }

}


   
   
void ContigWin :: toggleVerticalLineAtCursor() {

   pCP->bPutVerticalLineAtCursor_ = !pCP->bPutVerticalLineAtCursor_;

   ConsEd::pGetConsEd()->refreshAllContigWins();
}

   


bool ContigWin :: bDrawSeparatorBetweenTopAndBottomStrandReads() {


   // nGetForFragsVisible() == 0 is
   // case in which there are only reverse frag lines
   // Thus there is also no forward/reverse separator,
   // so the positions of the reverse lines is just like that
   // for forward reads

   if ( ( pContigView_->cHowAreReadsSorted_ == 
          ContigView::cSTRAND_AND_POSITION ) &&
        ( nGetForFragsVisible() > 0 ) )
      return true;
   else
      return false;
}


bool ContigWin :: bIsEditCursorVisible() {

   
   if ( !editCursor_.bCursorValid() ) return false;

   int nConsPosOfCursor = editCursor_.nConsPosGet();

   if ( nLeftEdgeConsensusPosition_ <= nConsPosOfCursor &&
        nConsPosOfCursor <= nGetRightEdgeConsensusPosition() ) {
      return true;
   }
   else
      return false;

}




// just used by horizontalScrollBarMoved(
void ContigWin :: maybeSortReadsAtCursor() {

   if ( !bIsEditCursorVisible() ) return;

   if ( pCP->soShowReadsAtCursorSortedHow_ == "none" ) return;

   int nConsPosAtCursor = editCursor_.nConsPosGet();

   if ( pCP->soShowReadsAtCursorSortedHow_ == "quality" ) {
      pContigView_->sortReadsByQualityAtConsPos( nConsPosAtCursor );
   }
   else if ( pCP->soShowReadsAtCursorSortedHow_ == "base" ) {
      pContigView_->sortReadsByBaseAtConsPos( nConsPosAtCursor );
   }

}



void ContigWin :: sortByQualityValuesAtCursor( const bool b ) {


   if ( b ) {
      pCP->soShowReadsAtCursorSortedHow_ = "quality";
   }
   else {
      pCP->soShowReadsAtCursorSortedHow_ = "none";
   }

   // get cursor, if it is available

   if ( !bIsEditCursorVisible() ) return;
   
   int nConsPosAtCursor = editCursor_.nConsPosGet();
   

   if ( pCP->soShowReadsAtCursorSortedHow_ == "quality" ) {
      pContigView_->sortReadsByQualityAtConsPos( nConsPosAtCursor );
   }
   else {
      pContigView_->sortReads();
   }

   displayHowReadsAreSorted();

   // now redisplay reads

   drawBasesAndConsensus( true ); // erase first

}



void ContigWin :: userOverstrikingConsensus( const int nConsPos,
                                             const char cNewBaseLower ) {


   // check if this base agrees with any read--allow ambiguity codes

   setNumberFromBase4();

   int nNewBase = nNumberFromBase4[ cNewBaseLower ];

   bool bAReadMatches = false;
   for( int nRead = 0; nRead < pContigView_->nGetNumberOfFragments();
           ++nRead ) {

      LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet( nRead );

      // commented out Oct 2009 since when extending consensus,
      // most reads are unaligned and thus it will give a warning
      // that no read has this base
      //      if ( !pLocFrag->bIsInAlignedPartOfRead( nConsPos ) ) continue;

      if ( !pLocFrag->bIsInRead( nConsPos ) ) continue;

      if ( nNumberFromBase4[ pLocFrag->ntGetFragFromConsPos( nConsPos ).cGetBase() ] 
           & nNewBase ) {
         bAReadMatches = true;
         break;
      }
   }

   if ( !bAReadMatches ) {
      RWCString soQuestion = "There is no read that has base " +
         RWCString( cNewBaseLower ) + ".  Are you sure?";
      if ( !bGuiGetAnswerYesNo( soQuestion ) ) {
         return;
      }
   }

   EditAction* pEditAction = new editOverstrikeConsensus( pContig_,
                                                          nConsPos,
                                                          cNewBaseLower );

   ConsEd::pGetConsEd()->doEditAction( pEditAction, true );

   
}

   
   


void ContigWin :: displayHowReadsAreSorted() {


   RWCString soHowSorted = pContigView_->soGetHowReadsAreSorted();


   pGcw_->displayMessageAtBottom( soHowSorted.data() );

}