int ME_GetSelection(ME_TextEditor *editor, ME_Cursor **from, ME_Cursor **to) { int from_ofs = ME_GetCursorOfs( &editor->pCursors[0] ); int to_ofs = ME_GetCursorOfs( &editor->pCursors[1] ); BOOL swap = (from_ofs > to_ofs); if (from_ofs == to_ofs) { /* If cursor[0] is at the beginning of a run and cursor[1] at the end of the prev run then we need to swap. */ if (editor->pCursors[0].nOffset < editor->pCursors[1].nOffset) swap = TRUE; } if (!swap) { *from = &editor->pCursors[0]; *to = &editor->pCursors[1]; return 0; } else { *from = &editor->pCursors[1]; *to = &editor->pCursors[0]; return 1; } }
void ME_TabPressedInTable(ME_TextEditor *editor, BOOL bSelectedRow) { /* FIXME: Shift tab should move to the previous cell. */ ME_Cursor fromCursor, toCursor; ME_InvalidateSelection(editor); { int from, to; from = ME_GetCursorOfs(editor, 0); to = ME_GetCursorOfs(editor, 1); if (from <= to) { fromCursor = editor->pCursors[0]; toCursor = editor->pCursors[1]; } else { fromCursor = editor->pCursors[1]; toCursor = editor->pCursors[0]; } } if (!editor->bEmulateVersion10) /* v4.1 */ { if (!ME_IsInTable(toCursor.pRun)) { editor->pCursors[0] = toCursor; editor->pCursors[1] = toCursor; } else { ME_SelectOrInsertNextCell(editor, toCursor.pRun); } } else { /* v1.0 - 3.0 */ if (!ME_IsInTable(fromCursor.pRun)) { editor->pCursors[0] = fromCursor; editor->pCursors[1] = fromCursor; /* FIXME: For some reason the caret is shown at the start of the * previous paragraph in v1.0 to v3.0, and bCaretAtEnd only works * within the paragraph for wrapped lines. */ if (ME_FindItemBack(fromCursor.pRun, diRun)) editor->bCaretAtEnd = TRUE; } else if ((bSelectedRow || !ME_IsInTable(toCursor.pRun))) { ME_SelectOrInsertNextCell(editor, fromCursor.pRun); } else { if (ME_IsSelection(editor) && !toCursor.nOffset) { ME_DisplayItem *run; run = ME_FindItemBack(toCursor.pRun, diRunOrParagraphOrEnd); if (run->type == diRun && run->member.run.nFlags & MERF_TAB) ME_SelectOrInsertNextCell(editor, run); else ME_SelectOrInsertNextCell(editor, toCursor.pRun); } else { ME_SelectOrInsertNextCell(editor, toCursor.pRun); } } } ME_InvalidateSelection(editor); ME_Repaint(editor); HideCaret(editor->hWnd); ME_ShowCaret(editor); ME_SendSelChange(editor); }
static int ME_GetSelCursor(ME_TextEditor *editor, int dir) { int cdir = ME_GetCursorOfs(editor, 0) - ME_GetCursorOfs(editor, 1); if (cdir*dir>0) return 0; else return 1; }
void ME_GetSelection(ME_TextEditor *editor, int *from, int *to) { *from = ME_GetCursorOfs(editor, 0); *to = ME_GetCursorOfs(editor, 1); if (*from > *to) { int tmp = *from; *from = *to; *to = tmp; } }
int ME_GetSelection(ME_TextEditor *editor, ME_Cursor **from, ME_Cursor **to) { if (ME_GetCursorOfs(&editor->pCursors[0]) < ME_GetCursorOfs(&editor->pCursors[1])) { *from = &editor->pCursors[0]; *to = &editor->pCursors[1]; return 0; } else { *from = &editor->pCursors[1]; *to = &editor->pCursors[0]; return 1; } }
/* Extends the selection with a word, line, or paragraph selection type. * * The selection is anchored by editor->pCursors[2-3] such that the text * between the anchors will remain selected, and one end will be extended. * * editor->pCursors[0] should have the position to extend the selection to * before this function is called. * * Nothing will be done if editor->nSelectionType equals stPosition. */ static void ME_ExtendAnchorSelection(ME_TextEditor *editor) { ME_Cursor tmp_cursor; int curOfs, anchorStartOfs, anchorEndOfs; if (editor->nSelectionType == stPosition || editor->nSelectionType == stDocument) return; curOfs = ME_GetCursorOfs(&editor->pCursors[0]); anchorStartOfs = ME_GetCursorOfs(&editor->pCursors[3]); anchorEndOfs = ME_GetCursorOfs(&editor->pCursors[2]); tmp_cursor = editor->pCursors[0]; editor->pCursors[0] = editor->pCursors[2]; editor->pCursors[1] = editor->pCursors[3]; if (curOfs < anchorStartOfs) { /* Extend the left side of selection */ editor->pCursors[1] = tmp_cursor; if (editor->nSelectionType == stWord) ME_MoveCursorWords(editor, &editor->pCursors[1], -1); else { ME_DisplayItem *pItem; ME_DIType searchType = ((editor->nSelectionType == stLine) ? diStartRowOrParagraph:diParagraph); pItem = ME_FindItemBack(editor->pCursors[1].pRun, searchType); editor->pCursors[1].pRun = ME_FindItemFwd(pItem, diRun); editor->pCursors[1].pPara = ME_GetParagraph(editor->pCursors[1].pRun); editor->pCursors[1].nOffset = 0; } } else if (curOfs >= anchorEndOfs) { /* Extend the right side of selection */ editor->pCursors[0] = tmp_cursor; if (editor->nSelectionType == stWord) ME_MoveCursorWords(editor, &editor->pCursors[0], +1); else { ME_DisplayItem *pItem; ME_DIType searchType = ((editor->nSelectionType == stLine) ? diStartRowOrParagraphOrEnd:diParagraphOrEnd); pItem = ME_FindItemFwd(editor->pCursors[0].pRun, searchType); if (pItem->type == diTextEnd) editor->pCursors[0].pRun = ME_FindItemBack(pItem, diRun); else editor->pCursors[0].pRun = ME_FindItemFwd(pItem, diRun); editor->pCursors[0].pPara = ME_GetParagraph(editor->pCursors[0].pRun); editor->pCursors[0].nOffset = 0; } } }
int ME_GetSelectionOfs(ME_TextEditor *editor, int *from, int *to) { *from = ME_GetCursorOfs(&editor->pCursors[0]); *to = ME_GetCursorOfs(&editor->pCursors[1]); if (*from > *to) { int tmp = *from; *from = *to; *to = tmp; return 1; } return 0; }
HRESULT ME_GetDataObject(ME_TextEditor *editor, const ME_Cursor *start, int nChars, LPDATAOBJECT *lplpdataobj) { DataObjectImpl *obj; TRACE("(%p,%d,%d)\n", editor, ME_GetCursorOfs(start), nChars); obj = heap_alloc(sizeof(DataObjectImpl)); if(cfRTF == 0) cfRTF = RegisterClipboardFormatA("Rich Text Format"); obj->IDataObject_iface.lpVtbl = &VT_DataObjectImpl; obj->ref = 1; obj->unicode = get_unicode_text(editor, start, nChars); obj->rtf = NULL; obj->fmtetc_cnt = 1; if(editor->mode & TM_RICHTEXT) obj->fmtetc_cnt++; obj->fmtetc = GlobalAlloc(GMEM_ZEROINIT, obj->fmtetc_cnt*sizeof(FORMATETC)); InitFormatEtc(obj->fmtetc[0], CF_UNICODETEXT, TYMED_HGLOBAL); if(editor->mode & TM_RICHTEXT) { obj->rtf = get_rtf_text(editor, start, nChars); InitFormatEtc(obj->fmtetc[1], cfRTF, TYMED_HGLOBAL); } *lplpdataobj = (LPDATAOBJECT)obj; return S_OK; }
BOOL ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, int nChars) { assert(nCursor>=0 && nCursor<editor->nCursors); /* text operations set modified state */ editor->nModifyStep = 1; return ME_InternalDeleteText(editor, ME_GetCursorOfs(editor, nCursor), nChars, FALSE); }
static HGLOBAL get_unicode_text(ME_TextEditor *editor, const ME_Cursor *start, int nChars) { int pars = 0; WCHAR *data; HANDLE ret; ME_DisplayItem *para; int nEnd = ME_GetCursorOfs(start) + nChars; /* count paragraphs in range */ para = start->pPara; while((para = para->member.para.next_para) && para->member.para.nCharOfs <= nEnd) pars++; ret = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR) * (nChars + pars + 1)); data = GlobalLock(ret); ME_GetTextW(editor, data, nChars + pars, start, nChars, TRUE, FALSE); GlobalUnlock(ret); return ret; }
int ME_GetTextLength(ME_TextEditor *editor) { ME_Cursor cursor; ME_SetCursorToEnd(editor, &cursor); return ME_GetCursorOfs(&cursor); }
/* 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; }
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; }
void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, int nChars) { assert(nCursor>=0 && nCursor<editor->nCursors); ME_InternalDeleteText(editor, ME_GetCursorOfs(editor, nCursor), nChars); }
/* Table rows should either be deleted completely or not at all. */ void ME_ProtectPartialTableDeletion(ME_TextEditor *editor, ME_Cursor *c, int *nChars) { int nOfs = ME_GetCursorOfs(c); ME_Cursor c2 = *c; ME_DisplayItem *this_para = c->pPara; ME_DisplayItem *end_para; ME_MoveCursorChars(editor, &c2, *nChars); end_para = c2.pPara; if (c2.pRun->member.run.nFlags & MERF_ENDPARA) { /* End offset might be in the middle of the end paragraph run. * If this is the case, then we need to use the next paragraph as the last * paragraphs. */ int remaining = nOfs + *nChars - c2.pRun->member.run.nCharOfs - end_para->member.para.nCharOfs; if (remaining) { assert(remaining < c2.pRun->member.run.len); end_para = end_para->member.para.next_para; } } if (!editor->bEmulateVersion10) { /* v4.1 */ if (this_para->member.para.pCell != end_para->member.para.pCell || ((this_para->member.para.nFlags|end_para->member.para.nFlags) & (MEPF_ROWSTART|MEPF_ROWEND))) { while (this_para != end_para) { ME_DisplayItem *next_para = this_para->member.para.next_para; BOOL bTruancateDeletion = FALSE; if (this_para->member.para.nFlags & MEPF_ROWSTART) { /* The following while loop assumes that next_para is MEPF_ROWSTART, * so moving back one paragraph let's it be processed as the start * of the row. */ next_para = this_para; this_para = this_para->member.para.prev_para; } else if (next_para->member.para.pCell != this_para->member.para.pCell || this_para->member.para.nFlags & MEPF_ROWEND) { /* Start of the deletion from after the start of the table row. */ bTruancateDeletion = TRUE; } while (!bTruancateDeletion && next_para->member.para.nFlags & MEPF_ROWSTART) { next_para = ME_GetTableRowEnd(next_para)->member.para.next_para; if (next_para->member.para.nCharOfs > nOfs + *nChars) { /* End of deletion is not past the end of the table row. */ next_para = this_para->member.para.next_para; /* Delete the end paragraph preceding the table row if the * preceding table row will be empty. */ if (this_para->member.para.nCharOfs >= nOfs) { next_para = next_para->member.para.next_para; } bTruancateDeletion = TRUE; } else { this_para = next_para->member.para.prev_para; } } if (bTruancateDeletion) { ME_Run *end_run = &ME_FindItemBack(next_para, diRun)->member.run; int nCharsNew = (next_para->member.para.nCharOfs - nOfs - end_run->len); nCharsNew = max(nCharsNew, 0); assert(nCharsNew <= *nChars); *nChars = nCharsNew; break; } this_para = next_para; } } } else { /* v1.0 - 3.0 */ ME_DisplayItem *pRun; int nCharsToBoundary; if ((this_para->member.para.nCharOfs != nOfs || this_para == end_para) && this_para->member.para.pFmt->dwMask & PFM_TABLE && this_para->member.para.pFmt->wEffects & PFE_TABLE) { pRun = c->pRun; /* Find the next tab or end paragraph to use as a delete boundary */ while (!(pRun->member.run.nFlags & (MERF_TAB|MERF_ENDPARA))) pRun = ME_FindItemFwd(pRun, diRun); nCharsToBoundary = pRun->member.run.nCharOfs - c->pRun->member.run.nCharOfs - c->nOffset; *nChars = min(*nChars, nCharsToBoundary); } else if (end_para->member.para.pFmt->dwMask & PFM_TABLE && end_para->member.para.pFmt->wEffects & PFE_TABLE) { /* The deletion starts from before the row, so don't join it with * previous non-empty paragraphs. */ ME_DisplayItem *curPara; pRun = NULL; if (nOfs > this_para->member.para.nCharOfs) { pRun = ME_FindItemBack(end_para, diRun); curPara = end_para->member.para.prev_para; } if (!pRun) { pRun = ME_FindItemFwd(end_para, diRun); curPara = end_para; } if (pRun) { nCharsToBoundary = curPara->member.para.nCharOfs + pRun->member.run.nCharOfs - nOfs; if (nCharsToBoundary >= 0) *nChars = min(*nChars, nCharsToBoundary); } } if (*nChars < 0) nChars = 0; } }
void ME_QueueInvalidateFromCursor(ME_TextEditor *editor, int nCursor) { editor->nInvalidOfs = ME_GetCursorOfs(editor, nCursor); }