diff --git a/dlls/riched20/caret.c b/dlls/riched20/caret.c index c5fa51894e8..75520a64c5c 100644 --- a/dlls/riched20/caret.c +++ b/dlls/riched20/caret.c @@ -301,7 +301,7 @@ void update_caret(ME_TextEditor *editor) hide_caret(editor); } -BOOL ME_InternalDeleteText(ME_TextEditor *editor, ME_Cursor *start, +BOOL ME_InternalDeleteText(ME_TextEditor *editor, const ME_Cursor *start, int nChars, BOOL bForce) { ME_Cursor c = *start; diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c index a144137baa1..27a329d3a4c 100644 --- a/dlls/riched20/editor.c +++ b/dlls/riched20/editor.c @@ -2335,13 +2335,13 @@ done: return hr == S_OK; } -static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars) +static HRESULT ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars, VARIANT *v) { LPDATAOBJECT dataObj = NULL; HRESULT hr = S_OK; if (editor->cPasswordMask) - return FALSE; /* Copying or Cutting masked text isn't allowed */ + return E_ACCESSDENIED; /* Copying or Cutting masked text isn't allowed */ if(editor->lpOleCallback) { @@ -2353,34 +2353,49 @@ static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars) if(FAILED(hr) || !dataObj) hr = ME_GetDataObject(editor, start, nChars, &dataObj); if(SUCCEEDED(hr)) { - hr = OleSetClipboard(dataObj); + IUnknown **ppUnknown = NULL; + if (v != NULL && V_VT(v) == (VT_UNKNOWN | VT_BYREF)) + ppUnknown = V_UNKNOWNREF(v); + if (ppUnknown != NULL) + hr = IDataObject_QueryInterface(dataObj, &IID_IUnknown, (void**)ppUnknown); + else + hr = OleSetClipboard(dataObj); IDataObject_Release(dataObj); } - return SUCCEEDED(hr); + return hr; } -static BOOL copy_or_cut(ME_TextEditor *editor, BOOL cut) +HRESULT ME_CopyOrCut(ME_TextEditor *editor, BOOL cut, BOOL silent, const ME_Cursor *start, int nChars, VARIANT *v) { - BOOL result; - int offs, num_chars; - int start_cursor = ME_GetSelectionOfs(editor, &offs, &num_chars); - ME_Cursor *sel_start = &editor->pCursors[start_cursor]; + HRESULT hr; if (cut && (editor->styleFlags & ES_READONLY)) { - MessageBeep(MB_ICONERROR); - return FALSE; + if (!silent) + MessageBeep(MB_ICONERROR); + return E_ACCESSDENIED; } - num_chars -= offs; - result = ME_Copy(editor, sel_start, num_chars); - if (result && cut) + hr = ME_Copy(editor, start, nChars, v); + if (SUCCEEDED(hr) && cut) { - ME_InternalDeleteText(editor, sel_start, num_chars, FALSE); + ME_InternalDeleteText(editor, start, nChars, FALSE); ME_CommitUndo(editor); ME_UpdateRepaint(editor, TRUE); } - return result; + return hr; +} + +static BOOL wm_copy_or_cut(ME_TextEditor *editor, BOOL cut) +{ + HRESULT result; + int offs, num_chars; + int start_cursor = ME_GetSelectionOfs(editor, &offs, &num_chars); + ME_Cursor *sel_start = &editor->pCursors[start_cursor]; + + num_chars -= offs; + result = ME_CopyOrCut(editor, cut, FALSE, sel_start, num_chars, NULL); + return SUCCEEDED(result); } /* helper to send a msg filter notification */ @@ -2699,7 +2714,7 @@ ME_KeyDown(ME_TextEditor *editor, WORD nKey) case 'C': case 'X': if (ctrl_is_down) - return copy_or_cut(editor, nKey == 'X'); + return wm_copy_or_cut(editor, nKey == 'X'); break; case 'Z': if (ctrl_is_down) @@ -4146,7 +4161,7 @@ LRESULT ME_HandleMessage(ME_TextEditor *editor, UINT msg, WPARAM wParam, return 0; case WM_CUT: case WM_COPY: - copy_or_cut(editor, msg == WM_CUT); + wm_copy_or_cut(editor, msg == WM_CUT); return 0; case WM_GETTEXTLENGTH: { diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h index 1a9f3fd6fb0..3cd87e0e8be 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -179,7 +179,7 @@ BOOL ME_IsSelection(ME_TextEditor *editor) DECLSPEC_HIDDEN; void ME_DeleteSelection(ME_TextEditor *editor) DECLSPEC_HIDDEN; void ME_SendSelChange(ME_TextEditor *editor) DECLSPEC_HIDDEN; void ME_InsertOLEFromCursor(ME_TextEditor *editor, const REOBJECT* reo, int nCursor) DECLSPEC_HIDDEN; -BOOL ME_InternalDeleteText(ME_TextEditor *editor, ME_Cursor *start, int nChars, BOOL bForce) DECLSPEC_HIDDEN; +BOOL ME_InternalDeleteText(ME_TextEditor *editor, const ME_Cursor *start, int nChars, BOOL bForce) DECLSPEC_HIDDEN; int ME_GetTextLength(ME_TextEditor *editor) DECLSPEC_HIDDEN; int ME_GetTextLengthEx(ME_TextEditor *editor, const GETTEXTLENGTHEX *how) DECLSPEC_HIDDEN; ME_Style *ME_GetSelectionInsertStyle(ME_TextEditor *editor) DECLSPEC_HIDDEN; @@ -271,6 +271,7 @@ extern BOOL me_debug DECLSPEC_HIDDEN; void ME_ReplaceSel(ME_TextEditor *editor, BOOL can_undo, const WCHAR *str, int len) DECLSPEC_HIDDEN; int set_selection( ME_TextEditor *editor, int to, int from ) DECLSPEC_HIDDEN; ME_Paragraph *editor_first_para( ME_TextEditor *editor ) DECLSPEC_HIDDEN; +HRESULT ME_CopyOrCut(ME_TextEditor *editor, BOOL cut, BOOL silent, const ME_Cursor *start, int nChars, VARIANT *v); /* table.c */ BOOL ME_IsInTable(ME_DisplayItem *pItem) DECLSPEC_HIDDEN; diff --git a/dlls/riched20/richole.c b/dlls/riched20/richole.c index 47d8442d5ce..4dfdae6cee3 100644 --- a/dlls/riched20/richole.c +++ b/dlls/riched20/richole.c @@ -2586,28 +2586,48 @@ static HRESULT WINAPI ITextRange_fnDelete(ITextRange *me, LONG unit, LONG count, return E_NOTIMPL; } +static HRESULT textrange_copy_or_cut(ITextRange *range, ME_TextEditor *editor, BOOL cut, VARIANT *v) +{ + LONG start, end; + ME_Cursor cursor; + + ITextRange_GetStart(range, &start); + ITextRange_GetEnd(range, &end); + if (start == end) + { + /* If the range is empty, all text is copied */ + LONG prev_end = end; + ITextRange_SetEnd(range, MAXLONG); + start = 0; + ITextRange_GetEnd(range, &end); + ITextRange_SetEnd(range, prev_end); + } + ME_CursorFromCharOfs(editor, start, &cursor); + return ME_CopyOrCut(editor, cut, TRUE, &cursor, end - start, v); +} + static HRESULT WINAPI ITextRange_fnCut(ITextRange *me, VARIANT *v) { ITextRangeImpl *This = impl_from_ITextRange(me); - FIXME("(%p)->(%p): stub\n", This, v); + TRACE("(%p)->(%p)\n", This, v); if (!This->child.reole) return CO_E_RELEASED; - return E_NOTIMPL; + return textrange_copy_or_cut(me, This->child.reole->editor, TRUE, v); } static HRESULT WINAPI ITextRange_fnCopy(ITextRange *me, VARIANT *v) { ITextRangeImpl *This = impl_from_ITextRange(me); - FIXME("(%p)->(%p): stub\n", This, v); + TRACE("(%p)->(%p)\n", This, v); if (!This->child.reole) return CO_E_RELEASED; - return E_NOTIMPL; + return textrange_copy_or_cut(me, This->child.reole->editor, FALSE, v); } static HRESULT WINAPI ITextRange_fnPaste(ITextRange *me, VARIANT *v, LONG format) @@ -5357,25 +5377,35 @@ static HRESULT WINAPI ITextSelection_fnDelete(ITextSelection *me, LONG unit, LON static HRESULT WINAPI ITextSelection_fnCut(ITextSelection *me, VARIANT *v) { ITextSelectionImpl *This = impl_from_ITextSelection(me); + ITextRange *range = NULL; + HRESULT hr; - FIXME("(%p)->(%p): stub\n", This, v); + TRACE("(%p)->(%p): stub\n", This, v); if (!This->reOle) return CO_E_RELEASED; - return E_NOTIMPL; + ITextSelection_QueryInterface(me, &IID_ITextRange, (void**)&range); + hr = textrange_copy_or_cut(range, This->reOle->editor, TRUE, v); + ITextRange_Release(range); + return hr; } static HRESULT WINAPI ITextSelection_fnCopy(ITextSelection *me, VARIANT *v) { ITextSelectionImpl *This = impl_from_ITextSelection(me); + ITextRange *range = NULL; + HRESULT hr; - FIXME("(%p)->(%p): stub\n", This, v); + TRACE("(%p)->(%p)\n", This, v); if (!This->reOle) return CO_E_RELEASED; - return E_NOTIMPL; + ITextSelection_QueryInterface(me, &IID_ITextRange, (void**)&range); + hr = textrange_copy_or_cut(range, This->reOle->editor, FALSE, v); + ITextRange_Release(range); + return hr; } static HRESULT WINAPI ITextSelection_fnPaste(ITextSelection *me, VARIANT *v, LONG format) diff --git a/dlls/riched20/tests/richole.c b/dlls/riched20/tests/richole.c index 16ff1fafa67..247c558808d 100644 --- a/dlls/riched20/tests/richole.c +++ b/dlls/riched20/tests/richole.c @@ -4012,6 +4012,79 @@ static void test_character_movement(void) ITextRange_Release(range); } +#define CLIPBOARD_RANGE_CONTAINS(range, start, end, expected) _clipboard_range_contains(range, start, end, expected, __LINE__, 0); +#define TODO_CLIPBOARD_RANGE_CONTAINS(range, start, end, expected) _clipboard_range_contains(range, start, end, expected, __LINE__, 1); +static void _clipboard_range_contains(ITextRange *range, LONG start, LONG end, const char *expected, int line, int todo) +{ + HRESULT hr; + BOOL clipboard_open; + HGLOBAL global; + const char *clipboard_text; + + hr = ITextRange_SetRange(range, start, end); + ok(SUCCEEDED(hr), "SetRange failed: 0x%08x\n", hr); + hr = ITextRange_Copy(range, NULL); + ok(hr == S_OK, "Copy failed: 0x%08x\n", hr); + + clipboard_open = OpenClipboard(NULL); + ok_(__FILE__,line)(clipboard_open, "OpenClipboard failed: %d\n", GetLastError()); + global = GetClipboardData(CF_TEXT); + ok_(__FILE__,line)(global != NULL, "GetClipboardData failed: %p\n", global); + clipboard_text = GlobalLock(global); + ok_(__FILE__,line)(clipboard_text != NULL, "GlobalLock failed: %p\n", clipboard_text); + todo_wine_if(todo) ok_(__FILE__,line)(!strcmp(expected, clipboard_text), "unexpected contents: %s\n", wine_dbgstr_a(clipboard_text)); + GlobalUnlock(global); + CloseClipboard(); +} + +static void test_clipboard(void) +{ + static const char text_in[] = "ab\n c"; + IRichEditOle *reole = NULL; + ITextDocument *doc = NULL; + ITextRange *range; + ITextSelection *selection; + HRESULT hr; + HWND hwnd; + + create_interfaces(&hwnd, &reole, &doc, &selection); + SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text_in); + + hr = ITextDocument_Range(doc, 0, 0, &range); + ok(hr == S_OK, "got 0x%08x\n", hr); + + CLIPBOARD_RANGE_CONTAINS(range, 0, 5, "ab\r\n c") + CLIPBOARD_RANGE_CONTAINS(range, 0, 0, "ab\r\n c") + CLIPBOARD_RANGE_CONTAINS(range, 1, 1, "ab\r\n c") + CLIPBOARD_RANGE_CONTAINS(range, 0, 1, "a") + CLIPBOARD_RANGE_CONTAINS(range, 5, 6, "") + + /* Setting password char does not stop Copy */ + SendMessageA(hwnd, EM_SETPASSWORDCHAR, '*', 0); + CLIPBOARD_RANGE_CONTAINS(range, 0, 1, "a") + + /* Cut can be undone */ + hr = ITextRange_SetRange(range, 0, 1); + ok(SUCCEEDED(hr), "SetRange failed: 0x%08x\n", hr); + hr = ITextRange_Cut(range, NULL); + ok(hr == S_OK, "Cut failed: 0x%08x\n", hr); + CLIPBOARD_RANGE_CONTAINS(range, 0, 4, "b\r\n c"); + hr = ITextDocument_Undo(doc, 1, NULL); + todo_wine ok(hr == S_OK, "Undo failed: 0x%08x\n", hr); + TODO_CLIPBOARD_RANGE_CONTAINS(range, 0, 5, "ab\r\n c"); + + /* Cannot cut when read-only */ + SendMessageA(hwnd, EM_SETREADONLY, TRUE, 0); + hr = ITextRange_SetRange(range, 0, 1); + ok(SUCCEEDED(hr), "SetRange failed: 0x%08x\n", hr); + hr = ITextRange_Cut(range, NULL); + ok(hr == E_ACCESSDENIED, "got 0x%08x\n", hr); + + release_interfaces(&hwnd, &reole, &doc, NULL); + ITextSelection_Release(selection); + ITextRange_Release(range); +} + START_TEST(richole) { /* Must explicitly LoadLibrary(). The test has no references to functions in @@ -4052,4 +4125,5 @@ START_TEST(richole) test_Expand(); test_MoveEnd_story(); test_character_movement(); + test_clipboard(); }