home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / macfe / Composer / CSpellChecker.cp < prev    next >
Encoding:
Text File  |  1998-04-08  |  28.4 KB  |  1,051 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. #include "CSpellChecker.h"
  20.  
  21. #include "CTextTable.h"
  22. #include "StBlockingDialogHandler.h"
  23. #include "URobustCreateWindow.h"
  24. #include "CSimpleTextView.h"
  25. #include "LGAEditField.h"
  26. #include "LGAPushButton.h"
  27. #include "LGAPopup.h"
  28. #include "CEditDictionary.h"
  29. #include "UMenuUtils.h"
  30.  
  31. // need to include "structs.h" before "edt.h" because "edt.h" is missing
  32. // some of the includes it needs
  33. #include "structs.h"
  34. #include "edt.h"
  35. #include "proto.h"        // LO_GetSelectionText
  36. #include "prefapi.h"    // PREF_SetIntPref
  37. #include "uerrmgr.h"    // ErrorManager
  38. #include "resgui.h"        // error stringNums
  39. #include "macgui.h"        // StPrepareForDialog
  40. #include "ufilemgr.h"    // CFileMgr
  41.  
  42. // shared library stuff from NSPR
  43. #include "prlink.h"
  44.  
  45.  
  46. /*    The difference between calling the spell checker lib from a DLL or from
  47.     a static linked lib depends on whether we're generating CFM code
  48.     (and on whether the static lib was included in the application project) */
  49. #if GENERATINGCFM
  50. #define USE_DYNAMIC_SC_LIB
  51. #else
  52. #undef USE_DYNAMIC_SC_LIB
  53. #endif
  54.  
  55. // local prototypes
  56. static void exit_spellchecker( PRLibrary *lib, ISpellChecker *pSpellChecker, CMacSpellChecker *macSpellChecker );
  57.  
  58.  
  59. // function typedefs
  60. typedef ISpellChecker*(*sc_create_func)(); 
  61. typedef void (*sc_destroy_func)(ISpellChecker *); 
  62.  
  63.  
  64.  
  65. CMacSpellChecker::CMacSpellChecker( MWContext *context, CEditView *editView, CSimpleTextView *textView )
  66. {
  67.     mMWContext = context;
  68.     mEditView = editView;
  69.     mTextView = textView;
  70.     mISpellChecker = NULL;
  71. }
  72.  
  73.  
  74. void CMacSpellChecker::ReplaceHilitedText( char *newText, Boolean doAll )
  75. {
  76.     if ( mEditView )
  77.     {
  78.         char *oldWord = (char *)LO_GetSelectionText( GetMWContext() );
  79.         if ( oldWord == NULL )
  80.             return;
  81.         
  82.         EDT_ReplaceMisspelledWord( GetMWContext(), oldWord, newText, doAll );
  83.         XP_FREE( oldWord );
  84.     }
  85.     else if ( mTextView )
  86.     {
  87.         GetISpellChecker()->ReplaceMisspelledWord( newText, doAll );
  88.  
  89.         /* assume the misspelled word is selected so that InsertPtr will replace it */
  90.         mTextView->InsertPtr( newText, XP_STRLEN(newText), NULL, NULL, false, true );
  91.     }
  92. }
  93.  
  94.  
  95. void CMacSpellChecker::IgnoreHilitedText( Boolean doAll )
  96. {
  97.     if ( mEditView )
  98.     {
  99.         char *oldWord = (char *)LO_GetSelectionText( GetMWContext() );
  100.         if ( oldWord == NULL )
  101.             return;
  102.         
  103.         EDT_IgnoreMisspelledWord( GetMWContext(), oldWord, doAll );
  104.         XP_FREE( oldWord );
  105.     }
  106.     else if ( mTextView && doAll )
  107.     {
  108.         SInt32    selStart     = 0;
  109.         SInt32    selEnd        = 0;
  110.         SInt32    selLen        = 0;
  111.         
  112.         mTextView->GetSelection( &selStart, &selEnd );
  113.         
  114.         selLen = selEnd - selStart;
  115.         
  116.         if ( selLen > 0 )
  117.         {
  118.             Handle    theText = mTextView->GetTextHandle();
  119.             XP_ASSERT( theText != NULL );
  120.             
  121.             char *textP = (char *)XP_ALLOC( selLen + 1 );
  122.             XP_ASSERT( textP );
  123.             if ( textP )
  124.             {
  125.                 ::BlockMoveData( ((char *)*theText) + selStart, textP, selLen );
  126.                 textP[ selLen ] = 0;
  127.                 
  128.                 GetISpellChecker()->IgnoreWord( textP );
  129.                 XP_FREE( textP );
  130.             }
  131.         }
  132.     }
  133.         // assume we don't need to do anything for ignoring just this occurrence in plain text
  134. }
  135.  
  136.  
  137. typedef struct dictionaryInfo
  138. {
  139.     int    langCode;
  140.     int    dialectCode;
  141.     int    menuItemNumber;
  142. } dictionaryInfo;
  143.  
  144. static void AddDictionaryMenuItem( MenuHandle languagePopup, dictionaryInfo *dictInfoP )
  145. {
  146.     // get string for lang code and dialectcode
  147.     int stringIndex = 4;
  148.     switch ( dictInfoP->langCode )
  149.     {
  150.         case L_ENGLISH:
  151.             switch ( dictInfoP->dialectCode )
  152.             {
  153.                 case D_US_ENGLISH:    stringIndex = 5;    break;
  154.                 case D_UK_ENGLISH:    stringIndex = 6;    break;
  155.                 case D_AUS_ENGLISH:    stringIndex = 7;    break;
  156.                 default:            stringIndex = 8;    break;
  157.             }
  158.             break;
  159.         
  160.         case L_CATALAN:    stringIndex = 9;    break;
  161.         case L_HUNGARIAN: stringIndex = 10;    break;
  162.         
  163.         case L_GERMAN:
  164.             switch ( dictInfoP->dialectCode )
  165.             {
  166.                 default:            stringIndex = 11;    break;
  167.                 case D_SCHARFES:    stringIndex = 12;    break;
  168.                 case D_DOPPEL:        stringIndex = 13;    break;
  169.             }
  170.             break;
  171.         
  172.         case L_SWEDISH:    stringIndex = 14;    break;
  173.         case L_SPANISH:    stringIndex = 15;    break;
  174.         case L_ITALIAN:    stringIndex = 16;    break;
  175.         case L_DANISH:    stringIndex = 17;    break;
  176.         case L_DUTCH:    stringIndex = 18;    break;
  177.             
  178.         case L_PORTUGUESE:
  179.             switch ( dictInfoP->dialectCode )
  180.             {
  181.                 default:
  182.                 case D_EUROPEAN:    stringIndex = 20;    break;
  183.                 case D_BRAZILIAN:    stringIndex = 21;    break;
  184.             }
  185.             break;
  186.         
  187.         case L_NORWEGIAN:
  188.             switch ( dictInfoP->dialectCode )
  189.             {
  190.                 default:
  191.                 case D_BOKMAL:    stringIndex = 22;    break;
  192.                 case D_NYNORSK:    stringIndex = 23;    break;
  193.             }
  194.             break;
  195.         
  196.         case L_FINNISH:    stringIndex = 24;    break;
  197.         case L_GREEK:    stringIndex = 25;    break;
  198.         case L_AFRIKAANS: stringIndex = 26;    break;
  199.         case L_POLISH:    stringIndex = 27;    break;
  200.         case L_CZECH:    stringIndex = 28;    break;
  201.         case L_FRENCH:    stringIndex = 29;    break;
  202.         case L_RUSSIAN:    stringIndex = 30;    break;
  203.     }
  204.     
  205.     Str255    str;
  206.     ::GetIndString( str, SpellCheckerResource, stringIndex );
  207.     if ( str[ 0 ] == 0 )
  208.         dictInfoP->menuItemNumber = 0;
  209.     else
  210.     {
  211.         UMenuUtils::AppendMenuItem( languagePopup, str, true );
  212.         dictInfoP->menuItemNumber = ::CountMItems( languagePopup );
  213.     }
  214. }
  215.  
  216.  
  217. static int FindDefaultMenuItem( int langcode, int dialectcode, dictionaryInfo *dictInfoP, int maxIndex )
  218. {
  219.     int i;
  220.     for (i = 0; i < maxIndex; i++ )
  221.     {
  222.         if ( langcode == dictInfoP[ i ].langCode && dialectcode == dictInfoP[ i ].dialectCode )
  223.             return dictInfoP[ i ].menuItemNumber;
  224.     }
  225.     
  226.     return 1;
  227. }
  228.  
  229. static int FindDictionaryInfoIndexForMenu( LGAPopup *languagePopup, dictionaryInfo *dictInfoP, int maxIndex )
  230. {
  231.     int menuSelection = languagePopup->GetValue();
  232.     if ( menuSelection == 0 )
  233.         return 0;
  234.     
  235.     int i;
  236.     for (i = 0; i < maxIndex; i++ )
  237.     {
  238.         if ( menuSelection == dictInfoP[ i ].menuItemNumber )
  239.             return i;
  240.     }
  241.     
  242.     return 0;
  243. }
  244.  
  245. void CMacSpellChecker::ShowDialog( char *textP )
  246. {
  247.     Boolean continueChecking = StartProcessing( false );
  248.     if ( continueChecking )
  249.         continueChecking = GetNextMisspelledWord( true );
  250.     
  251.         Str255    s;
  252.         LGAPushButton *btn;
  253.         StPrepareForDialog prepare;
  254.         StBlockingDialogHandler handler( res_ID, NULL );
  255.         LDialogBox* dialog = (LDialogBox *)handler.GetDialog();
  256.         
  257.         CTextTable *listview = (CTextTable *)dialog->FindPaneByID( pane_SuggestionList );
  258.         if ( listview == NULL )
  259.             return;
  260.         
  261.         PenState penState;
  262.         ::GetPenState( &penState );
  263.         listview->AddAttachment( new LColorEraseAttachment( &penState, NULL, NULL, true ) );
  264.         
  265.         listview->FocusDraw();
  266.         listview->AddListener( &handler );
  267.         dialog->FocusDraw();
  268.         
  269.         LGAEditField *editField = (LGAEditField *)dialog->FindPaneByID( pane_NewWord );
  270.         if ( editField == NULL )
  271.             return;
  272.         
  273.         LGAPushButton *changeBtn = (LGAPushButton *)dialog->FindPaneByID( msg_Change );
  274.         if ( changeBtn == NULL )
  275.             return;
  276.         
  277.         LGAPopup *languagePopup = (LGAPopup *)dialog->FindPaneByID( msg_NewLanguage );
  278.         if ( languagePopup == NULL )
  279.             return;
  280.         languagePopup->LoadPopupMenuH();
  281.         
  282.         int curDictionary, CurrLangCode, CurrDialectCode, menuLangCode, menuDialectCode;
  283.         int numDictionaries = GetISpellChecker()->GetNumOfDictionaries();
  284.         
  285.         dictionaryInfo *dictInfoP = (dictionaryInfo *)XP_ALLOC( numDictionaries * sizeof( dictionaryInfo ) );
  286.         if ( dictInfoP == NULL )
  287.             return;
  288.         
  289.         for ( curDictionary = 0; curDictionary < numDictionaries; curDictionary++ )
  290.         {
  291.             if ( GetISpellChecker()->GetDictionaryLanguage( curDictionary, CurrLangCode, CurrDialectCode ) )
  292.                 break;
  293.             
  294.             dictInfoP[ curDictionary ].langCode = CurrLangCode;
  295.             dictInfoP[ curDictionary ].dialectCode = CurrDialectCode;
  296.             dictInfoP[ curDictionary ].menuItemNumber = curDictionary + 1;
  297.             AddDictionaryMenuItem( languagePopup->GetMacMenuH(), &dictInfoP[ curDictionary ] );
  298.         }
  299.         
  300.         GetISpellChecker()->GetCurrentLanguage( CurrLangCode, CurrDialectCode );
  301.         
  302.         // the following line resets the menu so its width gets adjusted and max value set
  303.         languagePopup->SetMacMenuH( languagePopup->GetMacMenuH() );
  304.         languagePopup->SetValue( FindDefaultMenuItem( CurrLangCode, CurrDialectCode, dictInfoP, numDictionaries ) );
  305.         
  306.         if ( continueChecking )
  307.             SetNextMisspelledWord( textP, editField, listview, dialog );
  308.  
  309.         Boolean isEditFieldOriginal, doNextWord = false, doStartOver = false;
  310.         MessageT message;
  311.         do {
  312.             LCommander *target = dialog->GetTarget();
  313.             
  314.             // if we're done checking we won't update buttons anymore
  315.             if ( continueChecking )
  316.             {
  317.                 editField->GetDescriptor( s );
  318.                 isEditFieldOriginal = ( XP_STRNCMP( (char *)s, 
  319.                                 (char *)mOrigMisspelledWord, mOrigMisspelledWord[0] + 1 ) == 0 );
  320.                 p2cstr( s );
  321.                 char *indexp = XP_STRCHR( (char *)s, ' ' );
  322.                 Boolean containsNoSpaces = indexp == NULL || indexp[0] == 0;
  323.                 // set state of buttons (enable/disable); disable when editField is target and matches origword
  324.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Change_All );
  325.                 if ( target == editField && isEditFieldOriginal )
  326.                 {
  327.                     // string hasn't changed so disable "replace" and "replace all"
  328.                     changeBtn->Disable();
  329.                     if ( btn )
  330.                         btn->Disable();
  331.                 }
  332.                 else
  333.                 {
  334.                     changeBtn->Enable();
  335.                     if ( btn )
  336.                         btn->Enable();
  337.                 }
  338.                 
  339.                 // we shouldn't be able to "learn" something that is already in the dictionary (in sugg. list)
  340.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Add_Button );
  341.                 if ( btn )
  342.                 {
  343.                     // can't learn if there's a space in the string!
  344. #ifdef LEARN_BUTTON_WORKS_AS_SPECD
  345.                     if ( target == editField && containsNoSpaces )
  346. #else
  347.                     if ( containsNoSpaces )
  348. #endif
  349.                         btn->Enable();
  350.                     else
  351.                         btn->Disable();
  352.                 }
  353.                 
  354.                 // we shouldn't be able to "check" unless the editfield is the target
  355.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Check );
  356.                 if ( btn )
  357.                 {
  358.                     // can't check if multiple words (contains a space)
  359.                     if ( target == editField && containsNoSpaces )
  360.                         btn->Enable();
  361.                     else
  362.                         btn->Disable();
  363.                 }
  364.                 
  365.                 if ( changeBtn->GetValueMessage() != msg_Change )
  366.                 {
  367.                     changeBtn->SetValueMessage( msg_Change );
  368.                     ::GetIndString( s, SpellCheckerResource, ChangeStringIndex );
  369.                     changeBtn->SetDescriptor( s );
  370.                 }
  371.  
  372.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Ignore );
  373.                 if ( btn )
  374.                     btn->Enable();
  375.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Ignore_All );
  376.                 if ( btn )
  377.                     btn->Enable();
  378.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Stop );
  379.                 if ( btn )
  380.                     btn->Enable();
  381.                 
  382.                 editField->Enable();
  383. //                listview->Enable();
  384.             }
  385.             else // done checking
  386.             {
  387.                 doStartOver = false;
  388.                 dialog->FocusDraw();
  389.                 
  390.                 if ( changeBtn->GetValueMessage() != msg_Stop )
  391.                 {
  392.                     changeBtn->SetValueMessage( msg_Stop );
  393.                     ::GetIndString( s, SpellCheckerResource, DoneStringIndex );
  394.                     changeBtn->SetDescriptor( s );
  395.                 }
  396.                 changeBtn->Enable();
  397.                 
  398.                 // disable all of the other buttons
  399.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Change_All );
  400.                 if ( btn )
  401.                     btn->Disable();
  402.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Ignore );
  403.                 if ( btn )
  404.                     btn->Disable();
  405.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Ignore_All );
  406.                 if ( btn )
  407.                     btn->Disable();
  408.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Add_Button );
  409.                 if ( btn )
  410.                     btn->Disable();
  411.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Check );
  412.                 if ( btn )
  413.                     btn->Disable();
  414.                 
  415.                 btn = (LGAPushButton *)dialog->FindPaneByID( msg_Stop );
  416.                 if ( btn )
  417.                     btn->Disable();
  418.                 
  419.                 editField->Disable();    // don't allow anymore typing
  420.                 listview->Disable();
  421.             }
  422.             
  423.             if ( doNextWord )
  424.             {
  425.                 doNextWord = false;
  426.                 dialog->FocusDraw();
  427.             
  428.                 ClearReplacementWord( editField, listview );
  429.                 if ( continueChecking )
  430.                     continueChecking = GetNextMisspelledWord( doStartOver );
  431.                 if ( continueChecking )
  432.                     SetNextMisspelledWord( NULL, editField, listview, dialog );
  433.             }
  434.             
  435.             message = handler.DoDialog();
  436.             switch ( message )
  437.             {
  438.                 case msg_Add_Button:
  439.                     editField->GetDescriptor( s );
  440.                     p2cstr( s );
  441.                     int statusError = GetISpellChecker()->AddWordToPersonalDictionary( (char *)s );
  442.                     if ( statusError )
  443.                         SysBeep(0);
  444.                     else
  445.                         ReplaceHilitedText( (char *)s, true );
  446.                     doNextWord = true;
  447.                     break;
  448.                 
  449.                 case msg_Ignore:
  450.                 case msg_Ignore_All:
  451.                     IgnoreHilitedText( message == msg_Ignore_All );
  452.                     doNextWord = true;
  453.                     break;
  454.                 
  455.                 case msg_Change:
  456.                 case msg_Change_All:
  457.                     if ( target && target == editField )
  458.                         editField->GetDescriptor( s );
  459.                     else if ( target && target == listview )
  460.                     {
  461.                         STableCell    c(0, 1);
  462.                         c = listview->GetFirstSelectedCell();
  463.                         Uint32    len = sizeof( s );
  464.                         listview->GetCellData( c, s, len );
  465.                     }
  466.                     else
  467.                         s[ 0 ] = 0;
  468.                     
  469.                     if ( s[ 0 ] != 0 )
  470.                     {
  471.                         p2cstr( s );
  472.                         ReplaceHilitedText( (char *)s, message == msg_Change_All );
  473.                     }
  474.                 
  475.                     doNextWord = true;
  476.                     break;
  477.                 
  478.                 case msg_SelectionChanged:
  479.                     break;
  480.                 
  481.                 case msg_Check:
  482.                     GetAlternativesForWord( editField, listview, dialog );
  483.                     break;
  484.                 
  485.                 case msg_NewLanguage:
  486.                     // get current menu choice; convert to langcode/dialectcode
  487.                     int index = FindDictionaryInfoIndexForMenu( languagePopup, dictInfoP, numDictionaries );
  488.                     menuLangCode = dictInfoP[ index ].langCode;
  489.                     menuDialectCode = dictInfoP[ index ].dialectCode;
  490.                     if ( CurrLangCode != menuLangCode || menuDialectCode != CurrDialectCode )    // actually changed
  491.                     {
  492.                         CurrLangCode = menuLangCode;
  493.                         CurrDialectCode = menuDialectCode;
  494.                         PREF_SetIntPref( "SpellChecker.DefaultLanguage", menuLangCode );
  495.                         PREF_SetIntPref( "SpellChecker.DefaultDialect", menuDialectCode );
  496.                         
  497.                         if ( GetISpellChecker()->SetCurrentLanguage( menuLangCode, menuDialectCode ) == 0 )
  498.                         {
  499.                             doNextWord = true;    // set flag to get next misspelled word and enable/disable
  500.                             doStartOver = true;
  501.                             continueChecking = StartProcessing( true );    // continue if any misspelled words
  502.                         }
  503.                     }
  504.                     break;
  505.                     
  506.                 case msg_EditDictionary:
  507.                     CEditDictionary *dictEditor = dynamic_cast<CEditDictionary *>
  508.                             (URobustCreateWindow::CreateWindow( CEditDictionary::res_ID, LCommander::GetTopCommander() ));
  509.                     if ( dictEditor )
  510.                         dictEditor->SetISpellChecker( GetISpellChecker() );
  511.                     break;
  512.                 }
  513.         } while ( message != msg_Stop );
  514.  
  515.         if ( mEditView )
  516.             // clear the TF_SPELL mask for any words that might still have it 
  517.             EDT_IgnoreMisspelledWord( mMWContext, NULL, true );
  518. }
  519.  
  520.  
  521. char *CMacSpellChecker::GetTextBuffer()
  522. {
  523.     char *retVal;
  524.     
  525.     if ( mEditView )
  526.         retVal = EDT_GetPositionalText( mMWContext );
  527.     else if ( mTextView )
  528.     {
  529.         SInt32 theSize = mTextView->GetTextLength();
  530.         if ( theSize == 0 )
  531.             return NULL;
  532.     
  533.         retVal = (char *)XP_ALLOC( theSize + 1 );
  534.         XP_ASSERT( retVal );
  535.         if ( retVal )
  536.         {
  537.             Handle textH = mTextView->GetTextHandle();
  538.             ::BlockMoveData( *textH, retVal, theSize );
  539.             retVal[ theSize ] = 0;
  540.         }
  541.     }
  542.     
  543.     return retVal;
  544. }
  545.  
  546.  
  547. void CMacSpellChecker::GetSelection(int32 &selStart, int32 &selEnd)
  548. {
  549.     if (mEditView)
  550.     {
  551.         char *pSelection = (char *)LO_GetSelectionText( GetMWContext() );
  552.         if ( pSelection != NULL )
  553.         {
  554.             XP_FREE( pSelection );
  555.             EDT_GetSelectionOffsets( GetMWContext(), &selStart, &selEnd );
  556.         }
  557.     }
  558.     else
  559.         mTextView->GetSelection( &selStart, &selEnd );
  560. }
  561.  
  562.  
  563. Boolean CMacSpellChecker::StartProcessing( Boolean startOver )
  564. {
  565.     char *stringToCheck;
  566.     char *misspelledWord = NULL;
  567.     
  568.     stringToCheck = GetTextBuffer();
  569.     Boolean retVal = stringToCheck != NULL;
  570.     
  571.     if ( stringToCheck )
  572.     {
  573.         if ( mEditView )
  574.             EDT_SetRefresh( mMWContext, false );
  575.  
  576.         if ( startOver )
  577.             GetISpellChecker()->SetNewBuf( stringToCheck, true );
  578.         else
  579.         {
  580.             // setting buffer the first time
  581.             // let the spellchecker know the current selection
  582.             int32 selStart = 0, selEnd = 0;
  583.             GetSelection( selStart, selEnd );
  584.             GetISpellChecker()->SetBuf( stringToCheck, selStart, selEnd );
  585.         }
  586.         
  587.         XP_HUGE_FREE( stringToCheck );
  588.  
  589.         if ( mEditView )
  590.         {
  591.             // clear the TF_SPELL mask for any words that might still have it 
  592.             EDT_IgnoreMisspelledWord( mMWContext, NULL, true );
  593.             
  594.             // mark misspelled words
  595.             EDT_CharacterData *pData = EDT_NewCharacterData();
  596.             if ( pData )
  597.             {
  598.                 pData->mask = TF_SPELL;
  599.                 pData->values = TF_SPELL;
  600.                 
  601.                 unsigned long StartPos = 0, len = 0;
  602.                 while ( GetISpellChecker()->GetNextMisspelledWord( StartPos, len ) == 0 )
  603.                     EDT_SetCharacterDataAtOffset( GetMWContext(), pData, StartPos, len );
  604.                 
  605.                 EDT_FreeCharacterData( pData );
  606.             }
  607.  
  608.             // set cursor position at the beginning so that
  609.             // EDT_SelectNextMisspelledWord will start at beginning
  610.             EDT_BeginOfDocument( mMWContext, false );
  611.             EDT_SetRefresh( mMWContext, true );
  612.         }
  613.     }
  614.     
  615.     return retVal;
  616. }
  617.  
  618.  
  619. Boolean CMacSpellChecker::GetNextMisspelledWord( Boolean startOver )
  620. {
  621.     if ( isHTMLeditor() )
  622.     {
  623.         if ( startOver )
  624.             return EDT_SelectFirstMisspelledWord( GetMWContext() );
  625.         else
  626.             return EDT_SelectNextMisspelledWord( GetMWContext() );
  627.     }
  628.     else
  629.     {
  630.         unsigned long StartPos = 0, Len = 0;
  631.         if ( GetISpellChecker()->GetNextMisspelledWord( StartPos, Len ) == 0 )
  632.         {
  633.             mTextView->FocusDraw();
  634.             mTextView->SetSelection( StartPos, StartPos + Len );
  635.             
  636.             return true;
  637.         }
  638.         else    // no more misspelled words
  639.             return false;
  640.     }
  641.     
  642.     return false;
  643. }
  644.  
  645.  
  646. void CMacSpellChecker::SetNextMisspelledWord( char *textP, LEditField *typoField, 
  647.                                             CTextTable *table, LCommander *commander )
  648. {
  649.     Boolean    doFree = false;
  650.  
  651.     if ( textP == NULL )
  652.     {
  653.         if ( isHTMLeditor() )
  654.         {
  655.             textP = (char *)LO_GetSelectionText( GetMWContext() );
  656.             doFree = ( textP != NULL );
  657.         }
  658.         else
  659.         {
  660.             SInt32    selStart = 0;
  661.             SInt32    selEnd   = 0;
  662.             SInt32     selLen     = 0;
  663.             
  664.             mTextView->FocusDraw();
  665.             mTextView->GetSelection( &selStart, &selEnd );
  666.             selLen = selEnd - selStart;
  667.             
  668.             if ( selLen > 0 )
  669.             {
  670.                 textP = (char *)XP_ALLOC( selLen + 1 );
  671.                 XP_ASSERT( textP != NULL );
  672.                 
  673.                 doFree = ( textP != NULL );
  674.                 
  675.                 if ( textP )
  676.                 {
  677.                     Handle textH = mTextView->GetTextHandle();
  678.                     ::BlockMoveData( (*textH) + selStart, textP, selLen );
  679.                     textP[ selLen ] = 0;
  680.                 }
  681.             }
  682.         }
  683.     }
  684.     
  685.     // convert c-string to pascal-string
  686.     CStr255 str( textP );
  687.     if ( typoField )
  688.     {
  689.         typoField->FocusDraw();
  690.         typoField->SetDescriptor( str );
  691.         typoField->SelectAll();
  692.         // re-set misspelledword member
  693.         typoField->GetDescriptor( mOrigMisspelledWord );
  694.     }
  695.  
  696.     if ( table )
  697.     {
  698.         table->FocusDraw();
  699.     
  700.         STableCell    cell(0, 1);
  701.         int numRows = GetISpellChecker()->GetNumAlternatives( textP );
  702.         if ( numRows > 0 )
  703.         {
  704.             table->Enable();
  705.             table->InsertRows( numRows, 0, nil, 0, Refresh_Yes );
  706.         
  707.             int i, len;
  708.             for ( i = 1; i <= numRows; i++ )
  709.             {
  710.                 char liststr[256];
  711.                 if ( GetISpellChecker()->GetAlternative( i - 1, liststr, 255 ) == 0 )
  712.                 {
  713.                     len = XP_STRLEN( (char *)liststr );
  714.                     if ( len > 0 )
  715.                     {
  716.                         cell.row = i;
  717.                         CStr255 pascalstring( liststr );
  718.                         table->SetCellData( cell, pascalstring, len + 1 );
  719.                     }
  720.                 }
  721.             }
  722.  
  723.             cell.row = 1;
  724.             table->SelectCell( cell );
  725.             if ( commander )
  726.                 commander->SwitchTarget( table );
  727.         }
  728.         else
  729.         {
  730.             table->InsertRows( 1, 0, nil, 0, Refresh_Yes );
  731.             
  732.             Str255 noneFoundString;
  733.             noneFoundString[0] = 0;
  734.             ::GetIndString( noneFoundString, SpellCheckerResource, 31 );
  735.             cell.row = 1;
  736.             table->SetCellData( cell, noneFoundString, noneFoundString[0] + 1 );
  737.             
  738.             table->Disable();
  739.             
  740.             if ( commander && typoField )
  741.                 commander->SwitchTarget( typoField );
  742.         }
  743.     }
  744.     
  745.     if ( doFree )
  746.         XP_FREE( textP );
  747. }
  748.  
  749.  
  750. void CMacSpellChecker::ClearReplacementWord( LEditField *newWord, CTextTable *table )
  751. {
  752.     if ( newWord )
  753.         newWord->SetDescriptor( CStr255::sEmptyString );
  754.     
  755.     if ( table )
  756.     {
  757.         TableIndexT rows = 0, cols = 0;
  758.         table->GetTableSize( rows, cols );
  759.         if ( rows )
  760.         {
  761.             table->FocusDraw();
  762.             table->RemoveRows( rows, 0, Refresh_Yes );
  763.         }
  764.     }
  765. }
  766.  
  767.  
  768. // this function is used only for "Check" button
  769. void CMacSpellChecker::GetAlternativesForWord( LEditField *newWord, CTextTable *table, LCommander *commander )
  770. {
  771.     Str255    s;
  772.     if ( newWord )
  773.         newWord->GetDescriptor( s );
  774.     p2cstr( s );
  775.     
  776.     if ( table )
  777.     {
  778.         table->FocusDraw();
  779.     
  780.         // clear previous choices (if any)
  781.         TableIndexT rows = 0, cols = 0;
  782.         table->GetTableSize( rows, cols );
  783.         if ( rows )
  784.             table->RemoveRows( rows, 0, Refresh_Yes );
  785.         
  786.         // add new list of choices (if any)
  787.         STableCell cell(0, 1);
  788.         if ( GetISpellChecker()->CheckWord( (char *)s ) )
  789.         {
  790.             // word spelled correctly; no need to get alternatives
  791.             table->InsertRows( 1, 0, nil, 0, Refresh_Yes );
  792.             
  793.             Str255 spelledRightString;
  794.             spelledRightString[0] = 0;
  795.             ::GetIndString( spelledRightString, SpellCheckerResource, 32 );
  796.             cell.row = 1;
  797.             table->SetCellData( cell, spelledRightString, spelledRightString[0] + 1 );
  798.             table->Disable();
  799.             
  800.             if ( commander && newWord )
  801.                 commander->SwitchTarget( newWord );
  802.             
  803.             return;
  804.         }
  805.         
  806.         int numrows = GetISpellChecker()->GetNumAlternatives( (char *)s );
  807.         if ( numrows > 0 )
  808.         {
  809.             table->Enable();
  810.             table->InsertRows( numrows, 0, nil, 0, Refresh_Yes );
  811.         
  812.             char liststr[256];
  813.             int i, len;
  814.             for ( i = 1; i <= numrows; i++ )
  815.             {
  816.                 if ( GetISpellChecker()->GetAlternative( i - 1, liststr, sizeof(s) ) == 0 )
  817.                 {
  818.                     len = XP_STRLEN( liststr );
  819.                     if ( len > 0 )
  820.                     {
  821.                         cell.row = i;
  822.                         CStr255 pascalstring( liststr );
  823.                         table->SetCellData( cell, pascalstring, len + 1 );
  824.                     }
  825.                 }
  826.             }
  827.             
  828.             cell.row = 1;
  829.             table->SelectCell( cell );
  830.             if ( commander )
  831.                 commander->SwitchTarget( table );
  832.         }
  833.         else
  834.         {
  835.             table->InsertRows( 1, 0, nil, 0, Refresh_Yes );
  836.             
  837.             Str255 noneFoundString;
  838.             noneFoundString[0] = 0;
  839.             ::GetIndString( noneFoundString, SpellCheckerResource, 31 );
  840.             cell.row = 1;
  841.             table->SetCellData( cell, noneFoundString, noneFoundString[0] + 1 );
  842.             table->Disable();
  843.             
  844.             if ( commander && newWord )
  845.                 commander->SwitchTarget( newWord );
  846.         }
  847.     }
  848. }
  849.  
  850.  
  851. #pragma mark -
  852.  
  853. // returns success (true) or failure (false)
  854. static Boolean LocateDictionaries( FSSpec *mainDictionary, FSSpec *personalDictionary )
  855. {
  856.     FSSpec appFolder, userPrefsFolder, goodFS;
  857.     Str255    str;
  858.     OSErr    err;
  859.     
  860.     // assume main dictionary and netscape dictionary are next to each other
  861.     // get that netscape dictionary FSSpec
  862.     appFolder = CPrefs::GetFilePrototype( CPrefs::RequiredGutsFolder );        // CPrefs::NetscapeFolder
  863.     // get netscape dictionary name
  864.     ::GetIndString( str, SpellCheckerResource, NSDictionaryNameIndex );
  865.     if ( str[ 0 ] == 0 )
  866.         return false;
  867.     if ( str[ 0 ] > MAX_MAC_FILENAME )
  868.         str[ 0 ] = MAX_MAC_FILENAME;
  869.     
  870.     strncpy( (char *)&appFolder.name, (char *)str, str[0] + 1 );
  871.     err = FSMakeFSSpec( appFolder.vRefNum, appFolder.parID, appFolder.name, &goodFS );
  872.     if ( err == noErr )
  873.         memcpy(mainDictionary, &goodFS, sizeof(FSSpec));
  874.     else
  875.         return false;
  876.  
  877.     // check for custom dictionary (may or may not be present)
  878.     // assume in User Profile folder
  879.     Boolean isValidPath = false;
  880.     char *prefMacPath = NULL;
  881.     PREF_CopyCharPref("SpellChecker.PersonalDictionary", &prefMacPath); 
  882.     if ( prefMacPath && *prefMacPath )
  883.     {
  884.         err = CFileMgr::FSSpecFromPathname( prefMacPath, &goodFS );
  885.         if ( err == noErr )
  886.             isValidPath = true;
  887.     }
  888.     
  889.     XP_FREEIF( prefMacPath );
  890.     
  891.     if ( ! isValidPath )
  892.     {
  893.         userPrefsFolder = CPrefs::GetFilePrototype( CPrefs::MainFolder );
  894.         // get user dictionary name
  895.         ::GetIndString( str, SpellCheckerResource, UserDictionaryNameIndex );
  896.         if ( str[ 0 ] == 0 )
  897.             return false;
  898.         if ( str[ 0 ] > MAX_MAC_FILENAME )
  899.             str[ 0 ] = MAX_MAC_FILENAME;
  900.         
  901.         strncpy( (char *)&userPrefsFolder.name, (char *)str, str[0] + 1 );
  902.         err = FSMakeFSSpec( userPrefsFolder.vRefNum, userPrefsFolder.parID, userPrefsFolder.name, &goodFS );
  903.     }
  904.     
  905.     memcpy(personalDictionary, &goodFS, sizeof(FSSpec));
  906.     if ( err == fnfErr )
  907.         return true;
  908.     if ( err != noErr )
  909.         return false;
  910.     
  911.     return true;
  912. }
  913.  
  914. #ifndef USE_DYNAMIC_SC_LIB
  915. /*    The direct interface to PR_FindSymbol("SC_Create") and PR_FindSymbol("SC_Destroy"):
  916.     These prototypes can't be found in any header file; they're defined in an unreleased source file.
  917.     It's not _too_ unsafe to define them separately here, because the interface understood
  918.     for these functions is the one which uses PR_FindSymbol, which naturally allows
  919.     no prototypes anyway.
  920. */
  921. extern "C" ISpellChecker * SCAPI SC_Create();
  922. extern "C" void SCAPI SC_Destroy(ISpellChecker *pSpellChecker);
  923. #endif
  924.  
  925. void do_spellcheck( MWContext *mwcontext, CEditView *editView, CSimpleTextView *textView )
  926. {
  927.     if ( editView == NULL && textView == NULL )
  928.         return;
  929.     
  930.     // editor requires a context
  931.     if ( mwcontext == NULL && editView != NULL)
  932.         return;
  933.     
  934.     // locate dictionaries
  935.     FSSpec mainDictionary, personalDictionary;
  936.     try {
  937.         if ( ! LocateDictionaries( &mainDictionary, &personalDictionary ) )
  938.         {
  939.             // display message:  unable to locate dictionary!
  940.             ErrorManager::PlainAlert( NO_DICTIONARY_FOUND );
  941.             return;
  942.         }
  943.     }
  944.     catch(...)
  945.     {
  946.         // display message:  unable to locate dictionary!
  947.         ErrorManager::PlainAlert( NO_DICTIONARY_FOUND );
  948.         return;
  949.     }
  950.     
  951. #ifdef USE_DYNAMIC_SC_LIB
  952.     const char *libPath = PR_GetLibraryPath();
  953.     PR_SetLibraryPath( "/usr/local/netscape/" );
  954.     
  955.     // load library
  956.     char *libname = "NSSpellChecker";    // internal string within SHLB
  957.     PRLibrary *spellCheckerLib = PR_LoadLibrary( libname );
  958.     
  959.     // set path back to original path (don't have ourselves in list)
  960.     PR_SetLibraryPath( libPath );
  961.  
  962.     if ( spellCheckerLib == NULL )
  963.     {
  964.         // failed!
  965.         ErrorManager::PlainAlert( NO_SPELL_SHLB_FOUND );
  966.         return;
  967.     }
  968. #else
  969.     PRLibrary *spellCheckerLib = NULL;
  970. #endif
  971.         
  972.     ISpellChecker *pSpellChecker = NULL;
  973.     CMacSpellChecker *macSpellChecker = NULL;
  974.     
  975.     try    {
  976. #ifdef USE_DYNAMIC_SC_LIB
  977.         sc_create_func sc_createProc;
  978. #ifndef NSPR20
  979.         sc_createProc = (sc_create_func)PR_FindSymbol( "SC_Create", spellCheckerLib );
  980. #else
  981.         sc_createProc = (sc_create_func)PR_FindSymbol( spellCheckerLib, "SC_Create" );
  982. #endif
  983.         if ( sc_createProc == NULL )
  984.         {
  985.             ErrorManager::PlainAlert( NO_SPELL_SHLB_FOUND );
  986.             exit_spellchecker( spellCheckerLib, pSpellChecker, macSpellChecker );
  987.             return;
  988.         }
  989.         
  990.         pSpellChecker = sc_createProc();
  991. #else
  992.         pSpellChecker = SC_Create();
  993. #endif
  994.         if ( pSpellChecker )
  995.         {
  996.             int    err;
  997.             
  998.             macSpellChecker = new CMacSpellChecker( mwcontext, editView, textView );
  999.  
  1000.             int32 Language = 0;
  1001.             int32 Dialect = 0;
  1002.             PREF_GetIntPref( "SpellChecker.DefaultLanguage", &Language );
  1003.             PREF_GetIntPref( "SpellChecker.DefaultDialect", &Dialect );
  1004.  
  1005.             err = pSpellChecker->Initialize( Language, Dialect, 
  1006.                                     (char *)&mainDictionary, 
  1007.                                     (char *)&personalDictionary );
  1008.             if ( err == noErr )
  1009.             {
  1010.                 macSpellChecker->SetISpellChecker( pSpellChecker );
  1011.                 macSpellChecker->ShowDialog( NULL );
  1012.             }
  1013.         }
  1014.     }
  1015.     catch(...)
  1016.     {
  1017.         exit_spellchecker( spellCheckerLib, pSpellChecker, macSpellChecker );
  1018.         throw;
  1019.     }
  1020.  
  1021.     exit_spellchecker( spellCheckerLib, pSpellChecker, macSpellChecker );
  1022. }
  1023.  
  1024.  
  1025. static void exit_spellchecker( PRLibrary *lib, ISpellChecker *pSpellChecker, CMacSpellChecker *macSpellChecker )
  1026. {
  1027.     if ( pSpellChecker )
  1028.     {
  1029. #ifdef USE_DYNAMIC_SC_LIB
  1030.         sc_destroy_func sc_destroyProc;
  1031. #ifndef NSPR20
  1032.         sc_destroyProc = (sc_destroy_func)PR_FindSymbol( "SC_Destroy", lib );
  1033. #else
  1034.         sc_destroyProc = (sc_destroy_func)PR_FindSymbol( lib, "SC_Destroy" );
  1035. #endif
  1036.         if ( sc_destroyProc != NULL )
  1037.             sc_destroyProc( pSpellChecker );
  1038. #else
  1039.         SC_Destroy( pSpellChecker );
  1040. #endif
  1041.     }
  1042.     
  1043.     if ( macSpellChecker )
  1044.         delete macSpellChecker;
  1045.     macSpellChecker = NULL;
  1046.     
  1047.     // unload library
  1048.     if ( lib )
  1049.         int err = PR_UnloadLibrary( lib );
  1050. }
  1051.