/***************************************************************************** # 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. # #*****************************************************************************/ // // textbox.cpp // // implementation for TextBox object // // chrisa 28-feb-95, re-written dsg // //#include //#include #include #include #include #include #include #include #include #include "assert.h" #include "guiapp.h" #include "textbox.h" #include "hp_exception_kludge.h" #include #include #include "handleWindowManagerDelete2.h" #include "popupErrorMessage.h" static void cbDismiss( Widget wid, XtPointer pClientData, XtPointer pCallData ) { TextBox* pTextBox = (TextBox*) pClientData; TRY_CATCH_WRAPPER( delete pTextBox ); } static void cbFindFirst( Widget wid, XtPointer pClientData, XtPointer pCallData ) { TextBox* pTextBox = (TextBox*) pClientData; TRY_CATCH_WRAPPER( pTextBox->findFirst() ); } static void cbFindNext( Widget wid, XtPointer pClientData, XtPointer pCallData ) { TextBox* pTextBox = (TextBox*) pClientData; TRY_CATCH_WRAPPER( pTextBox->findNext() ); } static void cbSaveToFile( Widget wid, XtPointer pClientData, XtPointer pCallData ) { TextBox* pTextBox = (TextBox*) pClientData; TRY_CATCH_WRAPPER( pTextBox->saveToFile() ); } TextBox::TextBox(const char* szTitle, const int nNumberOfRowsVisible, const RWCString& soMessage ) : soMessage_( soMessage ), nLastFoundPosition_( RW_NPOS ), pToRunWhenDismissWindow_( NULL ) { Arg aArg[20]; int nArgs; assert (szTitle); widPopupShell_ = XtVaCreatePopupShell( szTitle, topLevelShellWidgetClass, GuiApp::pGetGuiApp()->widGetTopLevel(), XmNtitle, szTitle, XmNnoResize, False, XmNautoUnmanage, False, // what is this for? XmNtransient, False, // what is this for? XmNdeleteResponse, XmDO_NOTHING, NULL ); handleWindowManagerDelete2( widPopupShell_, cbDismiss, this ); Widget widForm = XtVaCreateManagedWidget( "form", xmFormWidgetClass, widPopupShell_, XmNshadowThickness, 0, XmNborderWidth, 0, NULL ); Widget widDismissButton = XtVaCreateManagedWidget( "Dismiss", xmPushButtonWidgetClass, widForm, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 10, XmNtopOffset, 10, XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, 40, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 60, XmNtraversalOn, False, NULL ); XtAddCallback( widDismissButton, XmNactivateCallback, cbDismiss, this ); Widget widSearchLabel = XtVaCreateManagedWidget( "Find (case insensitive):", xmLabelWidgetClass, widForm, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, widDismissButton, XmNleftAttachment, XmATTACH_FORM, XmNtraversalOn, False, NULL ); widSearchText_ = XtVaCreateManagedWidget( "searchText", xmTextWidgetClass, widForm, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, widSearchLabel, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, widSearchLabel, XmNtraversalOn, True, XmNcolumns, 20, NULL ); XtAddCallback( widSearchText_, XmNactivateCallback, cbFindFirst, this ); Widget widFindFirstButton = XtVaCreateManagedWidget( "Find First", xmPushButtonWidgetClass, widForm, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, widSearchLabel, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, widSearchText_, XmNtraversalOn, False, NULL ); XtAddCallback( widFindFirstButton, XmNactivateCallback, cbFindFirst, this ); Widget widFindNextButton = XtVaCreateManagedWidget( "Find Next", xmPushButtonWidgetClass, widForm, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, widSearchLabel, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, widFindFirstButton, XmNtraversalOn, False, NULL ); XtAddCallback( widFindNextButton, XmNactivateCallback, cbFindNext, this ); Widget widSaveToFileButton = XtVaCreateManagedWidget( "Save to File", xmPushButtonWidgetClass, widForm, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, widSearchLabel, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, widFindNextButton, XmNtraversalOn, False, NULL ); XtAddCallback( widSaveToFileButton, XmNactivateCallback, cbSaveToFile, this ); const int nDefTextBoxCols = 80; // create the scrolled text widget nArgs = 0; XtSetArg(aArg[nArgs], XmNtopAttachment, XmATTACH_FORM); nArgs++; XtSetArg(aArg[nArgs], XmNleftAttachment, XmATTACH_FORM); nArgs++; XtSetArg(aArg[nArgs], XmNrightAttachment, XmATTACH_FORM); nArgs++; XtSetArg( aArg[nArgs], XmNbottomAttachment, XmATTACH_WIDGET ); nArgs++; XtSetArg( aArg[nArgs], XmNbottomWidget, widSearchText_ ); nArgs++; XtSetArg(aArg[nArgs], XmNtopOffset,4); nArgs++; XtSetArg(aArg[nArgs], XmNleftOffset,4); nArgs++; XtSetArg(aArg[nArgs], XmNrightOffset,4); nArgs++; XtSetArg(aArg[nArgs], XmNscrollBarDisplayPolicy, XmAS_NEEDED); nArgs++; // XtSetArg(aArg[nArgs], XmNscrollingPolicy, XmAUTOMATIC); nArgs++; XtSetArg(aArg[nArgs], XmNrows, nNumberOfRowsVisible ); nArgs++; XtSetArg(aArg[nArgs], XmNcolumns, nDefTextBoxCols); nArgs++; XtSetArg(aArg[nArgs], XmNeditMode, XmMULTI_LINE_EDIT); nArgs++; XtSetArg(aArg[nArgs], XmNeditable, False); nArgs++; XtSetArg(aArg[nArgs], XmNtraversalOn, False); nArgs++; XtSetArg(aArg[nArgs], XmNcursorPositionVisible, False); nArgs++; if ( !soMessage.isNull() ) { XtSetArg( aArg[nArgs], XmNvalue, (const char*) soMessage ); ++nArgs; } widScrolledText_ = XmCreateScrolledText(widForm, "textBox", aArg, nArgs); XtManageChild(widScrolledText_); // there are 2 ways to bring up this dialog--either pass all data to // it at creation or else create it, add data, and the makeVisible if ( !soMessage.isNull() ) { XtPopup( widPopupShell_, XtGrabNone ); } } // set the text box value to the current contents of the // strstream buf and manage the dibox void TextBox::makeVisible() { Arg aArg[10]; int nArgs = 0; XtSetArg(aArg[nArgs], XmNvalue, (char*) soMessage_.data()); nArgs++; XtSetValues(widScrolledText_, aArg, nArgs); XtPopup( widPopupShell_, XtGrabNone ); } TextBox :: ~TextBox() { if ( pToRunWhenDismissWindow_ ) { (*pToRunWhenDismissWindow_)( pParameterToRunWhenDismissWindow_ ); } XtPopdown( widPopupShell_ ); XtDestroyWidget( widPopupShell_ ); } void TextBox :: append( const RWCString& soAdditionalMessage ) { soMessage_ += soAdditionalMessage; } void TextBox :: appendWithArgs( char* szFormat, ... ) { char szAdditionalMessage[10000]; va_list args; va_start( args, szFormat ); vsprintf( szAdditionalMessage, szFormat, args ); va_end( args ); soMessage_ += szAdditionalMessage; } void TextBox :: findFirst() { char* szSearchString = XmTextGetString( widSearchText_ ); RWCString soSearchStringLowerCase( szSearchString ); XtFree( szSearchString ); soSearchStringLowerCase.toLower(); if ( soMessageLowerCase_.isNull() ) { soMessageLowerCase_ = soMessage_; soMessageLowerCase_.toLower(); } // highlight all matches int nFoundPos; int nStartPosition = 0; bool bFoundAMatch = false; while( ( nFoundPos = soMessageLowerCase_.index( soSearchStringLowerCase, nStartPosition ) ) != RW_NPOS ) { if ( !bFoundAMatch ) { bFoundAMatch = true; nLastFoundPosition_ = nFoundPos; } XmTextSetHighlight( widScrolledText_, nFoundPos, nFoundPos + soSearchStringLowerCase.length(), XmHIGHLIGHT_SELECTED ); nStartPosition = nFoundPos + 1; } if ( !bFoundAMatch ) { GuiApp::beep(); return; } scrollWindowToLocation( nLastFoundPosition_ ); } void TextBox :: findNext() { int nStartPosition = nLastFoundPosition_ + 1; char* szSearchString = XmTextGetString( widSearchText_ ); RWCString soSearchStringLowerCase( szSearchString ); XtFree( szSearchString ); soSearchStringLowerCase.toLower(); int nFoundPos = soMessageLowerCase_.index( soSearchStringLowerCase, nStartPosition ); nLastFoundPosition_ = nFoundPos; if ( nFoundPos == RW_NPOS ) { GuiApp::beep(); return; } scrollWindowToLocation( nLastFoundPosition_ ); } void TextBox :: scrollWindowToLocation( const int nCharacterPosition ) { // now we need to figure out where to set the top of the page so that // there is some context before the highlighted word const int nLinesAboveFoundLine = 5; int nCRsFound = 0; int nPos = nCharacterPosition; while( nPos > 0 && nCRsFound <= nLinesAboveFoundLine ) { if ( soMessage_[ nPos ] == '\n' ) ++nCRsFound; --nPos; } // 2 ways to leave the loop: // 1) nPos == 0 // 2) nCRsFound > nLinesAboveFoundLine // It the former, fine--set the top character to 0 // If it is the latter, then nPos is pointing the \n in the diagram // below: // line above the one we want to show\n // line we want to be top line // Thus we need to advance nPos by 2--one to get to the \n and 1 more // to get to the 'l' in 'line we want to be top line' if ( nCRsFound > nLinesAboveFoundLine ) nPos = nPos + 2; XmTextSetTopCharacter( widScrolledText_, nPos ); } void TextBox :: saveToFile() { RWCString soDirMask = "*.*"; RWCString soTryOutFileName = "info.txt"; FileName soUserOutFileName = GuiApp::popupFileSelector( "Save info to file", soDirMask, soTryOutFileName ); // if user cancelled or did not supply name, bail out now if ( soUserOutFileName.isNull() ) return; bool bGoAhead = true; if ( soUserOutFileName.bFileByThisNameExists() ) { bGoAhead = GuiApp::popupDecisionMessage( "File %s exists. Save anyway?", (const char*) soUserOutFileName ); } if ( ! bGoAhead ) return; try { saveToFile2( soUserOutFileName ); } catch (SysRequestFailed srf) { popupErrorMessage( srf.szGetDesc() ); } } void TextBox :: saveToFile2(const FileName& soUserOutFileName ) { FILE* pOut = fopen( (const char*) soUserOutFileName, "w" ); if ( !pOut ) { PANIC_OST( ost ) << "Unable to open file " << soUserOutFileName << " for writing" << endl << ends; SysRequestFailed srf(ost.str()); // what the hell, show 'em the errno srf.includeErrnoDescription(); throw srf; } for( int n = 0; n < soMessage_.length(); ++n ) putc( soMessage_[n], pOut ); fclose( pOut ); } void TextBox :: raiseWindow() { XtMapWidget( widPopupShell_ ); XRaiseWindow( XtDisplay( widPopupShell_ ), XtWindow( widPopupShell_ ) ); }