/***************************************************************************** # 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. # #*****************************************************************************/ #include "displayPrimers.h" #include "numutil.h" #include using namespace std; #include #include "assert.h" #include "contig.h" #include "consedParameters.h" #include "whyIsPrimerNotAcceptableTypes.h" #include "guiMultiContigNavigator.h" #include "textbox.h" #include "oligoTag.h" #include "editAddConsensusTag.h" #include "consed.h" #include "guicontigwin.h" #include "guiGetCommentForTag.h" #include "popupErrorMessage2.h" #include "bIsPrimerStale.h" static const int nLengthOfPrimerOutputField = 25; static const int nLengthOfTemplateOutputField = 15; #define bIsBetween( A, B , C ) \ ( A <= B ) && ( B <= C ) class passToAcceptPrimer { public: passToAcceptPrimer() {} bool bCloneNotSubcloneTemplate_; bool bForwardNotReversePrimer_; guiMultiContigNavigator* pGuiMultiContigNavigator_; }; static RWCString soBlankTemplate( ' ', (size_t) (nLengthOfTemplateOutputField ) ); static void printStickiestFalseMatch( primerType* pPrimer, const int nPrimerNumber ) { char szDescription[500]; int n5PrimeFalseMatch = 0; int n3PrimeFalseMatch = 0; RWCString soFalseMatchContigName; // handle case in which there is no false match at all // so pContigOfStickiestFalseMatch is NULL if ( !pPrimer->pContigOfStickiestFalseMatch_ ) { printf( "no false match. nScoreOfStickiestFalseMatch_ = %d\n", pPrimer->nScoreOfStickiestFalseMatch_ ); return; } if ( pPrimer->bContigOfStickiestFalseMatchIsComplemented_ ) { n5PrimeFalseMatch = pPrimer->pContigOfStickiestFalseMatch_->nComplementUnpaddedIndex( pPrimer->nUnpaddedOfStickiestFalseMatch_ ); n3PrimeFalseMatch = n5PrimeFalseMatch - consedParameters::pGetConsedParameters()->nPrimersMinimumLengthOfAPrimerToUse_ + 1; } else { n5PrimeFalseMatch = pPrimer->nUnpaddedOfStickiestFalseMatch_; n3PrimeFalseMatch = n5PrimeFalseMatch + consedParameters::pGetConsedParameters()->nPrimersMinimumLengthOfAPrimerToUse_ - 1; } soFalseMatchContigName = pPrimer->pContigOfStickiestFalseMatch_->soGetName(); for( int n = 0; n < pPrimer->nUnpaddedLength_; ++n ) putchar( *(pPrimer->szPrimer_ + n) ); printf( " primer: %d match elsewhere score: %d\n", nPrimerNumber, pPrimer->nScoreOfStickiestFalseMatch_ ); // false matches are determined by examining just the suboligo of // length nPrimersMinimumLengthOfAPrimer_ // This may be less than nUnpaddedLength_ of the primer. So to // show the alignment, put spaces to start with if ( ABS( n5PrimeFalseMatch - n3PrimeFalseMatch ) + 1 < pPrimer->nUnpaddedLength_ ) { int nNumberOfSpacesToAdd = pPrimer->nUnpaddedLength_ - ABS( n5PrimeFalseMatch - n3PrimeFalseMatch ) - 1; for( int n = 0; n < nNumberOfSpacesToAdd; ++n ) putchar( ' ' ); } if ( pPrimer->pContigOfStickiestFalseMatch_ ) { if ( pPrimer->bContigOfStickiestFalseMatchIsComplemented_ ) { for( int nUnpadded = n5PrimeFalseMatch; nUnpadded >= n3PrimeFalseMatch; --nUnpadded ) { int nConsPos =pPrimer->pContigOfStickiestFalseMatch_->nPaddedIndexFast( nUnpadded ); char c = pPrimer->pContigOfStickiestFalseMatch_->ntGetCons( nConsPos ).cGetBase(); putchar( cComplementBase( c ) ); } } else { for( int nUnpadded = n5PrimeFalseMatch; nUnpadded <= n3PrimeFalseMatch; ++nUnpadded ) { int nConsPos = pPrimer->pContigOfStickiestFalseMatch_->nPaddedIndexFast( nUnpadded ); char c = pPrimer->pContigOfStickiestFalseMatch_->ntGetCons( nConsPos ).cGetBase(); putchar( c ); } } } printf( " false match at %s 5': %6d 3': %6d %s\n", (char*) soFalseMatchContigName.data(), n5PrimeFalseMatch, n3PrimeFalseMatch, ( pPrimer->bContigOfStickiestFalseMatchIsComplemented_ ? "Complemented" : "not complemented" ) ); } static void displayOnePrimer( primerType* pPrimer, guiMultiContigNavigator* pGuiMultiContigNav, const int nPrimerNumber, const bool bCloneNotSubcloneTemplate ) { assert( pPrimer->bAcceptable_ ); char szDescription[500]; sprintf( szDescription, "%3d ", nPrimerNumber ); strncat( szDescription, pPrimer->szPrimer_, pPrimer->nUnpaddedLength_ ); if ( pPrimer->nUnpaddedLength_ < nLengthOfPrimerOutputField ) { int nBlanksToAdd = nLengthOfPrimerOutputField - pPrimer->nUnpaddedLength_; for( int n = 0; n < nBlanksToAdd; ++n ) strcat( szDescription, " " ); } else { // always add at least 1 blank between the primer and the distance // from cursor (below) strcat( szDescription, " " ); } int nLength = strlen( szDescription ); sprintf( szDescription + nLength, "%3d %2d ", pPrimer->nDistanceFromCursor_, (int) ( pPrimer->dMeltingTemperature_ + .5 )); int nTemplatesDisplayedInFront; if ( !bCloneNotSubcloneTemplate ) { nTemplatesDisplayedInFront = MIN( nNUMBER_OF_TEMPLATES, pCP->nPrimersNumberOfTemplatesToDisplayInFront_ ); RWCString soTemplates; int n; for( n = 0; n < nTemplatesDisplayedInFront; ++n ) { if ( pPrimer->pSubcloneTemplate_[n] ) { soTemplates += pPrimer->pSubcloneTemplate_[n]->soTemplateName_; int nLengthOfTemplateName = pPrimer->pSubcloneTemplate_[n]->soTemplateName_.length(); if ( nLengthOfTemplateName < nLengthOfTemplateOutputField ) { RWCString soBlankFill( ' ', (size_t) (nLengthOfTemplateOutputField - nLengthOfTemplateName) ); soTemplates += soBlankFill; } else soTemplates += " "; } else { soTemplates += soBlankTemplate; } } strcat( szDescription, (char*) soTemplates.data() ); } // if ( !bCloneNotSubcloneTemplate ) { nLength = strlen( szDescription ); sprintf( szDescription + nLength, "%7d %7d %2d %2d %2d %2d %2d ", pPrimer->nUnpaddedStart_, pPrimer->nUnpaddedEnd_, pPrimer->nUnpaddedLength_, pPrimer->nSelfMatchScore_, pPrimer->nScoreOfStickiestFalseMatch_, pPrimer->nSequenceInFileStickiestScore_, (int) pPrimer->ucMinQuality_ ); if ( !bCloneNotSubcloneTemplate ) { RWCString soTemplates; for( int n = nTemplatesDisplayedInFront; n < nNUMBER_OF_TEMPLATES; ++n ) { if ( pPrimer->pSubcloneTemplate_[n] ) { soTemplates += pPrimer->pSubcloneTemplate_[n]->soTemplateName_; int nLengthOfTemplateName = pPrimer->pSubcloneTemplate_[n]->soTemplateName_.length(); if ( nLengthOfTemplateName < nLengthOfTemplateOutputField ) { RWCString soBlankFill( ' ', (size_t) (nLengthOfTemplateOutputField - nLengthOfTemplateName ) ); soTemplates += soBlankFill; } else soTemplates += " "; } else { soTemplates += soBlankTemplate; } } strcat( szDescription, (char*) soTemplates.data() ); } // if ( !bCloneNotSubcloneTemplate ) { int nPaddedConsPosStart = pPrimer->pContig_->nPaddedIndexFast( pPrimer->nUnpaddedStart_ ); int nPaddedConsPosEnd = pPrimer->pContig_->nPaddedIndexFast( pPrimer->nUnpaddedEnd_ ); gotoItem* pGotoItem = new gotoItem( pPrimer->pContig_, NULL, // LocatedFragment nPaddedConsPosStart, nPaddedConsPosEnd, pPrimer->nUnpaddedStart_, pPrimer->nUnpaddedEnd_, szDescription, false, // do not prefix consensus to description (void*) pPrimer ); pGuiMultiContigNav->appendToList( pGotoItem ); // print out stickiest false alignment printStickiestFalseMatch( pPrimer, nPrimerNumber ); } static RWTValOrderedVector aDisplayedPrimerIndices; static void acceptPrimer( const gotoItem* pGotoItem, void* pPassToAcceptPrimerVoid ) { passToAcceptPrimer* pPassToAcceptPrimer = (passToAcceptPrimer*) pPassToAcceptPrimerVoid; if ( pGotoItem == NULL ) { GuiApp::popupErrorMessage( "Please first click on a primer" ); return; } primerType* pPrimer = (primerType*) pGotoItem->pOtherData_; // check that primer info is still valid RWCString soErrorMessage; if ( bIsPrimerStale( pPrimer, soErrorMessage ) ) { popupErrorMessage2( pPassToAcceptPrimer->pGuiMultiContigNavigator_->widPopupShell_, soErrorMessage ); return; } RWCString soTagComment; bool bUserPushedCancel; popupBoxAndWaitForUserToEnterComment( soTagComment, bUserPushedCancel, pPassToAcceptPrimer->pGuiMultiContigNavigator_->widPopupShell_ ); if ( bUserPushedCancel ) return; // char szPrimer[200]; // szPrimer[0] = 0; // strncat( szPrimer, pPrimer->szPrimer_, pPrimer->nUnpaddedLength_ ); // char szDescription[500]; // sprintf( szDescription, " Contig: %s Start: %d End: %d Len: %d Melting Temp: %d ", // (char*) pPrimer->pContig_->soGetName().data(), // pPrimer->nUnpaddedStart_, // pPrimer->nUnpaddedEnd_, // pPrimer->nUnpaddedLength_, // (int) pPrimer->dMeltingTemperature_ + .5 ); // char szDescription2[500]; // szDescription2[0] = 0; // strcat( szDescription2, szPrimer ); // strcat( szDescription2, szDescription ); // cout << szDescription2 << endl; bool bComplementedWithRespectToWayPhrapCreatedContig = ! (pPassToAcceptPrimer->bForwardNotReversePrimer_ ); if ( pPrimer->pContig_->bIsComplementedFromWayPhrapCreated() ) bComplementedWithRespectToWayPhrapCreatedContig = ! bComplementedWithRespectToWayPhrapCreatedContig; oligoTag* pOligoTag = new oligoTag( pPrimer, pPassToAcceptPrimer->bCloneNotSubcloneTemplate_, bComplementedWithRespectToWayPhrapCreatedContig, -1 , // create new oligo name soTagComment ); EditAction* pEditAction = new editAddConsensusTag( pOligoTag ); // the edit action, once formed, goes all the way up to // the consed. any in-between objects will get notified // on the way back down by the refresh message (if the edit // completes successfully ConsEd::pGetConsEd()->doEditAction( pEditAction, true ); // scroll to the location of the accepted tag if ( pPassToAcceptPrimer->pGuiMultiContigNavigator_->pContigWin_ ) { ContigWin* pContigWin = pPassToAcceptPrimer->pGuiMultiContigNavigator_->pContigWin_; if ( pContigWin->pContig_ == pGotoItem->pContig_ ) { pContigWin->scrollToConsensusPosInCenter( pGotoItem->nGotoItemStart_ ); } } pPassToAcceptPrimer->pGuiMultiContigNavigator_->popDownAndDestroy(); } void displayPrimers( primerType* pPrimerArray, const int nNumberOfAcceptablePrimers, const bool bForwardNotReversePrimers, const bool bCloneNotSubcloneTemplate, ContigWin* pContigWin ) { assert( nNumberOfAcceptablePrimers ); int nExtraRoomForTemplateName = 0; if ( !bCloneNotSubcloneTemplate ) { int nLargestTemplateName = 0; for( int nPrimer = 0; nPrimer < nNumberOfAcceptablePrimers; ++nPrimer ) { primerType* pPrimer = pPrimerArray + nPrimer; subcloneTTemplate* pSub = pPrimer->pSubcloneTemplate_[0]; if ( pSub ) { if ( pSub->soGetName().length() > nLargestTemplateName ) nLargestTemplateName = pSub->soGetName().length(); } } if ( nLargestTemplateName > nLengthOfTemplateOutputField ) nExtraRoomForTemplateName = nLargestTemplateName - nLengthOfTemplateOutputField; } RWCString soColumnHeadings1 = "primer primer dis to melt"; RWCString soColumnHeadings2 = "number bases cursor temp"; if ( !bCloneNotSubcloneTemplate ) { int n; for( n = 0; n < consedParameters::pGetConsedParameters()->nPrimersNumberOfTemplatesToDisplayInFront_; ++n ) { if ( n == 0 ) soColumnHeadings1 += " best "; else { soColumnHeadings1 += " next best "; soColumnHeadings1 += RWCString( ' ', (size_t) nExtraRoomForTemplateName ); } } for( n = 0; n < pCP->nPrimersNumberOfTemplatesToDisplayInFront_; ++n ) { if ( n == 0 ) soColumnHeadings2 += " template "; else { soColumnHeadings2 += " template "; soColumnHeadings2 += RWCString( ' ', (size_t) nExtraRoomForTemplateName ); } } soColumnHeadings1 += " "; soColumnHeadings2 += " "; } // if ( !bCloneNotSubcloneTemplate ) { soColumnHeadings1 += " left right len -----matches----- min"; soColumnHeadings2 += " pos pos self false vector qua"; if ( !bCloneNotSubcloneTemplate ) { for( int n = pCP->nPrimersNumberOfTemplatesToDisplayInFront_; n < nNUMBER_OF_TEMPLATES; ++n ) { soColumnHeadings1 += " next best "; soColumnHeadings2 += " template "; soColumnHeadings1 += RWCString( ' ', (size_t) nExtraRoomForTemplateName ); soColumnHeadings2 += RWCString( ' ', (size_t) nExtraRoomForTemplateName ); } } passToAcceptPrimer* pPassToAcceptPrimer = new passToAcceptPrimer(); pPassToAcceptPrimer->bCloneNotSubcloneTemplate_ = bCloneNotSubcloneTemplate; pPassToAcceptPrimer->bForwardNotReversePrimer_ = bForwardNotReversePrimers; pContigWin->pGuiMultiContigNav_ = new guiMultiContigNavigator( "Primers", soColumnHeadings1, soColumnHeadings2, 100, "Accept Primer", acceptPrimer, (void*) pPassToAcceptPrimer, pContigWin->pGcw_->widGetGuiContigWinTopLevel(), NULL, // pGotoList pContigWin ); // Notice this is set after calling the ctor for guiMultiContigNavigator // That is ok since pPassToAcceptPrimer is set before passing, and // pPassToAcceptPrimer->pGuiMultiContigNavigator_ isn't used until // much after this routine completes. pPassToAcceptPrimer->pGuiMultiContigNavigator_ = pContigWin->pGuiMultiContigNav_; aDisplayedPrimerIndices.clear(); // overestimate of the number of primers displayed aDisplayedPrimerIndices.resize( nNumberOfAcceptablePrimers / 3 ); int nLast3PrimeEnd = -1000000000; // force != for first primer int nPrimerNumber = 1; // Pat wanted primers sequentially numbered // for reference within the list for( int nPrimer = 0; nPrimer < nNumberOfAcceptablePrimers; ++nPrimer ) { primerType* pPrimer = pPrimerArray + nPrimer; int nThis3PrimeEnd = bForwardNotReversePrimers ? pPrimer->nUnpaddedEnd_ : pPrimer->nUnpaddedStart_ ; // This is to only show the shortest primer that // has a particular 3' end. if (nLast3PrimeEnd != nThis3PrimeEnd ) { nLast3PrimeEnd = nThis3PrimeEnd; displayOnePrimer( pPrimer, pContigWin->pGuiMultiContigNav_, nPrimerNumber, bCloneNotSubcloneTemplate ); aDisplayedPrimerIndices.append( nPrimer ); ++nPrimerNumber; } } pContigWin->pGuiMultiContigNav_->displayList( false ); // don't re-sort the primers }