HRESULT WINAPI fnTextSrv_TxSetText(ITextServices *iface, LPCWSTR pszText) { ICOM_THIS_MULTI(ITextServicesImpl, lpVtbl, iface); ME_Cursor cursor; ME_SetCursorToStart(This->editor, &cursor); ME_InternalDeleteText(This->editor, &cursor, ME_GetTextLength(This->editor), FALSE); ME_InsertTextFromCursor(This->editor, 0, pszText, -1, This->editor->pBuffer->pDefaultStyle); ME_SetSelection(This->editor, 0, 0); This->editor->nModifyStep = 0; OleFlushClipboard(); ME_EmptyUndoStack(This->editor); ME_UpdateRepaint(This->editor); return S_OK; }
DECLSPEC_HIDDEN HRESULT WINAPI fnTextSrv_TxGetText(ITextServices *iface, BSTR *pbstrText) { ITextServicesImpl *This = impl_from_ITextServices(iface); int length; length = ME_GetTextLength(This->editor); if (length) { ME_Cursor start; BSTR bstr; bstr = SysAllocStringByteLen(NULL, length * sizeof(WCHAR)); if (bstr == NULL) return E_OUTOFMEMORY; ME_CursorFromCharOfs(This->editor, 0, &start); ME_GetTextW(This->editor, bstr, length, &start, INT_MAX, FALSE, FALSE); *pbstrText = bstr; } else { *pbstrText = NULL; } return S_OK; }
int ME_SetSelection(ME_TextEditor *editor, int from, int to) { int selectionEnd = 0; const int len = ME_GetTextLength(editor); /* all negative values are effectively the same */ if (from < 0) from = -1; if (to < 0) to = -1; /* select all */ if (from == 0 && to == -1) { editor->pCursors[1].pRun = ME_FindItemFwd(editor->pBuffer->pFirst, diRun); editor->pCursors[1].nOffset = 0; editor->pCursors[0].pRun = ME_FindItemBack(editor->pBuffer->pLast, diRun); editor->pCursors[0].nOffset = 0; ME_InvalidateSelection(editor); ME_ClearTempStyle(editor); return len + 1; } /* if both values are equal and also out of bound, that means to */ /* put the selection at the end of the text */ if ((from == to) && (to < 0 || to > len)) { selectionEnd = 1; } else { /* if from is negative and to is positive then selection is */ /* deselected and caret moved to end of the current selection */ if (from < 0) { int start, end; ME_GetSelection(editor, &start, &end); editor->pCursors[1] = editor->pCursors[0]; ME_Repaint(editor); ME_ClearTempStyle(editor); return end; } /* adjust to if it's a negative value */ if (to < 0) to = len + 1; /* flip from and to if they are reversed */ if (from>to) { int tmp = from; from = to; to = tmp; } /* after fiddling with the values, we find from > len && to > len */ if (from > len) selectionEnd = 1; /* special case with to too big */ else if (to > len) to = len + 1; } if (selectionEnd) { editor->pCursors[1].pRun = editor->pCursors[0].pRun = ME_FindItemBack(editor->pBuffer->pLast, diRun); editor->pCursors[1].nOffset = editor->pCursors[0].nOffset = 0; ME_InvalidateSelection(editor); ME_ClearTempStyle(editor); return len; } ME_RunOfsFromCharOfs(editor, from, &editor->pCursors[1].pRun, &editor->pCursors[1].nOffset); ME_RunOfsFromCharOfs(editor, to, &editor->pCursors[0].pRun, &editor->pCursors[0].nOffset); return to; }
void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, const WCHAR *str, int len, ME_Style *style) { const WCHAR *pos; ME_Cursor *p = NULL; int oldLen; /* FIXME really HERE ? */ if (ME_IsSelection(editor)) ME_DeleteSelection(editor); /* FIXME: is this too slow? */ /* Didn't affect performance for WM_SETTEXT (around 50sec/30K) */ oldLen = ME_GetTextLength(editor); /* text operations set modified state */ editor->nModifyStep = 1; assert(style); assert(nCursor>=0 && nCursor<editor->nCursors); if (len == -1) len = lstrlenW(str); /* grow the text limit to fit our text */ if(editor->nTextLimit < oldLen +len) editor->nTextLimit = oldLen + len; while (len) { pos = str; /* FIXME this sucks - no respect for unicode (what else can be a line separator in unicode?) */ while(pos-str < len && *pos != '\r' && *pos != '\n' && *pos != '\t') pos++; if (pos-str < len && *pos == '\t') { /* handle tabs */ WCHAR tab = '\t'; if (pos!=str) ME_InternalInsertTextFromCursor(editor, nCursor, str, pos-str, style, 0); ME_InternalInsertTextFromCursor(editor, nCursor, &tab, 1, style, MERF_TAB); pos++; if(pos-str <= len) { len -= pos - str; str = pos; continue; } } /* handle special \r\r\n sequence (richedit 2.x and higher only) */ if (!editor->bEmulateVersion10 && pos-str < len-2 && pos[0] == '\r' && pos[1] == '\r' && pos[2] == '\n') { WCHAR space = ' '; if (pos!=str) ME_InternalInsertTextFromCursor(editor, nCursor, str, pos-str, style, 0); ME_InternalInsertTextFromCursor(editor, nCursor, &space, 1, style, 0); pos+=3; if(pos-str <= len) { len -= pos - str; str = pos; continue; } } if (pos-str < len) { /* handle EOLs */ ME_DisplayItem *tp, *end_run; ME_Style *tmp_style; int numCR, numLF; if (pos!=str) ME_InternalInsertTextFromCursor(editor, nCursor, str, pos-str, style, 0); p = &editor->pCursors[nCursor]; if (p->nOffset) { ME_SplitRunSimple(editor, p->pRun, p->nOffset); p = &editor->pCursors[nCursor]; } tmp_style = ME_GetInsertStyle(editor, nCursor); /* ME_SplitParagraph increases style refcount */ /* Encode and fill number of CR and LF according to emulation mode */ if (editor->bEmulateVersion10) { const WCHAR * tpos; /* We have to find out how many consecutive \r are there, and if there is a \n terminating the run of \r's. */ numCR = 0; numLF = 0; tpos = pos; while (tpos-str < len && *tpos == '\r') { tpos++; numCR++; } if (tpos-str >= len) { /* Reached end of text without finding anything but '\r' */ if (tpos != pos) { pos++; } numCR = 1; numLF = 0; } else if (*tpos == '\n') { /* The entire run of \r's plus the one \n is one single line break */ pos = tpos + 1; numLF = 1; } else { /* Found some other content past the run of \r's */ pos++; numCR = 1; numLF = 0; } } else { if(pos-str < len && *pos =='\r') pos++; if(pos-str < len && *pos =='\n') pos++; numCR = 1; numLF = 0; } tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style, numCR, numLF, 0); p->pRun = ME_FindItemFwd(tp, diRun); end_run = ME_FindItemBack(tp, diRun); ME_ReleaseStyle(end_run->member.run.style); end_run->member.run.style = tmp_style; p->nOffset = 0; if(pos-str <= len) { len -= pos - str; str = pos; continue; } } ME_InternalInsertTextFromCursor(editor, nCursor, str, len, style, 0); len = 0; } }
/* Move the cursor nRelOfs characters (either forwards or backwards) * * returns the actual number of characters moved. **/ int ME_MoveCursorChars(ME_TextEditor *editor, ME_Cursor *cursor, int nRelOfs) { cursor->nOffset += nRelOfs; if (cursor->nOffset < 0) { cursor->nOffset += cursor->pRun->member.run.nCharOfs; if (cursor->nOffset >= 0) { /* new offset in the same paragraph */ do { cursor->pRun = ME_FindItemBack(cursor->pRun, diRun); } while (cursor->nOffset < cursor->pRun->member.run.nCharOfs); cursor->nOffset -= cursor->pRun->member.run.nCharOfs; return nRelOfs; } cursor->nOffset += cursor->pPara->member.para.nCharOfs; if (cursor->nOffset <= 0) { /* moved to the start of the text */ nRelOfs -= cursor->nOffset; ME_SetCursorToStart(editor, cursor); return nRelOfs; } /* new offset in a previous paragraph */ do { cursor->pPara = cursor->pPara->member.para.prev_para; } while (cursor->nOffset < cursor->pPara->member.para.nCharOfs); cursor->nOffset -= cursor->pPara->member.para.nCharOfs; cursor->pRun = ME_FindItemBack(cursor->pPara->member.para.next_para, diRun); while (cursor->nOffset < cursor->pRun->member.run.nCharOfs) { cursor->pRun = ME_FindItemBack(cursor->pRun, diRun); } cursor->nOffset -= cursor->pRun->member.run.nCharOfs; } else if (cursor->nOffset >= cursor->pRun->member.run.len) { ME_DisplayItem *next_para; int new_offset; new_offset = ME_GetCursorOfs(cursor); next_para = cursor->pPara->member.para.next_para; if (new_offset < next_para->member.para.nCharOfs) { /* new offset in the same paragraph */ do { cursor->nOffset -= cursor->pRun->member.run.len; cursor->pRun = ME_FindItemFwd(cursor->pRun, diRun); } while (cursor->nOffset >= cursor->pRun->member.run.len); return nRelOfs; } if (new_offset >= ME_GetTextLength(editor)) { /* new offset at the end of the text */ ME_SetCursorToEnd(editor, cursor); nRelOfs -= new_offset - ME_GetTextLength(editor); return nRelOfs; } /* new offset in a following paragraph */ do { cursor->pPara = next_para; next_para = next_para->member.para.next_para; } while (new_offset >= next_para->member.para.nCharOfs); cursor->nOffset = new_offset - cursor->pPara->member.para.nCharOfs; cursor->pRun = ME_FindItemFwd(cursor->pPara, diRun); while (cursor->nOffset >= cursor->pRun->member.run.len) { cursor->nOffset -= cursor->pRun->member.run.len; cursor->pRun = ME_FindItemFwd(cursor->pRun, diRun); } } /* else new offset is in the same run */ return nRelOfs; }
void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, const WCHAR *str, int len, ME_Style *style) { const WCHAR *pos; ME_Cursor *p = NULL; int oldLen; /* FIXME really HERE ? */ if (ME_IsSelection(editor)) ME_DeleteSelection(editor); /* FIXME: is this too slow? */ /* Didn't affect performance for WM_SETTEXT (around 50sec/30K) */ oldLen = ME_GetTextLength(editor); /* text operations set modified state */ editor->nModifyStep = 1; assert(style); assert(nCursor>=0 && nCursor<editor->nCursors); if (len == -1) len = lstrlenW(str); /* grow the text limit to fit our text */ if(editor->nTextLimit < oldLen +len) editor->nTextLimit = oldLen + len; pos = str; while (len) { /* FIXME this sucks - no respect for unicode (what else can be a line separator in unicode?) */ while(pos - str < len && *pos != '\r' && *pos != '\n' && *pos != '\t') pos++; if (pos != str) { /* handle text */ ME_InternalInsertTextFromCursor(editor, nCursor, str, pos-str, style, 0); } else if (*pos == '\t') { /* handle tabs */ WCHAR tab = '\t'; ME_InternalInsertTextFromCursor(editor, nCursor, &tab, 1, style, MERF_TAB); pos++; } else { /* handle EOLs */ ME_DisplayItem *tp, *end_run, *run, *prev; ME_Style *tmp_style; int eol_len = 0; /* Find number of CR and LF in end of paragraph run */ if (*pos =='\r') { if (len > 1 && pos[1] == '\n') eol_len = 2; else if (len > 2 && pos[1] == '\r' && pos[2] == '\n') eol_len = 3; else eol_len = 1; } else { assert(*pos == '\n'); eol_len = 1; } pos += eol_len; if (!editor->bEmulateVersion10 && eol_len == 3) { /* handle special \r\r\n sequence (richedit 2.x and higher only) */ WCHAR space = ' '; ME_InternalInsertTextFromCursor(editor, nCursor, &space, 1, style, 0); } else { const WCHAR cr = '\r', *eol_str = str; if (!editor->bEmulateVersion10) { eol_str = &cr; eol_len = 1; } p = &editor->pCursors[nCursor]; if (p->nOffset == p->pRun->member.run.len) { run = ME_FindItemFwd( p->pRun, diRun ); if (!run) run = p->pRun; } else { if (p->nOffset) ME_SplitRunSimple(editor, p); run = p->pRun; } tmp_style = ME_GetInsertStyle(editor, nCursor); /* ME_SplitParagraph increases style refcount */ tp = ME_SplitParagraph(editor, run, run->member.run.style, eol_str, eol_len, 0); end_run = ME_FindItemBack(tp, diRun); ME_ReleaseStyle(end_run->member.run.style); end_run->member.run.style = tmp_style; /* Move any cursors that were at the end of the previous run to the beginning of the new para */ prev = ME_FindItemBack( end_run, diRun ); if (prev) { int i; for (i = 0; i < editor->nCursors; i++) { if (editor->pCursors[i].pRun == prev && editor->pCursors[i].nOffset == prev->member.run.len) { editor->pCursors[i].pPara = tp; editor->pCursors[i].pRun = run; editor->pCursors[i].nOffset = 0; } } } } } len -= pos - str; str = pos; } }
BOOL ME_InternalDeleteText(ME_TextEditor *editor, ME_Cursor *start, int nChars, BOOL bForce) { ME_Cursor c = *start; int nOfs = ME_GetCursorOfs(start), text_len = ME_GetTextLength( editor ); int shift = 0; int totalChars = nChars; ME_DisplayItem *start_para; BOOL delete_all = FALSE; /* Prevent deletion past last end of paragraph run. */ nChars = min(nChars, text_len - nOfs); if (nChars == text_len) delete_all = TRUE; start_para = c.pPara; if (!bForce) { ME_ProtectPartialTableDeletion(editor, &c, &nChars); if (nChars == 0) return FALSE; } while(nChars > 0) { ME_Run *run; ME_CursorFromCharOfs(editor, nOfs+nChars, &c); if (!c.nOffset && nOfs+nChars == (c.pRun->member.run.nCharOfs + c.pPara->member.para.nCharOfs)) { /* We aren't deleting anything in this run, so we will go back to the * last run we are deleting text in. */ ME_PrevRun(&c.pPara, &c.pRun); c.nOffset = c.pRun->member.run.len; } run = &c.pRun->member.run; if (run->nFlags & MERF_ENDPARA) { int eollen = c.pRun->member.run.len; BOOL keepFirstParaFormat; if (!ME_FindItemFwd(c.pRun, diParagraph)) { return TRUE; } keepFirstParaFormat = (totalChars == nChars && nChars <= eollen && run->nCharOfs); if (!editor->bEmulateVersion10) /* v4.1 */ { ME_DisplayItem *next_para = ME_FindItemFwd(c.pRun, diParagraphOrEnd); ME_DisplayItem *this_para = next_para->member.para.prev_para; /* The end of paragraph before a table row is only deleted if there * is nothing else on the line before it. */ if (this_para == start_para && next_para->member.para.nFlags & MEPF_ROWSTART) { /* If the paragraph will be empty, then it should be deleted, however * it still might have text right now which would inherit the * MEPF_STARTROW property if we joined it right now. * Instead we will delete it after the preceding text is deleted. */ if (nOfs > this_para->member.para.nCharOfs) { /* Skip this end of line. */ nChars -= (eollen < nChars) ? eollen : nChars; continue; } keepFirstParaFormat = TRUE; } } ME_JoinParagraphs(editor, c.pPara, keepFirstParaFormat); /* ME_SkipAndPropagateCharOffset(p->pRun, shift); */ ME_CheckCharOffsets(editor); nChars -= (eollen < nChars) ? eollen : nChars; continue; } else { ME_Cursor cursor; int nCharsToDelete = min(nChars, c.nOffset); int i; c.nOffset -= nCharsToDelete; ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags |= MEPF_REWRAP; cursor = c; /* nChars is the number of characters that should be deleted from the PRECEDING runs (these BEFORE cursor.pRun) nCharsToDelete is a number of chars to delete from THIS run */ nChars -= nCharsToDelete; shift -= nCharsToDelete; TRACE("Deleting %d (remaning %d) chars at %d in %s (%d)\n", nCharsToDelete, nChars, c.nOffset, debugstr_run( run ), run->len); /* nOfs is a character offset (from the start of the document to the current (deleted) run */ add_undo_insert_run( editor, nOfs + nChars, get_text( run, c.nOffset ), nCharsToDelete, run->nFlags, run->style ); ME_StrDeleteV(run->para->text, run->nCharOfs + c.nOffset, nCharsToDelete); run->len -= nCharsToDelete; TRACE("Post deletion string: %s (%d)\n", debugstr_run( run ), run->len); TRACE("Shift value: %d\n", shift); /* update cursors (including c) */ for (i=-1; i<editor->nCursors; i++) { ME_Cursor *pThisCur = editor->pCursors + i; if (i == -1) pThisCur = &c; if (pThisCur->pRun == cursor.pRun) { if (pThisCur->nOffset > cursor.nOffset) { if (pThisCur->nOffset-cursor.nOffset < nCharsToDelete) pThisCur->nOffset = cursor.nOffset; else pThisCur->nOffset -= nCharsToDelete; assert(pThisCur->nOffset >= 0); assert(pThisCur->nOffset <= run->len); } if (pThisCur->nOffset == run->len) { pThisCur->pRun = ME_FindItemFwd(pThisCur->pRun, diRunOrParagraphOrEnd); assert(pThisCur->pRun->type == diRun); pThisCur->nOffset = 0; } } } /* c = updated data now */ if (c.pRun == cursor.pRun) ME_SkipAndPropagateCharOffset(c.pRun, shift); else ME_PropagateCharOffset(c.pRun, shift); if (!cursor.pRun->member.run.len) { TRACE("Removing empty run\n"); ME_Remove(cursor.pRun); ME_DestroyDisplayItem(cursor.pRun); } shift = 0; /* ME_CheckCharOffsets(editor); */ continue; } } if (delete_all) ME_SetDefaultParaFormat( editor, start_para->member.para.pFmt ); return TRUE; }
int ME_SetSelection(ME_TextEditor *editor, int from, int to) { int selectionEnd = 0; const int len = ME_GetTextLength(editor); /* all negative values are effectively the same */ if (from < 0) from = -1; if (to < 0) to = -1; /* select all */ if (from == 0 && to == -1) { ME_SetCursorToStart(editor, &editor->pCursors[1]); ME_SetCursorToEnd(editor, &editor->pCursors[0]); editor->pCursors[0].nOffset = editor->pCursors[0].pRun->member.run.len; ME_InvalidateSelection(editor); return len + 1; } /* if both values are equal and also out of bound, that means to */ /* put the selection at the end of the text */ if ((from == to) && (to < 0 || to > len)) { selectionEnd = 1; } else { /* if from is negative and to is positive then selection is */ /* deselected and caret moved to end of the current selection */ if (from < 0) { int start, end; ME_GetSelectionOfs(editor, &start, &end); if (start != end) { if (end > len) { editor->pCursors[0].nOffset = 0; end --; } editor->pCursors[1] = editor->pCursors[0]; ME_Repaint(editor); } return end; } /* adjust to if it's a negative value */ if (to < 0) to = len + 1; /* flip from and to if they are reversed */ if (from>to) { int tmp = from; from = to; to = tmp; } /* after fiddling with the values, we find from > len && to > len */ if (from > len) selectionEnd = 1; /* special case with to too big */ else if (to > len) to = len + 1; } if (selectionEnd) { ME_SetCursorToEnd(editor, &editor->pCursors[0]); editor->pCursors[1] = editor->pCursors[0]; ME_InvalidateSelection(editor); return len; } ME_CursorFromCharOfs(editor, from, &editor->pCursors[1]); editor->pCursors[0] = editor->pCursors[1]; ME_MoveCursorChars(editor, &editor->pCursors[0], to - from); /* Selection is not allowed in the middle of an end paragraph run. */ if (editor->pCursors[1].pRun->member.run.nFlags & MERF_ENDPARA) editor->pCursors[1].nOffset = 0; if (editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA) { if (to > len) editor->pCursors[0].nOffset = editor->pCursors[0].pRun->member.run.len; else editor->pCursors[0].nOffset = 0; } return to; }
void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, const WCHAR *str, int len, ME_Style *style) { const WCHAR *pos; ME_Cursor *p = NULL; /* FIXME: is this too slow? */ /* Didn't affect performance for WM_SETTEXT (around 50sec/30K) */ int freeSpace = editor->nTextLimit - ME_GetTextLength(editor); /* text operations set modified state */ editor->nModifyStep = 1; assert(style); /* FIXME really HERE ? */ if (ME_IsSelection(editor)) ME_DeleteSelection(editor); assert(nCursor>=0 && nCursor<editor->nCursors); if (len == -1) len = lstrlenW(str); len = min(len, freeSpace); while (len) { pos = str; /* FIXME this sucks - no respect for unicode (what else can be a line separator in unicode?) */ while(pos-str < len && *pos != '\r' && *pos != '\n' && *pos != '\t') pos++; if (pos-str < len && *pos == '\t') { /* handle tabs */ WCHAR tab = '\t'; if (pos!=str) ME_InternalInsertTextFromCursor(editor, nCursor, str, pos-str, style, 0); ME_InternalInsertTextFromCursor(editor, nCursor, &tab, 1, style, MERF_TAB); pos++; if(pos-str <= len) { len -= pos - str; str = pos; continue; } } if (pos-str < len) { /* handle EOLs */ ME_DisplayItem *tp, *end_run; ME_Style *tmp_style; if (pos!=str) ME_InternalInsertTextFromCursor(editor, nCursor, str, pos-str, style, 0); p = &editor->pCursors[nCursor]; if (p->nOffset) { ME_SplitRunSimple(editor, p->pRun, p->nOffset); p = &editor->pCursors[nCursor]; } tmp_style = ME_GetInsertStyle(editor, nCursor); /* ME_SplitParagraph increases style refcount */ tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style); p->pRun = ME_FindItemFwd(tp, diRun); end_run = ME_FindItemBack(tp, diRun); ME_ReleaseStyle(end_run->member.run.style); end_run->member.run.style = tmp_style; p->nOffset = 0; if(pos-str < len && *pos =='\r') pos++; if(pos-str < len && *pos =='\n') pos++; if(pos-str <= len) { len -= pos - str; str = pos; continue; } } ME_InternalInsertTextFromCursor(editor, nCursor, str, len, style, 0); len = 0; } }