diff --git a/dlls/riched20/caret.c b/dlls/riched20/caret.c index dcaab0c..9638a7a 100644 --- a/dlls/riched20/caret.c +++ b/dlls/riched20/caret.c @@ -740,17 +740,6 @@ ME_MoveCursorWords(ME_TextEditor *editor, ME_Cursor *cursor, int nRelOfs) } -void -ME_SelectWord(ME_TextEditor *editor) -{ - if (!(editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA)) - ME_MoveCursorWords(editor, &editor->pCursors[0], -1); - ME_MoveCursorWords(editor, &editor->pCursors[1], +1); - ME_InvalidateSelection(editor); - ME_SendSelChange(editor); -} - - int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor) { ME_Cursor *pCursor = &editor->pCursors[nCursor]; @@ -853,6 +842,63 @@ ME_CharFromPos(ME_TextEditor *editor, int x, int y) + cursor.pRun->member.run.nCharOfs + cursor.nOffset); } +/* 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) + return; + curOfs = ME_GetCursorOfs(editor, 0); + anchorStartOfs = ME_GetCursorOfs(editor, 2); + anchorEndOfs = ME_GetCursorOfs(editor, 3); + + 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[0] = tmp_cursor; + if (editor->nSelectionType == stWord) + ME_MoveCursorWords(editor, &editor->pCursors[0], -1); + else + { + ME_DisplayItem *pItem; + ME_DIType searchType = (editor->nSelectionType == stLine)?diStartRowOrParagraph:diParagraph; + pItem = ME_FindItemBack(editor->pCursors[0].pRun, searchType); + editor->pCursors[0].pRun = ME_FindItemFwd(pItem, diRun); + editor->pCursors[0].nOffset = 0; + } + } + else if (curOfs >= anchorEndOfs) + { + /* Extend the right 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)?diStartRowOrParagraphOrEnd:diParagraphOrEnd; + pItem = ME_FindItemFwd(editor->pCursors[1].pRun, searchType); + if (pItem->type == diTextEnd) + editor->pCursors[1].pRun = ME_FindItemBack(pItem, diRun); + else + editor->pCursors[1].pRun = ME_FindItemFwd(pItem, diRun); + editor->pCursors[1].nOffset = 0; + } + } +} void ME_LButtonDown(ME_TextEditor *editor, int x, int y) { @@ -866,64 +912,75 @@ void ME_LButtonDown(ME_TextEditor *editor, int x, int y) tmp_cursor = editor->pCursors[0]; is_selection = ME_IsSelection(editor); - if (x >= editor->selofs) + ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd); + + if (x >= editor->selofs || GetKeyState(VK_SHIFT) < 0) { - ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd); if (GetKeyState(VK_SHIFT)>=0) { editor->pCursors[1] = editor->pCursors[0]; + if (editor->nSelectionType == stWord) + { + ME_MoveCursorWords(editor, &editor->pCursors[1], +1); + editor->pCursors[0] = editor->pCursors[1]; + ME_MoveCursorWords(editor, &editor->pCursors[0], -1); + } + else if (editor->nSelectionType == stParagraph) + { + ME_DisplayItem *pItem; + pItem = ME_FindItemBack(editor->pCursors[0].pRun, diParagraph); + editor->pCursors[0].pRun = ME_FindItemFwd(pItem, diRun); + editor->pCursors[0].nOffset = 0; + + pItem = ME_FindItemFwd(editor->pCursors[1].pRun, diParagraphOrEnd); + if (pItem->type == diTextEnd) + editor->pCursors[1].pRun = ME_FindItemBack(pItem, diRun); + else + editor->pCursors[1].pRun = ME_FindItemFwd(pItem, diRun); + editor->pCursors[1].nOffset = 0; + } + /* Store the anchor positions for extending the selection. */ + editor->pCursors[2] = editor->pCursors[0]; + editor->pCursors[3] = editor->pCursors[1]; } - else if (!is_selection) { + else if (!is_selection) + { editor->pCursors[1] = tmp_cursor; - is_selection = 1; } - - ME_InvalidateSelection(editor); - HideCaret(editor->hWnd); - ME_MoveCaret(editor); - ShowCaret(editor->hWnd); - ME_ClearTempStyle(editor); - ME_SendSelChange(editor); + else if (editor->nSelectionType != stPosition) + { + ME_ExtendAnchorSelection(editor); + } } else { - ME_DisplayItem *pRow; + ME_DisplayItem *pItem; - editor->linesel = 1; - editor->sely = y; + editor->nSelectionType = stLine; /* Set pCursors[0] to beginning of line */ - ME_FindPixelPos(editor, x, y, &editor->pCursors[1], &editor->bCaretAtEnd); /* Set pCursors[1] to end of line */ - pRow = ME_FindItemFwd(editor->pCursors[1].pRun, diStartRowOrParagraphOrEnd); - assert(pRow); + pItem = ME_FindItemFwd(editor->pCursors[0].pRun, diStartRowOrParagraphOrEnd); + assert(pItem); + if (pItem->type == diTextEnd) + editor->pCursors[1].pRun = ME_FindItemBack(pItem, diRun); + else + editor->pCursors[1].pRun = ME_FindItemFwd(pItem, diRun); + editor->pCursors[1].nOffset = 0; /* pCursor[0] is the position where the cursor will be drawn, * pCursor[1] is the other end of the selection range - * pCursor[2] and [3] are backups of [0] and [1] so I - * don't have to look them up again + * pCursor[2] and [3] are the selection anchors that are backed up + * so they are kept when the selection changes for drag line selection. */ - - if (pRow->type == diStartRow) { - /* FIXME WTF was I thinking about here ? */ - ME_DisplayItem *pRun = ME_FindItemFwd(pRow, diRun); - assert(pRun); - editor->pCursors[0].pRun = pRun; - editor->pCursors[0].nOffset = 0; - editor->bCaretAtEnd = 1; - } else { - editor->pCursors[0].pRun = ME_FindItemBack(pRow, diRun); - assert(editor->pCursors[0].pRun && editor->pCursors[0].pRun->member.run.nFlags & MERF_ENDPARA); - editor->pCursors[0].nOffset = 0; - editor->bCaretAtEnd = 0; - } editor->pCursors[2] = editor->pCursors[0]; editor->pCursors[3] = editor->pCursors[1]; - ME_InvalidateSelection(editor); - HideCaret(editor->hWnd); - ME_MoveCaret(editor); - ShowCaret(editor->hWnd); - ME_ClearTempStyle(editor); - ME_SendSelChange(editor); } + + ME_InvalidateSelection(editor); + HideCaret(editor->hWnd); + ME_MoveCaret(editor); + ShowCaret(editor->hWnd); + ME_ClearTempStyle(editor); + ME_SendSelChange(editor); } void ME_MouseMove(ME_TextEditor *editor, int x, int y) @@ -934,39 +991,19 @@ void ME_MouseMove(ME_TextEditor *editor, int x, int y) tmp_cursor = editor->pCursors[0]; /* FIXME: do something with the return value of ME_FindPixelPos */ - if (!editor->linesel) - ME_FindPixelPos(editor, x, y, &tmp_cursor, &editor->bCaretAtEnd); - else ME_FindPixelPos(editor, (y > editor->sely) * editor->rcFormat.right, y, &tmp_cursor, &editor->bCaretAtEnd); - - if (!memcmp(&tmp_cursor, editor->pCursors, sizeof(tmp_cursor))) - return; + ME_FindPixelPos(editor, x, y, &tmp_cursor, &editor->bCaretAtEnd); ME_InvalidateSelection(editor); - if (!editor->linesel) - editor->pCursors[0] = tmp_cursor; - else if (!memcmp(&tmp_cursor, editor->pCursors+2, sizeof(tmp_cursor)) || - !memcmp(&tmp_cursor, editor->pCursors+3, sizeof(tmp_cursor))) - { - editor->pCursors[0] = editor->pCursors[2]; - editor->pCursors[1] = editor->pCursors[3]; - } - else if (y < editor->sely) - { - editor->pCursors[0] = tmp_cursor; - editor->pCursors[1] = editor->pCursors[2]; - } - else - { - editor->pCursors[0] = tmp_cursor; - editor->pCursors[1] = editor->pCursors[3]; - } + editor->pCursors[0] = tmp_cursor; + SendMessageW(editor->hWnd, EM_SCROLLCARET, 0, 0); + + ME_ExtendAnchorSelection(editor); HideCaret(editor->hWnd); ME_MoveCaret(editor); ME_InvalidateSelection(editor); ShowCaret(editor->hWnd); ME_SendSelChange(editor); - SendMessageW(editor->hWnd, EM_SCROLLCARET, 0, 0); } static ME_DisplayItem *ME_FindRunInRow(ME_TextEditor *editor, ME_DisplayItem *pRow, diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c index f7e20e0..b363843 100644 --- a/dlls/riched20/editor.c +++ b/dlls/riched20/editor.c @@ -1534,7 +1534,10 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey) { BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000; BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000; - + + if (nKey != VK_SHIFT && nKey != VK_CONTROL) + editor->nSelectionType = stPosition; + switch (nKey) { case VK_LEFT: @@ -1604,7 +1607,7 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey) static BOOL ME_SetCursor(ME_TextEditor *editor, int x) { if ((GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_SELECTIONBAR) && - (x < editor->selofs || editor->linesel)) + (x < editor->selofs || editor->nSelectionType == stLine)) { SetCursor(hLeft); return TRUE; @@ -1646,6 +1649,12 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) { ed->nZoomNumerator = ed->nZoomDenominator = 0; ME_MakeFirstParagraph(ed); ed->bCaretShown = FALSE; + /* The four cursors are for: + * 0 - The position where the caret is shown + * 1 - The anchored end of the selection (for normal selection) + * 2 & 3 - The anchored start and end respectively for word, line, + * or paragraph selection. + */ ed->nCursors = 4; ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors); ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun); @@ -1692,7 +1701,8 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) { ed->selofs = 16; else ed->selofs = 0; - ed->linesel = 0; + ed->nSelectionType = stPosition; + ed->bMultiClick = FALSE; if (GetWindowLongW(hWnd, GWL_STYLE) & ES_PASSWORD) ed->cPasswordMask = '*'; @@ -2958,7 +2968,31 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, ME_DestroyEditor(editor); SetWindowLongPtrW(hWnd, 0, 0); return 0; + case WM_LBUTTONDBLCLK: + SetTimer(hWnd, tidMouseClick, GetDoubleClickTime(), NULL); + editor->bMultiClick = TRUE; + /* Fall through */ case WM_LBUTTONDOWN: + if (editor->bMultiClick) + { + switch (editor->nSelectionType) + { + case stPosition: + editor->nSelectionType = stWord; + break; + case stWord: + editor->nSelectionType = stParagraph; + break; + case stParagraph: + editor->nSelectionType = stWord; + break; + default: + editor->nSelectionType = stPosition; + break; + } + } else if (!ME_IsSelection(editor) || GetKeyState(VK_SHIFT) >= 0) { + editor->nSelectionType = stPosition; + } ME_CommitUndo(editor); /* End coalesced undos for typed characters */ if ((editor->nEventMask & ENM_MOUSEEVENTS) && !ME_FilterEvent(editor, msg, &wParam, &lParam)) @@ -2987,18 +3021,21 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam, else { BOOL ret; - editor->linesel = 0; ret = ME_SetCursor(editor, LOWORD(lParam)); ME_LinkNotify(editor,msg,wParam,lParam); if (!ret) goto do_default; } break; - case WM_LBUTTONDBLCLK: - if ((editor->nEventMask & ENM_MOUSEEVENTS) && - !ME_FilterEvent(editor, msg, &wParam, &lParam)) - return 0; - ME_LinkNotify(editor,msg,wParam,lParam); - ME_SelectWord(editor); + case WM_TIMER: + switch(wParam) + { + case tidMouseClick: + editor->bMultiClick = FALSE; + KillTimer(hWnd, tidMouseClick); + break; + default: + goto do_default; + } break; case WM_RBUTTONUP: case WM_RBUTTONDOWN: diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h index 0fc615a..96c3d41 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -176,7 +176,6 @@ void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod); /* caret.c */ int ME_SetSelection(ME_TextEditor *editor, int from, int to); -void ME_SelectWord(ME_TextEditor *editor); void ME_HideCaret(ME_TextEditor *ed); void ME_ShowCaret(ME_TextEditor *ed); void ME_MoveCaret(ME_TextEditor *ed); diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h index d54edbf..020554c 100644 --- a/dlls/riched20/editstr.h +++ b/dlls/riched20/editstr.h @@ -242,6 +242,13 @@ typedef enum { umAddBackToUndo } ME_UndoMode; +typedef enum { + stPosition = 0, + stWord, + stLine, + stParagraph +} ME_SelectionType; + typedef struct tagME_FontTableItem { BYTE bCharSet; WCHAR *szFaceName; @@ -286,6 +293,10 @@ typedef struct tagME_FontCacheItem #define HFONT_CACHE_SIZE 10 +typedef enum { + tidMouseClick = 1 /* Must be non-zero */ +} ME_TimerID; + typedef struct tagME_TextEditor { HWND hWnd; @@ -331,7 +342,9 @@ typedef struct tagME_TextEditor BOOL bHaveFocus; /*for IME */ int imeStartIndex; - DWORD selofs, linesel, sely; + DWORD selofs; /* The size of the selection bar on the left side of control */ + ME_SelectionType nSelectionType; + BOOL bMultiClick; /* Track previous notified selection */ CHARRANGE notified_cr;