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; }
BOOL ME_ArrowKey(ME_TextEditor *editor, int nVKey, BOOL extend, BOOL ctrl) { int nCursor = 0; ME_Cursor *p = &editor->pCursors[nCursor]; ME_Cursor tmp_curs = *p; BOOL success = FALSE; ME_CheckCharOffsets(editor); switch(nVKey) { case VK_LEFT: editor->bCaretAtEnd = 0; if (ctrl) success = ME_MoveCursorWords(editor, &tmp_curs, -1); else success = ME_MoveCursorChars(editor, &tmp_curs, -1); break; case VK_RIGHT: editor->bCaretAtEnd = 0; if (ctrl) success = ME_MoveCursorWords(editor, &tmp_curs, +1); else success = ME_MoveCursorChars(editor, &tmp_curs, +1); break; case VK_UP: ME_MoveCursorLines(editor, &tmp_curs, -1); break; case VK_DOWN: ME_MoveCursorLines(editor, &tmp_curs, +1); break; case VK_PRIOR: ME_ArrowPageUp(editor, &tmp_curs); break; case VK_NEXT: ME_ArrowPageDown(editor, &tmp_curs); break; case VK_HOME: { if (ctrl) ME_ArrowCtrlHome(editor, &tmp_curs); else ME_ArrowHome(editor, &tmp_curs); editor->bCaretAtEnd = 0; break; } case VK_END: if (ctrl) ME_ArrowCtrlEnd(editor, &tmp_curs); else ME_ArrowEnd(editor, &tmp_curs); break; } if (!extend) editor->pCursors[1] = tmp_curs; *p = tmp_curs; ME_InvalidateSelection(editor); ME_Repaint(editor); HideCaret(editor->hWnd); ME_EnsureVisible(editor, &tmp_curs); ME_ShowCaret(editor); ME_SendSelChange(editor); return success; }
/* 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; } }