/*****************************************************************************
#   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    "miniassembly.h"
#include    <Xm/ScrolledW.h>
#include    <Xm/Form.h>
#include    <Xm/TextF.h>
#include    <Xm/Label.h>
#include    <Xm/Xm.h>
#include    <Xm/PushB.h>
#include    <Xm/List.h>
#include    <Xm/RowColumn.h>
#include    <Xm/ToggleB.h>
#include    "popupErrorMessage2.h"
#include    "popupErrorMessage.h"
#include    "guiapp.h"
#include    "handleWindowManagerDelete2.h"
#include    "hp_exception_kludge.h"
#include    "assembly.h"
#include    "consed.h"
#include    "filGetUniqueFilename.h"
#include    "consedParameters.h"
#include    "soGetErrno.h"
#include    "popupInfoMessage.h"
#include    "bIsNumeric.h"
#include    "guiMultiContigNavigator.h"
#include    "bGuiGetAnswerYesNo.h"
#include    "nCompareContigsByName.h"
#include    <unistd.h>
#include    <sys/wait.h>
#include    <signal.h>
#include    "rwctokenizer.h"
#include    "bIsNumericMaybeWithWhitespace.h"
#include    "intXtPointerUnion.h"
#include    "textbox.h"
#include    "phdBall2Fasta.h"




static void cbClearHighlightedContigsInRightListButton( Widget wid,
                                                      XtPointer pClientData,
                                                      XtPointer pCallData ) {

   miniassembly* pMiniassembly = (miniassembly*) pClientData;
   TRY_CATCH_WRAPPER( pMiniassembly->userPressedClearHighlightedContigsInRightListButton() );
}



static void cbClearRightListButton( Widget wid,
                                    XtPointer pClientData,
                                    XtPointer pCallData ) {

   miniassembly* pMiniassembly = (miniassembly*) pClientData;
   TRY_CATCH_WRAPPER( pMiniassembly->userPressedClearRightListButton() );
}


static void cbCancelButton( Widget wid,
                     XtPointer pClientData,
                     XtPointer pCallData ) {

   miniassembly* pMiniassembly = (miniassembly*) pClientData;
   TRY_CATCH_WRAPPER( delete pMiniassembly; );
}

static void cbReassembleButton( Widget wid,
                         XtPointer pClientData,
                         XtPointer pCallData ) {

   miniassembly* pMiniassembly = (miniassembly*) pClientData;
   TRY_CATCH_WRAPPER( pMiniassembly->userPushedReassembleButton() );
   
}
                         

static void cbMoveHighlightedToRight( Widget wid,
                                      XtPointer pClientData,
                                      XtPointer pCallData ) {

   miniassembly* pMiniassembly = (miniassembly*) pClientData;
   TRY_CATCH_WRAPPER( pMiniassembly->userPushedMoveHighlightedToRight() );

}



miniassembly :: miniassembly() : bPutEachSingletIntoItsOwnContig_( false )  {

   aContigsToReassemble_.soName_ = "miniassembly::aContigsToReassemble_";
   aReadsToReassemble_.soName_ = "miniassembly::aReadsToReassemble_";

}



void miniassembly :: createWindow() {


   widPopupShell_ = XtVaCreatePopupShell( 
      "miniassembly",
      topLevelShellWidgetClass,
      GAPP->widGetTopLevel(),
      XmNtitle, "Reassemble Some Contigs",
      XmNnoResize, False,
      XmNtransient, False,
      XmNdeleteResponse, XmDO_NOTHING,
      NULL );

   handleWindowManagerDelete2( widPopupShell_,
                               cbCancelButton,
                               this );

   Widget widForm = XtVaCreateManagedWidget(
      "form",
      xmFormWidgetClass,
      widPopupShell_,
      XmNshadowThickness, 0,
      XmNborderWidth, 0,
      NULL );

   Widget widAllContigsLabel = XtVaCreateManagedWidget(
      "All Contigs:",
      xmLabelWidgetClass,
      widForm,
      XmNtopAttachment, XmATTACH_FORM,
      XmNtopOffset, 20,
      XmNleftAttachment, XmATTACH_FORM,
      XmNbottomOffset, 0,
      NULL );

   Widget widContigsToReassembleLabel = XtVaCreateManagedWidget(
      "Contigs to Reassemble:",
      xmLabelWidgetClass,
      widForm,
      XmNtopAttachment, XmATTACH_FORM,
      XmNtopOffset, 20,
      XmNleftAttachment, XmATTACH_POSITION,
      XmNleftPosition, 50,
      XmNbottomOffset, 0,
      NULL );

   
   Widget widConnectToWidgetOnTop = widContigsToReassembleLabel;

   // now build up widgets from the bottom of the window

   widReassembleButton_ = XtVaCreateManagedWidget(
      "Reassemble",
      xmPushButtonWidgetClass,
      widForm,
      XmNbottomAttachment, XmATTACH_FORM,
      XmNbottomOffset, 10,
      XmNleftAttachment, XmATTACH_POSITION,
      XmNleftPosition, 20,
      XmNrightAttachment, XmATTACH_POSITION,
      XmNrightPosition, 40,
      NULL );

   XtAddCallback( widReassembleButton_,
                  XmNactivateCallback,
                  cbReassembleButton,
                  this );

   Widget widCancelButton = XtVaCreateManagedWidget(
      "Cancel",
      xmPushButtonWidgetClass,
      widForm,
      XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
      XmNbottomWidget, widReassembleButton_,
      XmNleftAttachment, XmATTACH_POSITION,
      XmNleftPosition, 60,
      XmNrightAttachment, XmATTACH_POSITION,
      XmNrightPosition, 80,
      NULL );

   XtAddCallback( widCancelButton,
                  XmNactivateCallback,
                  cbCancelButton,
                  this );

   Widget widConnectToWidgetOnBottom = widReassembleButton_;



   Widget widRadioBox = XmCreateRadioBox( widForm,
                                          "radio",
                                          NULL,
                                          0 );

   XtVaSetValues( widRadioBox,
                  XmNbottomAttachment, XmATTACH_WIDGET,
                  XmNbottomWidget, widConnectToWidgetOnBottom,
                  XmNbottomOffset, 20,
                  XmNleftAttachment, XmATTACH_FORM,
                  XmNleftOffset, 10,
                  XmNorientation, XmHORIZONTAL,
                  NULL );

   widConnectToWidgetOnBottom = widRadioBox;

   widPutIntoSeparateContigs_ = XtVaCreateManagedWidget(
       "Put into separate contigs",
       xmToggleButtonWidgetClass,
       widRadioBox,
       XmNset, True, // default
       NULL );

   Widget widDiscard = XtVaCreateManagedWidget(
       "Discard from assembly",
       xmToggleButtonWidgetClass,
       widRadioBox,
       XmNset, False,
       NULL );

   XtManageChild( widRadioBox );

   Widget widWhatToDoWithSinglets = XtVaCreateManagedWidget(
      "What to do with miniassembly singlet reads:",
      xmLabelWidgetClass,
      widForm,
      XmNbottomAttachment, XmATTACH_WIDGET,
      XmNbottomWidget, widConnectToWidgetOnBottom,
      XmNleftAttachment, XmATTACH_FORM,
      XmNleftOffset, 10,
      NULL );

   widConnectToWidgetOnBottom = widWhatToDoWithSinglets;



   Widget widPhrapOptionsLabel = XtVaCreateManagedWidget(
       "other phrap options:",
       xmLabelWidgetClass,
       widForm,
       XmNleftAttachment, XmATTACH_FORM,
       XmNleftOffset, 10,
       NULL );

   Dimension dimWidth;
   XtVaGetValues( widPhrapOptionsLabel, XmNwidth, &dimWidth, NULL );

   widOtherPhrapOptions_ = XtVaCreateManagedWidget(
       "phrap options",
       xmTextFieldWidgetClass,
       widForm,
       XmNbottomAttachment, XmATTACH_WIDGET,
       XmNbottomWidget, widConnectToWidgetOnBottom,
       XmNbottomOffset, 20,
       XmNleftAttachment, XmATTACH_FORM,
       XmNleftOffset, dimWidth + 10,
       NULL );

   XtVaSetValues( widPhrapOptionsLabel,
                  XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
                  XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
                  XmNtopWidget, widOtherPhrapOptions_,
                  XmNbottomWidget, widOtherPhrapOptions_,
                  NULL );
   
   widConnectToWidgetOnBottom = widOtherPhrapOptions_;


   Widget widMinScoreLabel = XtVaCreateManagedWidget(
       "-minscore ",
       xmLabelWidgetClass,
       widForm,
       XmNleftAttachment, XmATTACH_FORM,
       XmNleftOffset, 10,
       NULL );

   XtVaGetValues( widMinScoreLabel, XmNwidth, &dimWidth, NULL );

   widMinScore_ = XtVaCreateManagedWidget(
       "minscore",
       xmTextFieldWidgetClass,
       widForm,
       XmNbottomAttachment, XmATTACH_WIDGET,
       XmNbottomWidget, widConnectToWidgetOnBottom,
       XmNbottomOffset, 10,
       XmNleftAttachment, XmATTACH_FORM,
       XmNleftOffset, dimWidth + 10,
       XmNcolumns, 4,
       NULL );

   XtVaSetValues( widMinScoreLabel,
                  XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
                  XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
                  XmNtopWidget, widMinScore_,
                  XmNbottomWidget, widMinScore_,
                  NULL );

   Widget widMinMatchLabel = XtVaCreateManagedWidget( 
       "-minmatch",
       xmLabelWidgetClass,
       widForm,
       XmNleftAttachment, XmATTACH_WIDGET,
       XmNleftWidget, widMinScore_,
       XmNleftOffset, 20,
       NULL );

   XtVaGetValues( widMinMatchLabel, XmNwidth, &dimWidth, NULL );

   widMinMatch_ = XtVaCreateManagedWidget(
       "minmatch",
       xmTextFieldWidgetClass,
       widForm,
       XmNbottomAttachment, XmATTACH_WIDGET,
       XmNbottomWidget, widConnectToWidgetOnBottom,
       XmNbottomOffset, 10,
       XmNleftAttachment, XmATTACH_WIDGET,
       XmNleftWidget, widMinScore_,
       XmNleftOffset, 20 + dimWidth + 10,
       XmNcolumns, 4,
       NULL );

   XtVaSetValues( widMinMatchLabel,
                  XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
                  XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
                  XmNtopWidget, widMinMatch_,
                  XmNbottomWidget, widMinMatch_,
                  NULL );

   Widget widForceLevelLabel = XtVaCreateManagedWidget(
       "-forcelevel",
       xmLabelWidgetClass,
       widForm,
       XmNleftAttachment, XmATTACH_WIDGET,
       XmNleftWidget, widMinMatch_,
       XmNleftOffset, 20,
       NULL );
   
   XtVaGetValues( widForceLevelLabel, XmNwidth, &dimWidth, NULL );

   widForceLevel_ = XtVaCreateManagedWidget(
       "forcelevel",
       xmTextFieldWidgetClass,
       widForm,
       XmNbottomAttachment, XmATTACH_WIDGET,
       XmNbottomWidget, widConnectToWidgetOnBottom,
       XmNbottomOffset, 10,
       XmNleftAttachment, XmATTACH_WIDGET,
       XmNleftWidget, widMinMatch_,
       XmNleftOffset, 20 + dimWidth + 10,
       XmNcolumns, 4,
       NULL );

   XtVaSetValues( widForceLevelLabel,
                  XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
                  XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET,
                  XmNtopWidget, widForceLevel_,
                  XmNbottomWidget, widForceLevel_,
                  NULL );

   widConnectToWidgetOnBottom = widForceLevel_;

   Widget widClearHighlighted = XtVaCreateManagedWidget(
       "Clear Highlighted",
       xmPushButtonWidgetClass,
       widForm,
       XmNbottomAttachment, XmATTACH_WIDGET,
       XmNbottomWidget, widConnectToWidgetOnBottom,
       XmNbottomOffset, 10,
       XmNleftAttachment, XmATTACH_POSITION,
       XmNleftPosition, 60,
       NULL );

   XtAddCallback( widClearHighlighted,
                  XmNactivateCallback,
                  cbClearHighlightedContigsInRightListButton,
                  this );
   
   widConnectToWidgetOnBottom = widClearHighlighted;

   Widget widMoveHighlightedToRight = XtVaCreateManagedWidget(
       "Move Highlighted To Right",
       xmPushButtonWidgetClass,
       widForm,
       XmNbottomAttachment, XmATTACH_WIDGET,
       XmNbottomWidget, widConnectToWidgetOnBottom,
       XmNbottomOffset, 0,
       XmNleftAttachment, XmATTACH_FORM,
       XmNleftOffset, 10,
       NULL );

   XtAddCallback( widMoveHighlightedToRight,
                  XmNactivateCallback,
                  cbMoveHighlightedToRight,
                  this );

   Widget widClearList = XtVaCreateManagedWidget(
       "Clear List",
       xmPushButtonWidgetClass,
       widForm,
       XmNbottomAttachment, XmATTACH_WIDGET,
       XmNbottomWidget, widConnectToWidgetOnBottom,
       XmNbottomOffset, 0,
       XmNleftAttachment, XmATTACH_POSITION,
       XmNleftPosition, 60,
       NULL );

   XtAddCallback( widClearList,
                  XmNactivateCallback,
                  cbClearRightListButton,
                  this );

   widConnectToWidgetOnBottom = widMoveHighlightedToRight;


   const int nVisibleItemCount = 20;


   int nArgs;
   Arg aArg[50];

   nArgs = 0;
   XtSetArg( aArg[ nArgs ], XmNtopAttachment, XmATTACH_WIDGET ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNtopWidget, widConnectToWidgetOnTop ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNtopOffset, 10 ); ++nArgs;

   XtSetArg( aArg[ nArgs ], XmNbottomAttachment, XmATTACH_WIDGET ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNbottomWidget, widConnectToWidgetOnBottom ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNbottomOffset, 10 ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNleftAttachment, XmATTACH_FORM ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNrightAttachment, XmATTACH_POSITION ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNrightPosition, 45 ); ++nArgs;

   XtSetArg( aArg[ nArgs ], XmNvisibleItemCount, nVisibleItemCount ); ++nArgs;
   XtSetArg( aArg[nArgs], XmNselectionPolicy, XmEXTENDED_SELECT); nArgs++;

   widAllContigs_ = XmCreateScrolledList( 
      widForm,
      "all_contigs",
      aArg,
      nArgs );

   XtManageChild( widAllContigs_ );

   
   nArgs = 0;
   XtSetArg( aArg[ nArgs ], XmNtopAttachment, XmATTACH_WIDGET ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNtopWidget, widConnectToWidgetOnTop ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNtopOffset, 10 ); ++nArgs;

   XtSetArg( aArg[ nArgs ], XmNbottomAttachment, XmATTACH_WIDGET ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNbottomWidget, widConnectToWidgetOnBottom ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNbottomOffset, 10 ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNleftAttachment, XmATTACH_POSITION ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNleftPosition, 50 ); ++nArgs;
   XtSetArg( aArg[ nArgs ], XmNrightAttachment, XmATTACH_FORM ); ++nArgs;

   XtSetArg( aArg[ nArgs ], XmNvisibleItemCount, nVisibleItemCount ); ++nArgs;
   XtSetArg( aArg[nArgs], XmNselectionPolicy, XmEXTENDED_SELECT); nArgs++;

   widContigsToReassemble_ = XmCreateScrolledList(
      widForm,
      "contigs_to_reassemble",
      aArg,
      nArgs );

   XtManageChild( widContigsToReassemble_ );


   putContigsInLeftContigsList();
   putContigsInRightContigsList();





   
   XtPopup( widPopupShell_, XtGrabNone );
}


miniassembly :: ~miniassembly() {

   XtPopdown( widPopupShell_ );
   XtDestroyWidget( widPopupShell_ );

}


void miniassembly :: putContigsInLeftContigsList() {

   Assembly* pAssembly = ConsEd::pGetAssembly();

   XmStringTable  xmStringList = (XmStringTable) XtMalloc(
      pAssembly->nNumContigs() * sizeof( XmString * ) );

   RWCString soLineToDisplay( (size_t) 100 );

   int nContig;
   for( nContig= 0; nContig < pAssembly->nNumContigs(); ++nContig ) {
      Contig* pContig = pAssembly->pGetContig( nContig );
      soLineToDisplay = pContig->soGetFancyName();
      int nNumberOfReads = pContig->nGetNumberOfFragsInContig();

      if ( nNumberOfReads == 1 ) {
         soLineToDisplay += ": ";
         LocatedFragment* pLocFrag = pContig->pLocatedFragmentGet( 0 );
         soLineToDisplay += pLocFrag->soGetName();
      }
      else {
         int nContigLength = pContig->nGetUnpaddedSeqLength();

         soLineToDisplay.increaseMaxLengthIfNecessary( 100 );
         soLineToDisplay.appendFormat( " (%d reads, %d bps)",
                                       nNumberOfReads,
                                       nContigLength );
      }

      xmStringList[nContig] = XmStringCreateSimple( soLineToDisplay.data() );
   }

   XtVaSetValues( widAllContigs_,
                  XmNitems, xmStringList,
                  XmNitemCount, pAssembly->nNumContigs(),
                  XmNtopItemPosition, pAssembly->nNumContigs(),
                  NULL );

   // free all the allocated memory

   for( nContig = 0; nContig < pAssembly->nNumContigs(); ++nContig ) {
      XmStringFree( xmStringList[ nContig ] );
   }

   XtFree( (char*) xmStringList );

}




void miniassembly :: putContigsInRightContigsList() {

   for( int nContig = 0; 
        nContig < pCP->aListOfContigNamesForMiniassembly_.length();
        ++nContig ) {

      RWCString soContigName = pCP->aListOfContigNamesForMiniassembly_[ nContig ];
      Contig* pContig = ConsEd::pGetAssembly()->pGetContigByName( soContigName );
      if ( pContig ) {
         aContigsToReassemble_.insert( pContig );
      }
   }

   refreshRightList();
}



void miniassembly :: userPushedMoveHighlightedToRight() {

   // which reads are highlighted?

   int* pPositionList;
   int nNumberOfSelectedItems;
   if ( !XmListGetSelectedPos( widAllContigs_,
                               &pPositionList,
                               &nNumberOfSelectedItems ) ) {
      nNumberOfSelectedItems = 0;
   }

   if ( nNumberOfSelectedItems == 0 ) {
      popupErrorMessage2( widPopupShell_, "You must select the contigs you want to move to the right list.  See instructions by clicking on 'help'" );
      XtFree( (char*) pPositionList );
      return;
   }

   for( int nSelectedContig = 0; nSelectedContig < nNumberOfSelectedItems;
        ++nSelectedContig ) {

      int nOneBasedInXmList = pPositionList[ nSelectedContig ];

      // convert to zero-based, which is the index of Assembly::pGetContig

      Contig* pContig = 
         ConsEd::pGetAssembly()->pGetContig( nOneBasedInXmList - 1 );

      // add this to the right list

      aContigsToReassemble_.insert( pContig );
   }

   refreshRightList();


}



void miniassembly :: refreshRightList() {

   
   XmStringTable xmStringList = (XmStringTable) XtMalloc( 
      aContigsToReassemble_.length() * sizeof( XmString * ) );

   RWCString soLineToDisplay( (size_t) 100 );
   
   int nContig;
   for( nContig = 0; nContig < aContigsToReassemble_.length(); 
        ++nContig ) {

      Contig* pContig = aContigsToReassemble_[ nContig ];
      
      soLineToDisplay = pContig->soGetFancyName();

      int nNumberOfReads = pContig->nGetNumberOfFragsInContig();
      if ( nNumberOfReads == 1 ) {
         soLineToDisplay += ": ";
         LocatedFragment* pLocFrag = pContig->pLocatedFragmentGet( 0 );
         soLineToDisplay += pLocFrag->soGetName();
      }
      else {
         int nContigLength = pContig->nGetUnpaddedSeqLength();
         
         soLineToDisplay.increaseMaxLengthIfNecessary( 100 );
         soLineToDisplay.appendFormat( " (%d reads, %d bps)",
                                       nNumberOfReads,
                                       nContigLength );
      }

      xmStringList[nContig] = XmStringCreateSimple( soLineToDisplay.data() );
   }

   XtVaSetValues( widContigsToReassemble_,
                  XmNitems, xmStringList,
                  XmNitemCount, aContigsToReassemble_.length(),
                  XmNtopItemPosition, aContigsToReassemble_.length(),
                  NULL );

   // free all the allocated memory

   for( nContig = 0; nContig < aContigsToReassemble_.length(); ++nContig ) {
      XmStringFree( xmStringList[ nContig ] );
   }

   XtFree( (char*) xmStringList );
}


      
void miniassembly :: userPressedClearRightListButton() {

   aContigsToReassemble_.clear();
   refreshRightList();
}


void miniassembly :: userPressedClearHighlightedContigsInRightListButton() {

   // which contigs are highlighted?

   int* pPositionList;
   int nNumberOfSelectedItems;
   if ( !XmListGetSelectedPos( widContigsToReassemble_,
                               &pPositionList,
                               &nNumberOfSelectedItems ) ) {
      nNumberOfSelectedItems = 0;
   }

   if ( nNumberOfSelectedItems == 0 ) {
      popupErrorMessage2( widPopupShell_, "You must first select the contigs you want to remove from the right list.  See instructions by clicking on 'help'" );
      XtFree( (char*) pPositionList );
      return;
   }

   // must go in reverse order or else bug in that the list positions
   // will have changed and the list position is no longer nOneBasedInXmList - 1
   for( int nSelectedContig = nNumberOfSelectedItems - 1; nSelectedContig >= 0;
        --nSelectedContig ) {
      
      int nOneBasedInXmList = pPositionList[ nSelectedContig ];
      
      // convert to zero-based, which is the index of aContigsToReassemble_

      aContigsToReassemble_.removeAt( nOneBasedInXmList - 1 );
   }

   refreshRightList();
}





void miniassembly :: userPushedReassembleButton() {


   Assembly* pAssembly = ConsEd::pGetAssembly();

   if ( aContigsToReassemble_.length() == 0 ) {
      popupErrorMessage2( widPopupShell_,
                          "You have not specified any contigs to be reassembled.  Do so by adding contigs to the right list." );
      return;
   }


   if ( !bUserEnteredFieldsAreOK() ) return;

   if ( !bPhredPhrapVersionIsOK() ) return;

   if ( pAssembly->bChanged() ) {
      popupErrorMessage2( widPopupShell_,
                          "You must first save the assembly before making a miniassembly" );
      return;
   }


   // I'm concerned that there may be duplicate contigs in the right list
   
   // So sort them and eliminate dupicates

   qsort( aContigsToReassemble_.data(),
          aContigsToReassemble_.length(),
          sizeof( Contig* ),
          ( (int(*) (const void*, const void* ) ) nCompareContigsByName ) );
   // nCmpContigsByName is in assembly3.cpp

   // remove duplicates

   bool bDuplicatesRemoved = false;
   RWCString soMessage = "removed duplicate contigs: ";
   int nContig;
   for( nContig = aContigsToReassemble_.length() - 1; nContig >= 1;
        --nContig ) {

      Contig* pContigBefore = aContigsToReassemble_[ nContig - 1 ];
      Contig* pContigAfter = aContigsToReassemble_[ nContig ];

      if ( pContigBefore == pContigAfter ) {
         soMessage += pContigAfter->soGetName();
         soMessage += " ";
         bDuplicatesRemoved = true;
         aContigsToReassemble_.removeAt( nContig );
      }
   }

   if ( bDuplicatesRemoved ) {
      popupInfoMessage( widPopupShell_, soMessage );
   }

   // now keep a list of reads that are to be reassembled--this will be
   // useful to see if any of these reads are in singlets

   int nNumberOfReadsToReassemble = 0;
   for( nContig = 0; nContig < aContigsToReassemble_.length(); ++nContig ) {
      Contig* pContig = aContigsToReassemble_[ nContig ];
      nNumberOfReadsToReassemble += pContig->nGetNumberOfFragsInContig(); 
   }




   RWCString soMiniProjectName = filGetUniqueFilename( "mini" );

   FileName filFastaFileOfReads = soMiniProjectName + ".fasta.screen";
   FileName filFastaQual = filFastaFileOfReads + ".qual";

   FILE* pFasta = fopen( filFastaFileOfReads.data(), "w" );
   if ( !pFasta ) {
      THROW_FILE_ERROR( filFastaFileOfReads );
   }

   FILE* pQual = fopen( filFastaQual.data(), "w" );
   if ( !pQual ) {
      THROW_FILE_ERROR( filFastaQual );
   }


   RWTValSortedVector<RWCString> aPhdBallsNeeded;

   
   for( nContig = 0; nContig < aContigsToReassemble_.length(); ++nContig ) {

      Contig* pContig = aContigsToReassemble_[ nContig ];

      for( int nRead = 0; nRead < pContig->nGetNumberOfFragsInContig();
           ++nRead ) {

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

         if ( ! pLocFrag->filPhdBall_.isNull() ) {

            FileName filPhdBall = pLocFrag->filPhdBall_;

            if ( ! aPhdBallsNeeded.bContains( filPhdBall ) ) {
               aPhdBallsNeeded.insert( filPhdBall );
            }
         }


         pLocFrag->writeReadToFasta( pFasta, pQual,
                                     false ); // bUseSeqPhredCalledBases --use edited bases

      }
   }

   fclose( pFasta );
   fclose( pQual );


   // write file of phd balls

   FileName filPhdBallsNeeded = soMiniProjectName + ".phdballs.fof";
   FILE* pPhdBalls = fopen( filPhdBallsNeeded.data(), "w" );
   if ( !pPhdBalls ) {
      THROW_FILE_ERROR( filPhdBallsNeeded );
   }

   for( int nPhdBall = 0; nPhdBall < aPhdBallsNeeded.length(); ++nPhdBall ) {

      if ( aPhdBallsNeeded[ nPhdBall ] == "phd.ball" ) {
         fprintf( pPhdBalls, "phd.ball\n" );
      }
      else {

         FileName filPhdBallFullPath = pCP->filPhdBallDirectory_ + "/" +
            aPhdBallsNeeded[ nPhdBall ];

         fprintf( pPhdBalls, "%s\n", filPhdBallFullPath.data() );
      }
   }

   fclose( pPhdBalls );



   RWCString soCommand =
      pCP->filFullPathnameOfMiniassemblyScript_;

   soCommand += " ";
   soCommand += soMiniProjectName;
   soCommand += " -miniassembly";
   soCommand += " -phdballs ";
   soCommand += filPhdBallsNeeded;
   soCommand += " -file_with_new_ace_file_name ";
   
   FileName filLookHereForAceFileName = filGetUniqueFilename( "mini" ) + ".fof";

   soCommand += filLookHereForAceFileName;

   soCommand += " -ace_file_with_consensus_tags ";
   soCommand += pAssembly->soGetAceFileName();

   soCommand += " ";
   soCommand += soPhrapOptions_;

   // run phredPhrap

   cerr << "running: " << soCommand << endl;

   runCommandAndCreateTerminateButton( soCommand );


   // now comes the hard part--integrating the 2 assemblies

   // what is the name of the new ace file?  The answer is in
   // filLookHereForAceFileName

   FILE* pFOF = fopen( filLookHereForAceFileName.data(), "r" );
   if ( !pFOF ) {
      RWCString soErrorMessage = "phredPhrap was supposed to put the name of the new ace file in a file called ";
      soErrorMessage += filLookHereForAceFileName;
      soErrorMessage += ".  However, this file cannot be opened.  Thus we cannot merge this new assembly.";
      popupErrorMessage2( widPopupShell_, soErrorMessage.data() );
      return;
   }

   const int nMaxLineSize = 200;
   char szAceFileName[ nMaxLineSize + 1];
   if ( fgets( szAceFileName, nMaxLineSize, pFOF ) == NULL ) {
      RWCString soErrorMessage = "The file ";
      soErrorMessage += filLookHereForAceFileName;
      soErrorMessage += " was supposed to contain the name of the new ace file but instead it is empty. (If you terminated the assembly, that would be the cause, so don't worry.)";
      popupErrorMessage2( widPopupShell_, soErrorMessage.data() );
      return;
   }


   fclose( pFOF );

   RWCString soNewAceFileName( szAceFileName );
   // I think there is a \n at the end of it, so cut that off.
   
   soNewAceFileName.stripTrailingWhitespaceFast();
   // just in case the user is using a custom phredPhrap script that
   // puts in leading whitespace
   soNewAceFileName = soNewAceFileName.stripWhitespace( RWCString::LEADING );
   

   Assembly *pMiniAssembly = new Assembly( soNewAceFileName,
                                           false ); // bSetGlobalAssembly

   int nNumberOfRowsNeeded = pMiniAssembly->nNumContigs() + 6;

   if ( nNumberOfRowsNeeded > 20 )
      nNumberOfRowsNeeded = 20;

   TextBox* pTextBox = 
      new TextBox( "Miniassembly Contigs", nNumberOfRowsNeeded );


   for( nContig = 0; nContig < pMiniAssembly->nNumContigs(); ++nContig ) {
      Contig* pContig = pMiniAssembly->pGetContig( nContig );

      RWCString soLine = "one contig with " + 
         RWCString( (long) pContig->nGetNumberOfFragsInContig() ) +
         " reads\n";
      pTextBox->append( soLine );
   }
   
   pTextBox->makeVisible();

   
   RWCString soQuestion( (size_t) 1000 );

   soQuestion = "new miniassembly contains ";
   soQuestion += RWCString( (long) pMiniAssembly->nNumContigs() );
   soQuestion += " contig(s) as in other box that just popped up\n";

   soQuestion += "Are you finished mini-assembling these contigs? (yes) (If you answer \"no\", you may reassemble them again.)";

   if ( !bGuiGetAnswerYesNo( soQuestion ) ) {
      delete pMiniAssembly;
      return;
   }


   // if don't do this, get segmentation fault because the 
   // template arrays will no longer be valid but assemblyView (et al)
   // will try to use them
   ConsEd::pGetConsEd()->whatToDoBeforeModifyAssembly();


   // compare the new list of reads to the old to see if they are the same


   // first count the number of reads in the new assembly
   int nNumberOfReadsInNewAssembly = 0;
   for( nContig = 0; nContig < pMiniAssembly->nNumContigs(); ++nContig ) {
      Contig* pContig = pMiniAssembly->pGetContig( nContig );

      nNumberOfReadsInNewAssembly += pContig->nGetNumberOfFragsInContig();
   }


   readsSortedByReadName aReadsInNewAssembly( (size_t) nNumberOfReadsInNewAssembly );
   aReadsInNewAssembly.soName_ = "miniassembly::aReadsInNewAssembly";

   for( nContig = 0; nContig < pMiniAssembly->nNumContigs(); ++nContig ) {
      Contig* pContig = pMiniAssembly->pGetContig( nContig );
      
      for( int nRead = 0; nRead < pContig->nGetNumberOfFragsInContig();
           ++nRead ) {
         LocatedFragment* pLocFrag = pContig->pLocatedFragmentGet( nRead );

         aReadsInNewAssembly.insert( pLocFrag );
      }
   }


   aReadsToReassemble_.resize( nNumberOfReadsToReassemble );

   for( nContig = 0; nContig < aContigsToReassemble_.length(); ++nContig ) {
      Contig* pContig = aContigsToReassemble_[ nContig ];

      for( int nRead = 0; nRead < pContig->nGetNumberOfFragsInContig(); 
           ++nRead ) {
         LocatedFragment* pLocFrag = pContig->pLocatedFragmentGet( nRead );
         aReadsToReassemble_.insert( pLocFrag );

      }
   }

   LocatedFragment* pSentinelLocFrag = 
      new LocatedFragment( RWCString( (char) 255 ), // name
                           0, // nConsensusStartPos,
                           false, // bIsComp
                           NULL ); // pContig

   aReadsInNewAssembly.insert( pSentinelLocFrag );
   aReadsToReassemble_.insert( pSentinelLocFrag );




   aReadsToReassemble_.resort();
   aReadsInNewAssembly.resort();

   // now compare the lists

   RWTPtrOrderedVector<LocatedFragment> aSingletsInMiniassembly;

   int nReadInNewAssembly = 0;
   int nReadInOldAssembly = 0;
   RWCString soErrorMessage;

   while( nReadInNewAssembly < aReadsInNewAssembly.length() &&
          nReadInOldAssembly < aReadsToReassemble_.length() ) { 

      if ( aReadsInNewAssembly[ nReadInNewAssembly ]->soGetName() <
           aReadsToReassemble_[ nReadInOldAssembly ]->soGetName() ) {
         soErrorMessage += "Program bug since there is a read in the new assembly that was not in the original assembly: ";
         soErrorMessage += aReadsInNewAssembly[ nReadInNewAssembly ]->soGetName();
         soErrorMessage += "\n";
         ++nReadInNewAssembly;
      }
      else if ( aReadsInNewAssembly[ nReadInNewAssembly ]->soGetName() >
                aReadsToReassemble_[ nReadInOldAssembly ]->soGetName() ) {
         // I think no error message about these--we are usually dealing with
         // them by putting them into their own contigs.  3/03
//          soErrorMessage += "Read ";
//          soErrorMessage += aReadsToReassemble_[ nReadInOldAssembly ]->soGetName();
//          soErrorMessage += " is not found in the new assembly.  It must be in the singlets file meaning phrap did find significant overlap between it and other reads.\n";
         aSingletsInMiniassembly.insert( aReadsToReassemble_[ nReadInOldAssembly ] );
         ++nReadInOldAssembly;

      }
      else if ( aReadsInNewAssembly[ nReadInNewAssembly ]->soGetName() ==
           aReadsToReassemble_[ nReadInOldAssembly ]->soGetName() ) {
         ++nReadInNewAssembly;
         ++nReadInOldAssembly;
      }
      else {
         // we should have covered all the cases above
         assert( false );
      }
   }


   if ( !soErrorMessage.isNull() ) {
      // must attach this to the main window since the miniassembly
      // window is going away
      popupErrorMessage( soErrorMessage.data() );
   }


   // get rid of the sentinel
   aReadsToReassemble_.removeLast();
   aReadsInNewAssembly.removeLast();


   // optionally, put all singlets into their own contigs


   RWTPtrOrderedVector<Contig> aSingletContigs;

   if ( bPutEachSingletIntoItsOwnContig_ ) {

      cerr << "putting singlets into own contigs...";
      cerr.flush();

      aSingletContigs.resize( aSingletsInMiniassembly.length() );

      for( int nSinglet = 0; nSinglet < aSingletsInMiniassembly.length();
        ++nSinglet ) {
         LocatedFragment* pLocFrag = aSingletsInMiniassembly[ nSinglet ];


         Contig* pOldContig = pLocFrag->pContig_;

         // remove this read from the old contig.  this is 
         // important so that the read isn't deleted when the contig 
         // is deleted (below) but still used in a new contig.
         
         pOldContig->apLocatedFragment_.remove( pLocFrag );

         
         // make a new contig out of it:
         // This is given a name that is consistent with others in the
         // main assembly and is moved into the main assembly.

         Contig* pNewContig = pLocFrag->pMakeAContigOutOfThisRead();


         aSingletContigs.insert( pNewContig );

      }

      cerr << "done" << endl;
      cerr.flush();
   }



   // now delete the old contigs/reads from the assembly

   for( nContig = 0; nContig < aContigsToReassemble_.length(); ++nContig ) {
      Contig* pContig = aContigsToReassemble_[ nContig ];
      for( int nRead = 0; nRead < pContig->nGetNumberOfFragsInContig();
           ++nRead ) {
         LocatedFragment* pLocFrag = pContig->pLocatedFragmentGet( nRead );

         delete pLocFrag;

      }

      // this doesn't delete the reads
      delete pContig;
   }

   
   // now we do not need to create the contigs since they are already created.
   // We need to rename them so they do not conflict with the names of the
   // existing contigs.  Then we will remove the pMiniassembly, but not
   // the contigs within it.  Probably the best way to do that would
   // be to remove all contig pointers from it and then delete pMiniassembly.

   // renaming contigs and moving them over

   Assembly* pMainAssembly = ConsEd::pGetAssembly();

   for( nContig = 0; nContig < pMiniAssembly->nNumContigs(); ++nContig ) {
      Contig* pContig = pMiniAssembly->pGetContig( nContig );

      pContig->soSequenceName_ = pMainAssembly->soGetNewContigName();

      // put the contig in the new Assembly

      pMainAssembly->addContig( pContig );
   }



   // load saved list of contigs  
   pCP->aListOfContigNamesForMiniassembly_.clear();


   // for the purpose of the gotolist, add the singlet contigs to
   // pMiniAssembly

   for( int nSinglet = 0; nSinglet < aSingletContigs.length(); ++nSinglet ) {
      Contig* pContig = aSingletContigs[ nSinglet ];
      pMiniAssembly->addContig( pContig );
   }
   
   // now make gotolist
   // put up a navigation box with the new contigs:

   gotoList* pGotoList = new gotoList();


   RWCString soDescription( (size_t) 1000 );
   for( nContig = 0; nContig < pMiniAssembly->nNumContigs(); ++nContig ) {
      Contig* pContig = pMiniAssembly->pGetContig( nContig );

      soDescription = pContig->soGetFancyName();

      int nNumberOfReads = pContig->nGetNumberOfFragsInContig();

      if ( nNumberOfReads == 1 ) {
         soDescription += ": ";
         LocatedFragment* pLocFrag = pContig->pLocatedFragmentGet( 0 );
         soDescription += pLocFrag->soGetName();
      }
      else {
         int nContigLength = pContig->nGetUnpaddedSeqLength();
         
         soDescription.increaseMaxLengthIfNecessary( 100 );
         soDescription.appendFormat( " (%d reads, %d bps)",
                                     nNumberOfReads,
                                     nContigLength );
      }

      gotoItem* pGotoItem = new gotoItem( pContig,
                                          NULL, // pLocFrag
                                          1, // nStart 
                                          1, // nEnd
                                          1, // nUnpaddedStart
                                          1, // nUnpaddedEnd
                                          soDescription,
                                          false, // bPrefixContigToDescription
                                          NULL ); // pOtherData

      pGotoList->addToList( pGotoItem );
   }

   guiMultiContigNavigator* pGuiNav =
      new guiMultiContigNavigator( 
                                "New Contigs",
                                "",
                                "",
                                50,
                                "",
                                NULL,
                                NULL,
                                NULL, // widTopLevelShellToBeConnectedTo
                                pGotoList,
                                NULL ); // pContigWin

   pGuiNav->scrollDownToBottom();
   

   // this does not delete the contigs--just the array
   delete pMiniAssembly;



   // do not allow user to try to run this again

   delete this;


   ConsEd::pGetConsEd()->updateContigListOnMainConsedWindow();
   pMainAssembly->setChanged();
   ConsEd::pGetConsEd()->disallowUndos();
}




bool miniassembly :: bUserEnteredFieldsAreOK() {

   soPhrapOptions_ = "";

   char* szMinScore = XmTextFieldGetString( widMinScore_ );

   RWCString soMinScore( szMinScore );
   
   XtFree( szMinScore );

   soMinScore = soMinScore.stripWhitespace( RWCString::BOTH );

   if ( !soMinScore.isNull() ) {
      if ( ! bIsNumeric( soMinScore ) ) {
         popupErrorMessage2( widPopupShell_,
                             "The -minscore box must either be left empty or else must have a whole number but instead it has %s\n",
                             soMinScore.data() );
         return( false );
      }
      soPhrapOptions_ += "-minscore ";
      soPhrapOptions_ += soMinScore;
      soPhrapOptions_ += " ";
   }


   char* szMinMatch = XmTextFieldGetString( widMinMatch_ );
   RWCString soMinMatch( szMinMatch );
   XtFree( szMinMatch );

   soMinMatch = soMinMatch.stripWhitespace( RWCString::BOTH );

   if ( ! soMinMatch.isNull() ) {
      if ( ! bIsNumeric( soMinMatch ) ) {
         popupErrorMessage2( widPopupShell_,
                             "The -minmatch box must either be left empty or else must have a whole number but instead it has %s\n",
                             soMinMatch.data() );
         return( false );
      }
      
      soPhrapOptions_ += "-minmatch ";
      soPhrapOptions_ += soMinMatch;
      soPhrapOptions_ += " ";
   }

   char* szForceLevel = XmTextFieldGetString( widForceLevel_ );
   RWCString soForceLevel( szForceLevel );
   XtFree( szForceLevel );

   soForceLevel = soForceLevel.stripWhitespace( RWCString::BOTH );

   if ( ! soForceLevel.isNull() ) {
      if ( ! bIsNumeric( soForceLevel ) ) {
         popupErrorMessage2( widPopupShell_,
                             "The -forcelevel box must either be left empty or else must have a whole number but instead it has %s\n",
                             soForceLevel.data() );
         return( false );
      }
      
      soPhrapOptions_ += "-forcelevel ";
      soPhrapOptions_ += soForceLevel;
      soPhrapOptions_ += " ";
   }

   char* szOtherPhrapOptions = XmTextFieldGetString( widOtherPhrapOptions_ );
   soPhrapOptions_ += szOtherPhrapOptions;
   XtFree( szOtherPhrapOptions );
   soPhrapOptions_ += " ";


   bPutEachSingletIntoItsOwnContig_ = 
      ( XmToggleButtonGetState( widPutIntoSeparateContigs_ ) == True ?
        true : false );

   return( true );
}
   
   
bool miniassembly :: bPhredPhrapVersionIsOK() {

   // need to run phredPhrap -v from a pipe so we can read the output

   if ( pCP->filFullPathnameOfMiniassemblyScript_.index( "phredPhrap" ) ==
        RW_NPOS ) {
      // the user has their own script, so no way to check versions
      return( true );
   }

   RWCString soCommand = pCP->filFullPathnameOfMiniassemblyScript_ + " -V";
      

   FILE* pPipe = popen( soCommand.data(), "r" );

   if ( !pPipe ) {
      RWCString soErrorMessage = "could not open pipe to process running ";
      soErrorMessage += soCommand;
      soErrorMessage += " ";
      soErrorMessage += soGetErrno();
      popupErrorMessage( soErrorMessage );
      // we will try again running phredPhrap above
      return( true );
   }

   const int nMaxLineSize = 500;
   char szLine[ nMaxLineSize + 1 ];

   if ( fgets( szLine, nMaxLineSize, pPipe ) == NULL ) {
      RWCString soErrorMessage = "opened pipe to process running ";
      soErrorMessage += soCommand;
      soErrorMessage += " but could not read anything from the pipe";
      popupErrorMessage( soErrorMessage );
      // we will try again running phredPhrap above
      return( true );
   }

   pclose( pPipe );

   RWCString soRequiredVersion( "080818" );
   RWCString soVersion( szLine );
   soVersion.stripTrailingWhitespaceFast();

   if ( soVersion >= soRequiredVersion && soVersion <= "800000" ) {
      cerr << "Using phredPhrap version " << soVersion << " --good." << endl;
      return( true );
   }
   else {
      RWCString soErrorMessage = "To run miniassemblies, you must have phredPhrap version ";
      soErrorMessage += soRequiredVersion;
      soErrorMessage += " or better.  Instead you have version ";
      soErrorMessage += soVersion;
      soErrorMessage += " The phredPhrap script is part of the Consed distribution which you can get from www.phrap.org";
      popupErrorMessage( soErrorMessage );
      return( false );
   }
}

                                  
void miniassembly :: runCommandAndCreateTerminateButton( 
           const RWCString&  soCommand ) {

   /* works like this: The current process is suspended and a new
      process is created, child1.  The new process forks creating
      child2, but doesn't suspend.  Child2 starts running phredPhrap.
      child1 creates a button.  If the user pushes the button, child2
      and all its descendants are killed, and then child1 terminates.
      If the user does not push the button, when child2 finishes
      running the phredPhrap script, it kills child1 (its parent) and
      terminates.  In either case, child1 terminates, so the current
      suspended process can continue. */

   int nPid = fork();

   if ( nPid < 0 ) {
      popupErrorMessage( "Problem %d running fork so cannot run miniassembly %s",
                         nPid,
                         soCommand.data() );
      return;
   }
   else if ( nPid > 0 ) {
      waitpid( nPid, 0, 0 );
   }
   else {
      child1ProcessCode( soCommand );
   }
}



static void killProcessAndAllDescendants( 
                            RWTValOrderedVector<int>& aPids, 
                            RWTValOrderedVector<int>& aPPids, 
                            RWTValOrderedVector<RWCString>& aCommands,
                            const int nPidToKill,
                            const RWCString& soCommandOfPidToKill ) {


   cerr << "killing process " << nPidToKill << "(" <<
      soCommandOfPidToKill << ") and descendants" << endl;
   
   for( int nIndex = 0; nIndex < aPids.length(); ++nIndex ) {
      if ( aPPids[ nIndex ] == nPidToKill ) {
         // there is a process that is a child of the process to kill
         // so kill it first

         killProcessAndAllDescendants( aPids, 
                                       aPPids, 
                                       aCommands, 
                                       aPids[ nIndex ],
                                       aCommands[ nIndex ] );
      }
   }

   if ( kill( nPidToKill, SIGKILL ) != 0 ) {
      cerr << "error trying to kill process " << nPidToKill <<
         " " << soGetErrno() << endl;
   }
   else {
      cerr << "killed process " << nPidToKill << endl;
   }
}




static void cbUserPushedCancel( Widget wid,
                                XtPointer pClientData,
                                XtPointer pCallData ) {


   union intXtPointerUnion uPidPhredPhrap;

   uPidPhredPhrap.pXtPointer = pClientData;
   
   int nPidPhredPhrap = uPidPhredPhrap.n;



   FILE* pPS = popen( "ps -al", "r" );

   // make a list of pids and parent pids

   const int nMaxLineSize = 1000;
   RWCString soLine( (size_t) nMaxLineSize );
   if ( fgets( soLine.data(), nMaxLineSize, pPS ) == NULL ) {
      cerr << "could not read header line of ps so can't terminate phredPhrap" << endl;
      exit( 1 );
   }
   soLine.nCurrentLength_ = strlen( soLine.data() );

   RWCTokenizer tokHeading( soLine );

   int nHeading = -1; // set up for first ++
   int nPidField = -666;
   int nPPidField = -666;
   int nCmdField = -666;
   RWCString soHeading;
   while( !( soHeading = tokHeading() ).isNull() ) {
      ++nHeading;

      if ( soHeading == "PID" ) {
         nPidField = nHeading;
      }
      else if ( soHeading == "PPID" ) {
         nPPidField = nHeading;
      }
      else if ( soHeading == "CMD" ) {
         nCmdField = nHeading;
      }
   }

   if ( nPidField == -666 || nPPidField == -666 ) {
      cerr << "could not find PID or PPID in ps header so can't terminate phredPhrap" << endl;
      exit( 1 );
   }

   RWTValOrderedVector<int> aPids;
   RWTValOrderedVector<int> aPPids;
   RWTValOrderedVector<RWCString> aCommands;
   
   while( fgets( soLine.data(), nMaxLineSize, pPS ) != NULL ) {
      soLine.nCurrentLength_ = strlen( soLine.data() );

      RWTValOrderedVector<RWCString> aFields;
      RWCTokenizer tokPSLine( soLine );
      RWCString soField;
      while( !( soField = tokPSLine() ).isNull() ) {
         aFields.insert( soField );
      }

      if ( aFields.length() <= MAX( nPidField, nPPidField ) ) {
         cerr << "problem:  couldn't find PID and PPID fields on this line: " <<
            soLine << endl;
         continue;
      }

      int nPid;
      if ( !bIsNumericMaybeWithWhitespace( aFields[ nPidField ], nPid ) ) {
         cerr << "error:  couldn't understand field " << nPidField << 
            " in line " << soLine << endl;
         continue;
      }

      int nPPid;
      if ( !bIsNumericMaybeWithWhitespace( aFields[ nPPidField ], nPPid ) ) {
         cerr << "error: couldn't understand field " << nPPidField <<
            " in line " << soLine << endl;
         continue;
      }

      aPids.insert( nPid );
      aPPids.insert( nPPid );

      if ( nCmdField != -666 ) {
         if ( nCmdField < aFields.length() ) {
            aCommands.insert( aFields[ nCmdField ] );
         }
         else {
            aCommands.insert( aFields[ aFields.length() - 1 ] );
         }
      }
      else {
         aCommands.insert( "couldn't find" );
      }
   }
   pclose( pPS );


   killProcessAndAllDescendants( aPids, aPPids, aCommands, nPidPhredPhrap, 
                                 "?" ); // soCommand
   
   exit( 1 );
}



//extern String* aszFallbackResources;
static String aszFallbackResources[] = {

  // "system" font, used on labels, buttons, menus, etc.
   //  "miniassembly*fontList: helvetica",

  // drawing font used in contigwin
  "miniassembly*font: -misc-fixed-medium-r-normal--15-140-75-75-c-90-iso8859-1",
  "miniassembly*fontList: -misc-fixed-bold-r-normal--13-100-100-100-c-70-iso8859-1",
  "miniassembly*foreground: Black",
  // colors and shadows
//  "miniassembly*troughColor: Grey45",
  "miniassembly*troughColor: Black",
  "miniassembly*background: Grey77",
  "miniassembly*topShadowColor: Grey88",
  "miniassembly*bottomShadowColor: Grey45",
  "miniassembly*shadowThickness: 2",
  // these need to be reversed appearance consistent with diboxes, etc.
  "miniassembly*mainwin.topShadowColor: Grey45",
  "miniassembly*mainwin.bottomShadowColor: Grey88",

  // colors in trace window

  "miniassembly*scaleDrawingArea.background: black",
  "miniassembly*consensusDrawingArea.background: black",
  "miniassembly*editableBasesDrawingArea.background: black",
  "miniassembly*tagsDrawingArea.background: black",
  "miniassembly*phredCalledBasesDrawingArea.background: black",
  "miniassembly*ABICalledBasesDrawingArea.background: black",
  "miniassembly*tracesDrawingArea.background: black",

  //  "miniassembly*assemblyView*assemblyViewDrawingArea.background: white",

  // time in milliseconds--very long time for recognizing double clicks
  "miniassembly*doubleClickInterval: 400",

  // "miniassembly*contigwin.background: Black",
  // "miniassembly*scaleNumbers.foreground: Yellow",
  // "miniassembly*scaleGraphics.foreground: Yellow",

  // scale widgets require messing with for some obscure reason
  "miniassembly*verticalScale.height: 18",
  "miniassembly*horizontalScale.height: 18",

  // not sure this belongs here, but users will probably want
  // to mess with these ranges and defaults
  "miniassembly*verticalScale.value: 100",
  "miniassembly*verticalScale.maximum: 400",
  "miniassembly*verticalScale.minimum: 1",
  "miniassembly*horizontalScale.value: 10",
  "miniassembly*horizontalScale.maximum: 50",
  "miniassembly*horizontalScale.minimum: 3",

  // infobuttons are used to display current filename, fragment, etc.
  // pressing (usually) them supplies more info
  //  "miniassembly*infobutton.fontList: helvetica",
  //  "miniassembly*textfield.fontList: helvetica",
  "miniassembly*textfield.background: Grey70",

  // for textbox object
  "miniassembly*textBox.fontList: -misc-fixed-medium-r-normal--15-140-75-75-c-90-iso8859-1",
  "miniassembly*navtextfield*fontList: -misc-fixed-medium-r-normal--15-140-75-75-c-90-iso8859-1",
  "miniassembly*navigator*fontList: -misc-fixed-medium-r-normal--15-140-75-75-c-90-iso8859-1",

  "miniassembly.title: Miniassembly",


  "miniassembly*contigwin.background: black",
  "miniassembly*scaleNumbers.foreground: black",
  "miniassembly*scaleGraphics.foreground: black",



  NULL // list is null-terminated

};


void miniassembly :: child1ProcessCode( const RWCString& soCommand ) {


   // first, start up phrap

   int nPidChild2 = fork();
   if ( nPidChild2 == 0 ) {
      child2ProcessCode( soCommand );
      exit( 1 ); // probably will never get here
   }

   
   // let's put up a button 

   Arg  aArg[20];

   int nArg;
   
   
   XtAppContext ctxAppContext;

   nArg = 0;
   Widget widTopLevel = XtAppInitialize( &ctxAppContext,
                                         "miniassembly",
                                         NULL,
                                         0,
                                         &nArg,
                                         NULL,
                                         // (String*) NULL,
                                         aszFallbackResources,
                                         NULL,
                                         (Cardinal) 0 );

   /* Create a push button widget */

   Widget widCancelButton = XtVaCreateManagedWidget( "    Cancel Miniassembly    ",
                                                     xmPushButtonWidgetClass,
                                                     widTopLevel,
                                                     NULL );

   XtAddCallback( widCancelButton,
                  XmNactivateCallback,
                  cbUserPushedCancel,
                  (void*) nPidChild2 );


   XtRealizeWidget( widTopLevel );

   XtAppMainLoop( ctxAppContext );

}

void miniassembly :: child2ProcessCode( const RWCString& soCommand ) {
   
   int nRetStat = system( (char*) soCommand.data() );
   if ( nRetStat != 0 ) {
      // how will we display this error message?  
      cerr << "error " << nRetStat;
      if ( strerror( nRetStat ) ) {
         cerr << " which means " << strerror( nRetStat );
      }
      cerr << " when running phredPhrap.  See xterm for details." << endl;
   }
   

   // kill parent (child1) when done

   int nPid = getppid();

   if ( kill( nPid, SIGKILL ) != 0 ) {
      cerr << "error trying to kill parent process " << nPid <<
         " " << soGetErrno() << endl;
   }
   
}