/* AbiWord * Copyright (C) 1998,1999 AbiSource, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "ut_string.h" #include "ut_assert.h" #include "ut_debugmsg.h" #include #include "xap_App.h" #include "xap_BeOSApp.h" #include "ap_Strings.h" #include "ap_Dialog_Id.h" #include "ap_Dialog_Spell.h" #include "ap_BeOSDialog_Spell.h" #include "ut_Rehydrate.h" // We really shouldn't unlock the window lock, but the redraw when we focus on the next // misspelled word will deadlock if we don't.. status_t WaitForDelete(sem_id blocker) { status_t result; thread_id this_tid = find_thread(NULL); BLooper *pLoop; BWindow *pWin = 0; pLoop = BLooper::LooperForThread(this_tid); if (pLoop) pWin = dynamic_cast(pLoop); // block until semaphore is deleted (modal is finished) if (pWin) { do { // update the window periodically pWin->Unlock(); // Who will know?=) snooze(100); pWin->Lock(); pWin->UpdateIfNeeded(); result = acquire_sem_etc(blocker, 1, B_TIMEOUT, 1000); } while (result != B_BAD_SEM_ID); } else { do { // just wait for exit result = acquire_sem(blocker); } while (result != B_BAD_SEM_ID); } return result; } /*****************************************************************/ class SpellWin:public BWindow { public: SpellWin(BMessage *data); void SetDlg(AP_BeOSDialog_Spell *dlg); virtual void DispatchMessage(BMessage *msg, BHandler *handler); virtual bool QuitRequested(void); private: AP_BeOSDialog_Spell *m_DlgSpell; void _showMisspelledWord(); void _suggestChange(); void _toggleChangeButtons(bool shouldShow); void _change(); void _tryAgain(); void _changeAll(); rgb_color normalText, badText; sem_id modalSem; }; SpellWin::SpellWin(BMessage *data) :BWindow(data) { } void SpellWin::SetDlg(AP_BeOSDialog_Spell *dlg) { m_DlgSpell = dlg; // As nice as using InterfaceElements is, the app doesn't properly set up // the textRect for our BTextView.. BTextView* sentenceView = (BTextView *)FindView("sentencedisplay"); Lock(); BRect frameRect = sentenceView->Bounds(); frameRect.InsetBy(5,5); sentenceView->SetTextRect(frameRect); normalText.red = normalText.blue = normalText.green = 0; normalText.alpha = 255; badText.red = 255; badText.blue = badText.green = 0; badText.alpha = 255; bool bRes = m_DlgSpell->nextMisspelledWord(); if(!bRes) return; // set initial state m_DlgSpell->makeWordVisible(); _showMisspelledWord(); // This semaphore ties up the window until after it deletes.. modalSem = create_sem(0,"SpellModalSem"); Unlock(); Show(); WaitForDelete(modalSem); } void SpellWin::_suggestChange() { BListView* suggestList = (BListView *)FindView("suggestlist"); BTextControl* changeText = (BTextControl *)FindView("changetxt"); m_DlgSpell->m_iSelectedRow = suggestList->CurrentSelection(); if (!m_DlgSpell->m_Suggestions->size()) { // FIXME: this should insert the original word, surely! // no change to suggest, ignore it if (m_DlgSpell->m_iSelectedRow != -1) suggestList->Select(-1); } else { // copy suggestion to edit field UT_ASSERT((m_DlgSpell->m_iSelectedRow > -1)); BStringItem* selectedItem = (BStringItem *)suggestList->ItemAt(m_DlgSpell->m_iSelectedRow); changeText->SetText(selectedItem->Text()); } } void SpellWin::_change(void) { UT_UCSChar * replace = NULL; BTextControl* changeText = (BTextControl *)FindView("changetxt"); if (m_DlgSpell->m_iSelectedRow != -1) { replace = static_cast(m_DlgSpell->m_Suggestions->getNthItem(m_DlgSpell->m_iSelectedRow)); m_DlgSpell->changeWordWith(replace); } else { UT_UCS4_cloneString_char(&replace, changeText->Text()); if (!UT_UCS4_strlen(replace)) { return; } m_DlgSpell->changeWordWith(replace); } _tryAgain(); } void SpellWin::_tryAgain(void) { BListView* suggestList = (BListView *)FindView("suggestlist"); // clear prior suggestions m_DlgSpell->_purgeSuggestions(); int32 numItems = suggestList->CountItems(); // Clear out the dialog for ( int32 i = 0; i < numItems; i++ ) { BListItem* pItem = suggestList->RemoveItem((long int)0); delete pItem; } // what's next bool bRes = m_DlgSpell->nextMisspelledWord(); if (bRes) { // show word in main window m_DlgSpell->makeWordVisible(); // update dialog with new misspelled word info/suggestions _showMisspelledWord(); } else { PostMessage(B_QUIT_REQUESTED); } } void SpellWin::_changeAll(void) { BTextControl* changeText = (BTextControl *)FindView("changetxt"); UT_UCSChar * replace = NULL; if (m_DlgSpell->m_iSelectedRow != -1) { replace = (UT_UCSChar*) m_DlgSpell->m_Suggestions->getNthItem(m_DlgSpell->m_iSelectedRow); m_DlgSpell->addChangeAll(replace); m_DlgSpell->changeWordWith(replace); } else { UT_UCS4_cloneString_char(&replace,changeText->Text()); if (!UT_UCS4_strlen(replace)) { return; } m_DlgSpell->addChangeAll(replace); m_DlgSpell->changeWordWith(replace); } _tryAgain(); } void SpellWin::_showMisspelledWord(void) { const UT_UCSChar *p; UT_uint32 len; char * buf; UT_uint32 sum = 0; BTextView* sentenceView = (BTextView *)FindView("sentencedisplay"); BListView* suggestList = (BListView *)FindView("suggestlist"); sentenceView->SetText(""); sentenceView->MakeEditable(true); // Run array is 3 long because begin sentence black, misspelled word red, end sentence black.. // Note: We need to delete this when we are done.. text_run_array* array = (text_run_array *)malloc(sizeof(text_run_array) + sizeof(text_run) * 3); array->count = 3; array->runs[0].offset = 0; array->runs[0].font = be_plain_font; array->runs[0].color = normalText; array->runs[1].font = be_bold_font; array->runs[1].color = badText; array->runs[2].font = be_plain_font; array->runs[2].color = normalText; // insert start of sentence UT_sint32 iLength; // FIXME: broken // p = m_pWordIterator->getPreWord(iLength); if (0 < iLength) { // FIXME: this is broken - should take iLength characters from p // and convert them to char. I.e., first substr p, then // convert. But output length is not necessarily related to iLength! buf = new char [iLength + 1]; UT_UCS4_strncpy_to_char(buf, p, iLength); buf[iLength] = '\0'; sentenceView->Insert(buf, (long int)iLength); DELETEP(buf); } sum += iLength; array->runs[1].offset = sum; // FIXME: broken // p = m_pWordIterator->getCurrentWord(iLength); if (0 < iLength) { // FIXME: this is broken - should take iLength characters from p // and convert them to char. I.e., first substr p, then // convert. But output length is not necessarily related to iLength! buf = new char [iLength + 1]; UT_UCS4_strncpy_to_char(buf, p, iLength); buf[iLength] = '\0'; sentenceView->Insert(sum, buf, (long int)iLength); DELETEP(buf); } sum += iLength; array->runs[2].offset = sum; // FIXME: broken // p = m_pWordIterator->getPostWord(iLength); if (0 < iLength) { // FIXME: this is broken - should take iLength characters from p // and convert them to char. I.e., first substr p, then // convert. But output length is not necessarily related to iLength! buf = new char [iLength + 1]; UT_UCS4_strncpy_to_char(buf, p, iLength); buf[iLength] = '\0'; sentenceView->Insert(sum, buf, (long int)iLength); DELETEP(buf); } sum += iLength; sentenceView->SetRunArray(0, (long int)sum, array); sentenceView->MakeEditable(false); // insert suggestions if (!m_DlgSpell->m_Suggestions->size()) { const XAP_StringSet * pSS = m_DlgSpell->m_pApp->getStringSet(); BStringItem* emptyItem = new BStringItem(pSS->getValue(AP_STRING_ID_DLG_Spell_NoSuggestions)); suggestList->AddItem(emptyItem); m_DlgSpell->m_iSelectedRow = -1; _toggleChangeButtons(false); } else { for (int i = 0; i < m_DlgSpell->m_Suggestions->size(); i++) { p = (UT_UCSChar *) m_DlgSpell->m_Suggestions->getNthItem(i); len = UT_UCS4_strlen(p); if (len) { buf = new char [len + 1]; UT_UCS4_strcpy_to_char(buf, p); BStringItem* pItem = new BStringItem(buf); suggestList->AddItem(pItem); DELETEP(buf); } } m_DlgSpell->m_iSelectedRow = 0; _toggleChangeButtons(true); } suggestList->Select(0); _suggestChange(); } void SpellWin::DispatchMessage(BMessage *msg, BHandler *handler) { BTextControl* editSuggest; BListView* suggestList; int32 selIndex; BStringItem* pCurSelItem; switch(msg->what) { case 'ibut': // ignore button m_DlgSpell->ignoreWord(); _tryAgain(); break; case 'iabu': // ignore all button. m_DlgSpell->addIgnoreAll(); m_DlgSpell->ignoreWord(); _tryAgain(); break; case 'abut': // add button m_DlgSpell->addToDict(); m_DlgSpell->ignoreWord(); _tryAgain(); break; case 'cbut': _change(); break; case 'cabu': _changeAll(); break; case 'selc': // Update the current selection in the add box.. suggestList = (BListView *)FindView("suggestlist"); editSuggest = (BTextControl *)FindView("changetxt"); m_DlgSpell->m_iSelectedRow = suggestList->CurrentSelection(); selIndex = m_DlgSpell->m_iSelectedRow; pCurSelItem = (BStringItem *)suggestList->ItemAt(m_DlgSpell->m_iSelectedRow); if(pCurSelItem) editSuggest->TextView()->SetText(pCurSelItem->Text()); break; case 'invs': // Called when the user double-clicks a suggested word in the spell dialog. suggestList = (BListView *)FindView("suggestlist"); if( msg->FindInt32("index" , &selIndex) == B_OK) { m_DlgSpell->m_iSelectedRow = selIndex; _change(); } break; case 'spmo': // Whenever the user edits the text control. // Determine if the user clicked an item in the suggest box. suggestList = (BListView *)FindView("suggestlist"); editSuggest = (BTextControl *)FindView("changetxt"); for(int i = 0; i < suggestList->CountItems(); i ++) { pCurSelItem = (BStringItem *)suggestList->ItemAt(i); if(strcmp(pCurSelItem->Text() , editSuggest->Text()) == 0) { return;; } } // The user is editing it so, enable the change button. _toggleChangeButtons(true); suggestList->DeselectAll(); m_DlgSpell->m_iSelectedRow = -1; break; default: BWindow::DispatchMessage(msg, handler); } } //Behave like a good citizen bool SpellWin::QuitRequested() { delete_sem(modalSem); return(true); } void SpellWin::_toggleChangeButtons(bool shouldShow) { BButton *change, *changeall; change = (BButton *)FindView("change"); changeall = (BButton *)FindView("changeall"); if(change) change->SetEnabled(shouldShow); if(changeall) changeall->SetEnabled(shouldShow); } /*****************************************************************/ XAP_Dialog * AP_BeOSDialog_Spell::static_constructor(XAP_DialogFactory * pFactory, XAP_Dialog_Id id) { AP_BeOSDialog_Spell * p = new AP_BeOSDialog_Spell(pFactory,id); return p; } AP_BeOSDialog_Spell::AP_BeOSDialog_Spell(XAP_DialogFactory * pDlgFactory, XAP_Dialog_Id id) : AP_Dialog_Spell(pDlgFactory,id) { } AP_BeOSDialog_Spell::~AP_BeOSDialog_Spell(void) { } /************************************************************/ void AP_BeOSDialog_Spell::runModal(XAP_Frame * pFrame) { // call the base class method to initialize some basic xp stuff AP_Dialog_Spell::runModal(pFrame); m_bCancelled = false; BMessage msg; SpellWin *newwin; if (RehydrateWindow("SpellWindow", &msg)) { newwin = new SpellWin(&msg); newwin->SetDlg(this); //Take the information here ... // newwin->Close(); QuitRequested kills this dialog.. } }