void SpellCheckerPlugin::OnEditorTooltip(CodeBlocksEvent& event)
{
    if (   !IsAttached() || wxGetKeyState(WXK_CONTROL)
        || !(m_sccfg->GetEnableSpellTooltips() || m_sccfg->GetEnableThesaurusTooltips()))
    {
        event.Skip();
        return;
    }
    EditorBase* base = event.GetEditor();
    cbEditor* ed = base && base->IsBuiltinEditor() ? static_cast<cbEditor*>(base) : 0;
    if (   !ed || ed->IsContextMenuOpened()
        || wxWindow::FindFocus() != static_cast<wxWindow*>(ed->GetControl()) )
    {
        event.Skip();
        return;
    }
    cbStyledTextCtrl* stc = ed->GetControl();
    if (!stc)
        return;
    int pos = stc->PositionFromPointClose(event.GetX(), event.GetY());
    if (pos < 0 || pos >= stc->GetLength())
    {
        event.Skip();
        return;
    }

    wxString tip;
    int wordstart = pos, wordend = pos;
    while (wordstart)
    {
        if ( m_pSpellHelper->IsWhiteSpace( stc->GetCharAt(wordstart - 1) ) )
            break;
        --wordstart;
    }
    while ( wordend < stc->GetLength() )
    {
        if ( m_pSpellHelper->IsWhiteSpace( stc->GetCharAt(++wordend) ) )
            break;
    }
    int tipWidth = 0;
    if (   m_sccfg->GetEnableSpellTooltips()
        && m_pSpellChecker->IsInitialized()
        && stc->IndicatorValueAt(m_pOnlineChecker->GetIndicator(), pos))
    {
        // indicator is on -> check if we can find a suggestion
        wxString misspelledWord = stc->GetTextRange(wordstart, wordend);
        m_suggestions = m_pSpellChecker->GetSuggestions(misspelledWord);
        if (!m_suggestions.IsEmpty())
        {
            // allow maximum 12 entries in 3 rows
            int lineWidth = 0;
            for (size_t i = 0; i < 12 && i < m_suggestions.size(); ++i)
            {
                tip << m_suggestions[i];
                lineWidth += m_suggestions[i].Length();
                if (i % 4 == 3)
                {
                    tip << wxT(",\n");
                    if (lineWidth > tipWidth)
                        tipWidth = lineWidth;
                    lineWidth = 0;
                }
                else
                {
                    tip << wxT(", ");
                    lineWidth += 2;
                }
            }
            tip.RemoveLast(2);
            lineWidth -= 2;
            if (lineWidth > tipWidth) // in case the last line was not full, and thereby not checked
                tipWidth = lineWidth;
        }
    }
    else if (   m_sccfg->GetEnableThesaurusTooltips()
             && m_pThesaurus->IsOk()
             && m_pSpellHelper->HasStyleToBeChecked(ed->GetColourSet()->GetLanguageName(ed->GetLanguage()), event.GetInt()))
    {
        wxString word = stc->GetTextRange(wordstart, wordend);
        synonyms syn = m_pThesaurus->GetSynonyms(word);
        if (!syn.size()) // if not found, try lower case
            syn = m_pThesaurus->GetSynonyms(word.Lower());
        if (syn.size())
        {
            wxArrayString usedSyns; // avoid duplicate synonyms
            // allow maximum 12 entries in 4 rows
            synonyms::iterator it = syn.begin();
            for (size_t i = 0; i < 4 && it != syn.end(); ++i, ++it)
            {
                wxString tipLine(it->first + wxT(": "));
                std::vector< wxString > syns = syn[it->first];
                size_t j = 0;
                for (size_t k = 0; k < 3 && j < syns.size(); ++j, ++k)
                {
                    if (usedSyns.Index(syns[j]) == wxNOT_FOUND)
                    {
                        tipLine << syns[j] << wxT(", ");
                        usedSyns.Add(syns[j]);
                    }
                    else
                        --k; // synonym already listed, look for another word
                }
                tipLine.RemoveLast(2);
                if (tipLine.Length() > static_cast<size_t>(tipWidth))
                    tipWidth = tipLine.Length();
                tip << tipLine << wxT("\n");
            }
            tip.RemoveLast();
        }
    }

    if (tip.IsEmpty())
    {
        event.Skip();
        return;
    }

    if (stc->CallTipActive())
        stc->CallTipCancel();
    // calculation from CC
    const int lnStart = stc->PositionFromLine(stc->LineFromPosition(pos));
                  // pos - lnStart   == distance from start of line
                  //  + tipWidth + 1 == projected virtual position of tip end (with a 1 character buffer) from start of line
                  //  - (width_of_editor_in_pixels / width_of_character) == distance tip extends past window edge
                  //       horizontal scrolling is accounted for by PointFromPosition().x
    const int offset = tipWidth + pos + 1 - lnStart -
                       (stc->GetSize().x - stc->PointFromPosition(lnStart).x) /
                       stc->TextWidth(wxSCI_STYLE_LINENUMBER, _T("W"));
    if (offset > 0)
        pos -= offset;
    if (pos < lnStart) // do not go to previous line if tip is wider than editor
        pos = lnStart;

    stc->CallTipShow(pos, tip);
    event.SetExtraLong(1); // notify CC not to cancel this tooltip
    event.Skip();
}
Exemplo n.º 2
0
// cbEVT_EDITOR_TOOLTIP
void CCManager::OnEditorTooltip(CodeBlocksEvent& event)
{
    event.Skip();

    if (wxGetKeyState(WXK_CONTROL))
        return;

    EditorBase* base = event.GetEditor();
    cbEditor* ed = base && base->IsBuiltinEditor() ? static_cast<cbEditor*>(base) : nullptr;
    if (!ed || ed->IsContextMenuOpened())
        return;

    cbStyledTextCtrl* stc = ed->GetControl();
    cbCodeCompletionPlugin* ccPlugin = GetProviderFor(ed);
    int pos = stc->PositionFromPointClose(event.GetX(), event.GetY());
    if (!ccPlugin || pos < 0 || pos >= stc->GetLength())
    {
        if (stc->CallTipActive() && event.GetExtraLong() == 0 && m_CallTipActive == wxSCI_INVALID_POSITION)
            static_cast<wxScintilla*>(stc)->CallTipCancel();
        return;
    }

    int hlStart, hlEnd, argsPos;
    hlStart = hlEnd = argsPos = wxSCI_INVALID_POSITION;
    bool allowCallTip = true;
    const std::vector<cbCodeCompletionPlugin::CCToken>& tokens = ccPlugin->GetTokenAt(pos, ed, allowCallTip);
    std::set<wxString> uniqueTips;
    for (size_t i = 0; i < tokens.size(); ++i)
        uniqueTips.insert(tokens[i].displayName);
    wxStringVec tips(uniqueTips.begin(), uniqueTips.end());

    const int style = event.GetInt();
    if (!tips.empty())
    {
        const int tknStart = stc->WordStartPosition(pos, true);
        const int tknEnd   = stc->WordEndPosition(pos,   true);
        if (tknEnd - tknStart > 2)
        {
            for (size_t i = 0; i < tips[0].Length(); ++i)
            {
                size_t hlLoc = tips[0].find(stc->GetTextRange(tknStart, tknEnd), i);
                if (hlLoc == wxString::npos)
                    break;
                hlStart = hlLoc;
                hlEnd = hlStart + tknEnd - tknStart;
                if (   (hlStart > 0 && (tips[0][hlStart - 1] == wxT('_') || wxIsalpha(tips[0][hlStart - 1])))
                    || (hlEnd < static_cast<int>(tips[0].Length()) - 1 && (tips[0][hlEnd] == wxT('_') || wxIsalpha(tips[0][hlEnd]))) )
                {
                    i = hlEnd;
                    hlStart = hlEnd = wxSCI_INVALID_POSITION;
                }
                else
                    break;
            }
        }
    }
    else if (  allowCallTip
             && !(   stc->IsString(style)
                  || stc->IsComment(style)
                  || stc->IsCharacter(style)
                  || stc->IsPreprocessor(style) ) )
    {
        const int line = stc->LineFromPosition(pos);
        if (pos + 4 > stc->PositionFromLine(line) + (int)ed->GetLineIndentString(line).Length())
        {
            const CallTipVec& cTips = ccPlugin->GetCallTips(pos, style, ed, argsPos);
            for (size_t i = 0; i < cTips.size(); ++i)
                tips.push_back(cTips[i].tip);
            if (!tips.empty())
            {
                hlStart = cTips[0].hlStart;
                hlEnd   = cTips[0].hlEnd;
            }
        }
    }
    if (tips.empty())
    {
        if (stc->CallTipActive() && event.GetExtraLong() == 0 && m_CallTipActive == wxSCI_INVALID_POSITION)
            static_cast<wxScintilla*>(stc)->CallTipCancel();
    }
    else
    {
        DoShowTips(tips, stc, pos, argsPos, hlStart, hlEnd);
        event.SetExtraLong(1);
    }
    m_CallTipActive = wxSCI_INVALID_POSITION;
}