//需要用枚举的办法来获得指定OLE的显示位置,不知道有没有其它更好的办法。 BOOL CImageOle::GetOleRect( LPRECT lpRect ) { IRichEditOle *pRichEditOle=NULL; LRESULT lRes=m_pRichedit->DuiSendMessage(EM_GETOLEINTERFACE,0,(LPARAM)&pRichEditOle); if(!pRichEditOle) return FALSE; BOOL bRet=FALSE; int nObjCount = pRichEditOle->GetObjectCount(); int i = 0; for (i = 0;i < nObjCount;i++) { REOBJECT reo; ZeroMemory(&reo, sizeof(REOBJECT)); reo.cbStruct = sizeof(REOBJECT); HRESULT hr = pRichEditOle->GetObject(i, &reo, REO_GETOBJ_POLEOBJ); if (hr != S_OK) continue; reo.poleobj->Release(); if (reo.poleobj == (IOleObject *)this) { ITextDocument *pTextDocument = NULL; ITextRange *pTextRange = NULL; pRichEditOle->QueryInterface(IID_ITextDocument, (void **)&pTextDocument); if (!pTextDocument) break; long nLeft = 0; long nBottom = 0; pTextDocument->Range(reo.cp, reo.cp, &pTextRange); if (reo.dwFlags & REO_BELOWBASELINE) hr = pTextRange->GetPoint(TA_BOTTOM|TA_LEFT, &nLeft, &nBottom); else hr = pTextRange->GetPoint(TA_BASELINE|TA_LEFT, &nLeft, &nBottom); pTextDocument->Release(); pTextRange->Release(); CRect rcRichedit; GetWindowRect(m_pRichedit->GetContainer()->GetHostHwnd(),&rcRichedit); CSize szOle=m_pSkin->GetSkinSize(); lpRect->left = nLeft - rcRichedit.left; lpRect->bottom = nBottom - rcRichedit.top; lpRect->right = lpRect->left + szOle.cx ; lpRect->top = lpRect->bottom - szOle.cy; bRet=TRUE; break; } } pRichEditOle->Release(); return bRet; }
/**************************************************************************** * CRecoEventMgr::SelNotify * *--------------------------* * Description: * Called whenever the selection changes. * Drops a new listening point onto the list of listening points for * this phrase if there is a phrase currently being processed * being listened to. * Hands back the range to be eventually replaced by recognized * text (or deleted). * Return: * S_OK * S_FALSE if nothing had to be done * E_OUTOFMEMORY * Return value of ITextRange::GetDuplicate() * Return value of ITextRange::Collapse() *****************************************************************************/ HRESULT CRecoEventMgr::SelNotify( ITextRange &rSelRange ) { // Only want to queue this listen if we are listening to a phrase HRESULT hr = S_FALSE; if ( m_fPhraseStarted ) { // Get the time now FILETIME ftNow; ::CoFileTimeNow( &ftNow ); // Does this range overlap any of the existing ranges in the list? LISTENPOINT *p; for ( p = m_pHeadLP; p && AreDisjointRanges( &rSelRange, p->cpRangeToReplace ); p = p->pNext ) ; // If p is not NULL, that means that there is already some listen point // that is adjacent to or overlaps this range, so another listen point // is not necessary if ( !p ) { // Add a new listen point LISTENPOINT *pNewPoint = new LISTENPOINT; if ( !pNewPoint ) { return E_OUTOFMEMORY; } // Get the ranges rSelRange.GetDuplicate( &(pNewPoint->cpRangeToReplace) ); // Get the timestamp pNewPoint->ftTime = ftNow; // This flag will be set by PhraseStart() if appropriate pNewPoint->fFromPhraseStart = false; // No hypothesis text here yet pNewPoint->fHasHypothesisText = false; // Put this new listenpoint onto the head of the list pNewPoint->pNext = m_pHeadLP; m_pHeadLP = pNewPoint; long lEndOfRangeToReplace; pNewPoint->cpRangeToReplace->GetEnd( &lEndOfRangeToReplace ); // The selection should be forced to the end of the range to replace; // this keeps the selection out of ranges that might be replaced // by recognized text m_pTextSel->SetRange( lEndOfRangeToReplace, lEndOfRangeToReplace ); // If we got here, we were successful hr = S_OK; } } return hr; } /* CRecoEventMgr::SelNotify */
TCHAR *RichEdit::GetText(int start, int end) const { if (end <= start) end = GetTextLength(); if (textDocument != NULL) { ITextRange *range; if (textDocument->Range(start, end, &range) != S_OK) return mir_tstrdup(_T("")); BSTR text = NULL; if (range->GetText(&text) != S_OK || text == NULL) { range->Release(); return mir_tstrdup(_T("")); } TCHAR *ret = mir_u2t(text); SysFreeString(text); range->Release(); return ret; } else { int len = GetTextLength(); TCHAR *tmp = (TCHAR *) mir_alloc(len * sizeof(TCHAR)); GetWindowText(hwnd, tmp, len); tmp[len] = 0; TCHAR *ret = (TCHAR *) mir_alloc((end - start + 1) * sizeof(TCHAR)); memmove(ret, &tmp[start], (end - start) * sizeof(TCHAR)); ret[end - start] = 0; mir_free(tmp); return ret; } }
static void SetPosition(HWND hwnd) { IRichEditOle* RichEditOle; if (SendMessage(hwnd, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle) == 0) return; ITextDocument* TextDocument; if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK) { RichEditOle->Release(); return; } // retrieve text range ITextRange* TextRange; if (TextDocument->Range(0, 0, &TextRange) != S_OK) { TextDocument->Release(); RichEditOle->Release(); return; } TextDocument->Release(); int objectCount = RichEditOle->GetObjectCount(); for (int i = objectCount - 1; i >= 0; i--) { REOBJECT reObj = {0}; reObj.cbStruct = sizeof(REOBJECT); HRESULT hr = RichEditOle->GetObject(i, &reObj, REO_GETOBJ_POLEOBJ); if (FAILED(hr)) continue; ISmileyBase *igsc = NULL; if (reObj.clsid == CLSID_NULL) reObj.poleobj->QueryInterface(IID_ISmileyAddSmiley, (void**) &igsc); reObj.poleobj->Release(); if (igsc == NULL) continue; TextRange->SetRange(reObj.cp, reObj.cp); BOOL res; POINT pt; RECT rect; hr = TextRange->GetPoint(tomStart | TA_BOTTOM | TA_LEFT, &pt.x, &pt.y); if (hr == S_OK) { res = ScreenToClient(hwnd, &pt); rect.bottom = pt.y; rect.left = pt.x; } else rect.bottom = -1; hr = TextRange->GetPoint(tomStart | TA_TOP | TA_LEFT, &pt.x, &pt.y); if (hr == S_OK) { res = ScreenToClient(hwnd, &pt); rect.top = pt.y; rect.left = pt.x; } else rect.top = -1; igsc->SetPosition(hwnd, &rect); igsc->Release(); } TextRange->Release(); RichEditOle->Release(); }
static int getIndex(ITextRange range, int charIndex, Array<int> *glyphLengths = NULL) { // Count glyphs until pos. There is a bug in glyphRun.GetCharacterSize() // and glyphRun.GetContents(), so we cannot count on these. // They sometimes contain chars that are in the next run or contain chars // from the previous ones.... // So let's do it the hard way and count only on GetSingleGlyphInRange // TODO: cash these results in an int table! // IDEA: cash it in the Story of the range, as a lookup table // char-index -> glyph-index int glyphPos = 0; int start = range.GetStart(); int end = range.GetEnd(); int size = range.GetSize(); int scanPos = start; while (scanPos < charIndex) { // There is a way to discover ligatures: the TextRange's GetSingleGlyphInRange // only returns if the length is set to the amount of chars that produce a ligature // otherwise it fails. So we can test.... // TODO: determine maximum ligature size. // Assumption is 16 for now. // In most cases, 1 will return a result, so there won't be too much iteration here... ATEGlyphID id; int length = 1; int max = MIN(16, size - scanPos); for (; length <= max; length++) { // First set the text range of the glpyhrun to test GetSingleGlyphInRange on range.SetRange(scanPos, scanPos + length); // ASCharType type = range.GetCharacterType(); if (range.GetSingleGlyphInRange(&id)) // Found a full glyph? break; } // If the length goes all the way to the end, we are likely to have encountered a hyphen glyph, as forced by the // AI layout engine's auto hyphenation. // There seems to be no way to detect this otherwise, and GetSingleGlyphInRange does not return an id // for the situation where the range only describes the one letter before the hyphen. So let's assume // this situation is only encountered for hyphens, and adjust glyphLengths, scanPos and glyphPos accordingly // further bellow. if (length < max || max == 1) { scanPos += length; // Glyph runs do not count paragraph end chars, so don't count them here either. if (range.GetCharacterType() != kParagraphEndChar) { glyphPos++; if (glyphLengths != NULL) glyphLengths->add(length); } else if (glyphLengths != NULL) { int last = glyphLengths->size() - 1; if (last >= 0) { int value = glyphLengths->get(last); // Add length to last one. If it's negative, subtract it, since the range starts with paragraph // end chars (see bellow) if (value < 0) value -= length; else value += length; glyphLengths->set(last, value); } else { // Add a negative value, to indicate that range starts with paragraph end chars glyphLengths->add(-length); } } } else { // Increase glyph pos both for the actual char and the hyphen. This is guessing. There might be situations where // this is wrong, e.g. ligatures before hypenation, etc. TODO: Test! glyphPos += 2; scanPos++; if (glyphLengths != NULL) { // Normal glyph (what if it's a ligature?) glyphLengths->add(1); // The hyphen, to be ignored as a glyph glyphLengths->add(0); } } } if (scanPos > charIndex) glyphPos--; range.SetRange(start, end); return glyphPos; }