/***************************************************************************** # 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. # #*****************************************************************************/ // // teditor.cpp // // implementation for the Teditor and TedWin objects. // // #include using namespace std; #include #include #include #include #include #include #include "sysdepend.h" #include "numutil.h" #include "filename.h" #include "guiapp.h" #include "tedwin.h" #include "teditor.h" #include "guiteditor.h" #include "guitedwin.h" #include "consed.h" #include "mbt_errors.h" #include "mbt_exception.h" #include "editaction.h" #include "ntide.h" #include "locatedFragment.h" #include "selection.h" #include "contigwin.h" #include "consedParameters.h" #include "popupErrorMessage.h" #include "tagTypes.h" #include "oligoTag.h" // at the moment this is incomplete - it still creates one Teditor for // each tedwin. hence if the tedwin ctor fails, it destroys the // Teditor. Teditor::Teditor(ContigWin* pContigWin, const int nHoldThisManyTraces, const bool bShowTracesInScrollingWindow ) : pContigWin_(pContigWin), bTedWinsScrollTogether_( true ), pGuiTedWinScrollMaster_( NULL ), pContigView_( NULL ), nConsPos_( -666 ), aConsensusGuiColorTextArray_( 0, 0 ) { // create your companion GuiTeditor object pGuiTeditor_ = new GuiTeditor( this, nHoldThisManyTraces, bShowTracesInScrollingWindow ); } Teditor::~Teditor() { // tell Contigwin to remove you from the list of Teditors pContigWin_->setTeditor( NULL ); // remove resize callback so it doesn't get called pGuiTeditor_->prepareToDie(); // calls dtor for all TedWins dapTedWin_.clearAndDestroy(); // destroy your gui by calling it's dtor delete pGuiTeditor_; } // draw cursor on all child TedWins void Teditor :: drawCursors(const bool bMakeVisible) { for (int nTw = 0; nTw < dapTedWin_.length(); nTw++) { dapTedWin_[nTw]->drawCursor(bMakeVisible); } } // catch a refresh message from consed. crude first cut. // runs down list of all TedWins and redraws each, regardless // of whether needed or not void Teditor::refreshAllTedWins( const bool bRecalculateBasePositions ) { for (int nTw = 0; nTw < dapTedWin_.length(); nTw++) { dapTedWin_[nTw]->drawBasesAndTraces( bRecalculateBasePositions ); } } // special case refresh after complementation. requires scrolling // first. void Teditor :: refreshAllTedWinsAfterCompContig() { for (int nTw = 0; nTw < dapTedWin_.length(); nTw++) { dapTedWin_[nTw]->refreshAfterCompContig(); } } // set the Undo buttons on child TedWins to active or inactive // on each TedWin according to whether or not that TedWin initiated // the last edit void Teditor::setTedWinsUndoButtonState() { for (int nTw = 0; nTw < dapTedWin_.length(); nTw++) { dapTedWin_[nTw]->setUndoButtonState(); } } // get pointer to Contig to which you apply Contig* Teditor :: pGetContig() { return pContigWin_->pGetContig(); } TedWin* Teditor :: pGetTedWinByLocatedFragment( const LocatedFragment* pLocatedFragment) { TedWin* pTedWin; bool bFound = false; int nTedWin = 0; while( !bFound && nTedWin < dapTedWin_.length() ) { pTedWin = dapTedWin_[ nTedWin ]; if ( pLocatedFragment == pTedWin->pLocFragGet() ) bFound = true; else ++nTedWin; } if (bFound) return( pTedWin ); else return( NULL ); } void Teditor :: changeAllTedWinsScrollTogether( const bool bScrollTogether, GuiTedWin* pGuiTedWinScrollMaster) { // change scrolled together buttons on all tedwins bTedWinsScrollTogether_ = bScrollTogether; pGuiTedWinScrollMaster_ = pGuiTedWinScrollMaster; // change all buttons for( int n = 0; n < nNumberOfTedWins(); ++n ) { pGetTedWin( n )->pGetGuiTedWin()->changeGuiTedWinsScrollTogetherButtons( bTedWinsScrollTogether_ ); } if (bTedWinsScrollTogether_ ) { alignAllTracesToMaster(); refreshAllTedWins( false ); } } void Teditor :: alignAllTracesToMaster() { // scroll all teditors so that the 2 outermost bases line up for( int n = 0; n < nNumberOfTedWins(); ++n ) { GuiTedWin* pGuiTedWin = pGetTedWin( n )->pGetGuiTedWin(); if (pGuiTedWin != pGuiTedWinScrollMaster_ ) { pGuiTedWin->alignTraceToMaster(); } } } void Teditor :: alignToThisTraceIfScrollingTogether( TedWin* pTedWin ) { if ( bTedWinsScrollTogether() ) { setGuiTedWinScrollMaster( pTedWin->pGetGuiTedWin() ); alignAllTracesToMaster(); } } void Teditor :: changeToADifferentScrollMaster() { GuiTedWin* pOldGuiTedWinScrollMaster = pGuiTedWinScrollMaster_; GuiTedWin* pNewGuiTedWin; bool bFoundADifferentOne = false; int nGuiTedWin = 0; while( !bFoundADifferentOne && nGuiTedWin < dapTedWin_.length() ) { pNewGuiTedWin = pGetTedWin( nGuiTedWin )->pGetGuiTedWin(); if ( pNewGuiTedWin != pOldGuiTedWinScrollMaster ) bFoundADifferentOne = true; else ++nGuiTedWin; } if ( bFoundADifferentOne ) pGuiTedWinScrollMaster_ = pNewGuiTedWin; else pGuiTedWinScrollMaster_ = 0; } // draw cursor on all child TedWins void Teditor :: drawVerticalLineThroughTracesOfAllTedWins(const int nConsPos ) { for (int nTw = 0; nTw < dapTedWin_.length(); nTw++) { dapTedWin_[nTw]->drawVerticalLineThroughTraces( nConsPos ); } } // draw traces of all TedWins (for purpose of erasing vertical lines // through traces ) void Teditor :: drawTracesOfAllTedWins() { for (int nTw = 0; nTw < dapTedWin_.length(); nTw++) { dapTedWin_[nTw]->drawTraces( ); } } void Teditor :: deleteTedWinsIfTooMany( const int nNumberOfTedWinsToAdd ) { int nNumberToDelete = nNumberOfTedWinsToAdd + nNumberOfTedWins() - consedParameters::pGetConsedParameters()->nMaximumNumberOfTracesShown_; if (nNumberToDelete > 0 ) { for( int n = 1; n <= nNumberToDelete; ++n ) { GuiTedWin* pGuiTedWin = pGetTedWin( 0 )->pGetGuiTedWin(); pGuiTeditor_->removeGuiTedWin(pGuiTedWin ); } } } void Teditor :: raiseTracesWindow() { pGuiTeditor_->raiseTracesWindow(); } void Teditor :: prepareToShowAllTraces() { deleteAllTedWins(); pGuiTeditor_->createScrollingWindowForTraces(); } void Teditor :: deleteAllTedWins() { int nNumberToDelete = nNumberOfTedWins(); if ( nNumberToDelete > 0 ) { for( int n = 1; n <= nNumberToDelete; ++n ) { GuiTedWin* pGuiTedWin = pGetTedWin( 0 )->pGetGuiTedWin(); pGuiTeditor_->removeGuiTedWin( pGuiTedWin ); } } } void Teditor :: drawScaleAndConsensusForShowAllTraces() { pGuiTedWinScrollMaster_->pTedWin_->drawScale( true, // bDrawInShowAllTracesConsensusWindow true ); // bDrawConsensusPositions pGuiTedWinScrollMaster_->pTedWin_->drawConsensusBases(); } void Teditor :: userPushedUpDownArrowKey( const bool bUpNotDown ) { // move cursor to previous/next trace. // where is the current cursor? EditCursor* pEditCursor = pContigWinGet()->pGetEditCursor(); if ( !pEditCursor->bCursorValid() ) { popupErrorMessage( "Please first set the cursor by clicking on the edt line in the Traces Window" ); return; } // is it on a located fragment, and if so, on this located frag? if (! pEditCursor->bCursorOnFrag()) return; LocatedFragment* pLocFragOfCursor = pEditCursor->pLocFragGet(); // where is the cursor right now? bool bFoundCursor = false; int nReadWithCursor; for( int nRead = 0; nRead < dapTedWin_.length() && !bFoundCursor; ++nRead ) { TedWin* pTedWin = dapTedWin_[ nRead ]; if (pTedWin->pLocatedFragment_ == pLocFragOfCursor ) { bFoundCursor = true; nReadWithCursor = nRead; } } // I don't think this can happen, but handle it anyway if ( !bFoundCursor ) return; int nConsPos = pEditCursor->nConsPosGet(); // our goal in this loop is to get pNewLocFragOfCursor int nNewReadIndex = nReadWithCursor; LocatedFragment* pNewLocFragOfCursor = NULL; while( 1 ) { if ( bUpNotDown ) { --nNewReadIndex; if ( nNewReadIndex < 0 ) { GuiApp::beep(); return; } } else { ++nNewReadIndex; if ( nNewReadIndex >= dapTedWin_.length() ) { GuiApp::beep(); return; } } pNewLocFragOfCursor = dapTedWin_[ nNewReadIndex ]->pLocatedFragment_; // if the read is not at nConsPos, skip over that read if ( pNewLocFragOfCursor->nGetAlignStart() <= nConsPos && nConsPos <= pNewLocFragOfCursor->nGetAlignEnd() ) { break; // found a read at this cursor position } } pContigWinGet()->moveCursorToFragPos( nConsPos, pNewLocFragOfCursor ); // most importantly, scroll the traces window if necessary to see it. pGuiTeditor_->scrollScrollingWindow( nNewReadIndex, dapTedWin_.length() ); // move input focus TedWin* pNewTedWin = dapTedWin_[ nNewReadIndex ]; pNewTedWin->pGetGuiTedWin()->grabInputFocus(); } void Teditor :: showAllTraces( const int nInitialConsPos ) { nConsPos_ = nInitialConsPos; /* this assumes that pContigView_ has been set */ assert( pContigView_ ); bool bDisplayedSomeTraces; showAllTraces2( 0, // starting read line false, // bUpNotDown bDisplayedSomeTraces ); if ( !bDisplayedSomeTraces ) { popupErrorMessage( "no traces at this position" ); } } void Teditor :: showNextPrevTraces( const char cNextOrPrevTraces ) { assert( pContigView_ ); int nStartingReadLine; bool bUpNotDown; if ( cNextOrPrevTraces == cPrevTraces ) { nStartingReadLine = nTopDisplayedTrace_ - 1; bUpNotDown = true; } else if ( cNextOrPrevTraces == cNextTraces ) { nStartingReadLine = nBottomDisplayedTrace_ + 1; bUpNotDown = false; } else assert( false ); bool bDisplayedSomeTraces; showAllTraces2( nStartingReadLine, bUpNotDown, bDisplayedSomeTraces ); if ( !bDisplayedSomeTraces ) { GuiApp::beep(); } } void Teditor :: showAllTraces2( const int nStartingReadLine, const bool bUpNotDown, bool& bDisplayedSomeTraces ) { bDisplayedSomeTraces = false; int nTracesAttempted = 0; int nTracesActuallyDisplayed = 0; LocatedFragment* pBestRead = NULL; int nSumOfQualityValuesBestRead = 0; int nIncrement = ( bUpNotDown ? -1 : 1 ); RWTValOrderedVector aReadLinesToShow( (size_t) pCP->nShowAllTracesMaxNumberOfTracesToShowAtOnce_ ); for( int nReadLine = nStartingReadLine; 0 <= nReadLine && nReadLine <= ( pContigView_->nGetNumberOfFragments() - 1 ) && aReadLinesToShow.length() < pCP->nShowAllTracesMaxNumberOfTracesToShowAtOnce_; nReadLine += nIncrement ) { LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet( nReadLine ); bool bProblemReadingTraces = false; TedWin* pTedWin = NULL; try { // if we want to speed this up further, we could // not read the traces if there is a dataNeeded tag on pLocFrag->readTraces( false ); if ( pCP->bShowAllTracesJustShowGoodTraces_ && !pLocFrag->bOKToShowThisTraceInShowAllTraces( nConsPos_ ) ) continue; } catch(...) { // failed for any reason. Carry on. bProblemReadingTraces = true; } if ( bProblemReadingTraces ) continue; aReadLinesToShow.append( nReadLine ); // figure out which is the best read about this point const int nWindowAroundCursor = 20; int nConsPosStart = MAX( pLocFrag->nGetAlignStart(), nConsPos_ - nWindowAroundCursor ); int nConsPosEnd = MIN( pLocFrag->nGetAlignEnd(), nConsPos_ + nWindowAroundCursor ); // this will hold true if there is *some* bases within the window // above and the cons pos of interest lies within the read. // Thus the master trace must intersect the cursor. if ( nConsPosStart <= nConsPosEnd && nConsPosStart <= nConsPos_ && nConsPos_ <= nConsPosEnd ) { int nSumOfQualityValues = 0; for( int nConsPos = nConsPosStart; nConsPos <= nConsPosEnd; ++nConsPos ) { Quality q = pLocFrag->ntGetFragFromConsPos( nConsPos ).qualGetQuality(); nSumOfQualityValues += q; } // changed from > to >= in case all reads are quality 0 if ( nSumOfQualityValues >= nSumOfQualityValuesBestRead ) { nSumOfQualityValuesBestRead = nSumOfQualityValues; pBestRead = pLocFrag; } } // if ( nConsPosStart <= nConsPosend ) ... } if ( aReadLinesToShow.length() == 0 ) { bDisplayedSomeTraces = false; return; } assert( pBestRead ); // now we have a list of the traces to display // If we are scrolling up, the list is in reverse order so // reverse it: if ( bUpNotDown ) aReadLinesToShow.reverseElementOrder(); // remove any old traces (possibly none if this is the first // call-up of traces removeAllTraces(); // now show the traces TedWin* pBestTedWin = NULL; Dimension dimLastHeightOfTraceWindow = 0; bool bTooManyTraces = false; for( int nIndex = 0; nIndex < aReadLinesToShow.length(); ++nIndex ) { int nReadLine = aReadLinesToShow[ nIndex ]; LocatedFragment* pLocFrag = pContigView_->pLocatedFragmentGet( nReadLine ); // does this need a try/catch wrapper around it? TedWin* pTedWin = new TedWin( this, nConsPos_, pLocFrag, 0, // ignored true ); // bAddToScrollingWindow if ( pBestRead == pLocFrag ) pBestTedWin = pTedWin; // check that we haven't reached the limit of traces to // put into a scrolling window at once. We do this by // checking that the widRowCol_ widget is continuing to grow // larger. if ( !bTooManyTraces ) { Dimension dimHeight; XtVaGetValues( pGuiTeditor_->widRowCol_, XmNheight, &dimHeight, NULL ); if ( dimHeight == dimLastHeightOfTraceWindow ) { // record how many traces actually are displayed bTooManyTraces = true; nTracesActuallyDisplayed = nIndex + 1; } } } // for( int nIndex = 0; assert( pBestTedWin ); // sets pixel positions for bases of master so other reads // can align to it (added July 2004) pBestTedWin->scrollToConsPosInCenter( nConsPos_ ); alignToThisTraceIfScrollingTogether( pBestTedWin ); pBestTedWin->pGuiTedWin_->guiTedWinResetSlider( 0, // initial point position 0, // minimum point position pBestTedWin->nGetNumberOfPoints(), // maximum point position pBestTedWin->pGuiTedWin_->nGetTraceWindowPointsWide() ); // number of points displayed at once pBestTedWin->pGuiTedWin_->alignScrollingWindowConsensusToThisTrace(); drawScaleAndConsensusForShowAllTraces(); raiseTracesWindow(); pGuiTeditor_->setVerticalScrollBarIncrement( dapTedWin_.length() ); if ( bTooManyTraces ) { RWCString soErrorMessage = "There were more traces than could be displayed. Consed could only display "; soErrorMessage += RWCString( (long) nTracesActuallyDisplayed ); soErrorMessage += " out of "; soErrorMessage += RWCString( (long) nTracesAttempted ); soErrorMessage += " attempted. To show more traces, on the Main Consed Window, point to the Options menu, hold down the left mouse button and release on General Preferences. Then reduce the number next to \"Initial Height of Traces Area in Pixels\" Click \"Apply and Dismiss\" on the General Preferences Window. Then dismiss the Traces Window and bring it up again."; popupErrorMessage( soErrorMessage ); } // save for next prev/next: nTopDisplayedTrace_ = aReadLinesToShow[0]; nBottomDisplayedTrace_ = aReadLinesToShow[ aReadLinesToShow.length() - 1 ]; // inform user what is being displayed RWCString soMessage = "showing read lines "; soMessage += RWCString( (long) nTopDisplayedTrace_ ); soMessage += "-"; soMessage += RWCString( (long) nBottomDisplayedTrace_ ); soMessage += " out of 0-"; soMessage += RWCString( (long) ( pContigView_->nGetNumberOfFragments() - 1 ) ); pGuiTeditor_->displayStatusMessage( soMessage ); bDisplayedSomeTraces = true; } // void Teditor :: showAllTraces2 void Teditor :: removeAllTraces() { for( int nTedWin = nNumberOfTedWins() - 1; nTedWin >= 0; --nTedWin ) { GuiTedWin* pGuiTedWin = pGetTedWin( nTedWin )->pGetGuiTedWin(); pGuiTeditor_->removeGuiTedWin( pGuiTedWin ); } } void Teditor :: reAlignTracesSinceCursorChanged( LocatedFragment* pLocFrag ) { // try to find a trace corresponding to this read (the user // might not have the traces for this read displayed ) TedWin* pTedWin = pGetTedWinByLocatedFragment( pLocFrag ); if ( !pTedWin ) pTedWin = pGetGuiTedWinScrollMaster()->pTedWinGet(); else { setGuiTedWinScrollMaster( pTedWin->pGetGuiTedWin() ); if ( pGuiTeditor_->bShowTracesInScrollingWindow_ ) pTedWin->pGetGuiTedWin()->alignScrollingWindowConsensusToThisTrace(); } if ( bTedWinsScrollTogether() ) { alignAllTracesToMaster(); refreshAllTedWins( false ); } } void Teditor :: loadConsensusGuiColorTextArray( const int nConsPosMin, const int nConsPosMax ) { aConsensusGuiColorTextArray_.resizeButNotSmaller( nConsPosMax - nConsPosMin + 1 ); aConsensusGuiColorTextArray_.nOffset_ = nConsPosMin; // if we shouldn't show tags, load the array with nulls if ( !pContigWinGet()->bMustLoadGuiColorTextArrayForTags() ) return; Contig* pContig = pContigWinGet()->pContig_; 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 nDrawTagConsPosLeft; int nDrawTagConsPosRight; if ( !pTag->bTagIsOnScreen( nConsPosMin, nConsPosMax, nDrawTagConsPosLeft, nDrawTagConsPosRight ) ) continue; // if reached here, the tag is visible for( int nConsPos = nDrawTagConsPosLeft; nConsPos <= nDrawTagConsPosRight; ++nConsPos ) { if ( aConsensusGuiColorTextArray_[ nConsPos ] ) { // there is another tag at this position aConsensusGuiColorTextArray_[ nConsPos ] = tagTypes::pGetTagTypes()->pGetGuiColorTextForTagsOverlap(); } else { // there is not another tag at this position (at least // we haven't found one so far) bool bAt3PrimeEndOfOligoTag = false; if ( pTag->soType_ == "oligo" ) { if ( ((oligoTag*) pTag)->bIsThisThe3PrimeEnd( nConsPos ) ) { aConsensusGuiColorTextArray_[ nConsPos ] = tagTypes::pGetTagTypes()->pGetGuiColorTextForTagType( "oligo3PrimeEnd" ); bAt3PrimeEndOfOligoTag = true; } } if ( !bAt3PrimeEndOfOligoTag ) { aConsensusGuiColorTextArray_[ nConsPos ] = tagTypes::pGetTagTypes()->pGetGuiColorTextForTag( pTag ); } } } } }