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; }