[PATCH 6/6] riched20: Implement ITextRange/ITextSelection Copy and Cut.
Huw Davies
huw at codeweavers.com
Thu Oct 15 04:24:27 CDT 2020
From: Damjan Jovanovic <damjan.jov at gmail.com>
Signed-off-by: Damjan Jovanovic <damjan.jov at gmail.com>
Signed-off-by: Huw Davies <huw at codeweavers.com>
---
dlls/riched20/editor.c | 84 +++++++++++++++++++++--------------
dlls/riched20/editor.h | 2 +
dlls/riched20/richole.c | 51 +++++++++++++++++----
dlls/riched20/tests/richole.c | 74 ++++++++++++++++++++++++++++++
4 files changed, 170 insertions(+), 41 deletions(-)
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index d9bcea84f74..c5045961077 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -2333,52 +2333,70 @@ done:
return hr == S_OK;
}
-static BOOL ME_Copy(ME_TextEditor *editor, const ME_Cursor *start, int nChars)
+static HRESULT editor_copy( ME_TextEditor *editor, ME_Cursor *start, int chars, IDataObject **data_out )
{
- LPDATAOBJECT dataObj = NULL;
- HRESULT hr = S_OK;
+ IDataObject *data = NULL;
+ HRESULT hr = S_OK;
- if (editor->cPasswordMask)
- return FALSE; /* Copying or Cutting masked text isn't allowed */
+ if (editor->lpOleCallback)
+ {
+ CHARRANGE range;
+ range.cpMin = ME_GetCursorOfs( start );
+ range.cpMax = range.cpMin + chars;
+ hr = IRichEditOleCallback_GetClipboardData( editor->lpOleCallback, &range, RECO_COPY, &data );
+ }
- if(editor->lpOleCallback)
- {
- CHARRANGE range;
- range.cpMin = ME_GetCursorOfs(start);
- range.cpMax = range.cpMin + nChars;
- hr = IRichEditOleCallback_GetClipboardData(editor->lpOleCallback, &range, RECO_COPY, &dataObj);
- }
- if(FAILED(hr) || !dataObj)
- hr = ME_GetDataObject(editor, start, nChars, &dataObj);
- if(SUCCEEDED(hr)) {
- hr = OleSetClipboard(dataObj);
- IDataObject_Release(dataObj);
- }
- return SUCCEEDED(hr);
+ if (FAILED( hr ) || !data)
+ hr = ME_GetDataObject( editor, start, chars, &data );
+
+ if (SUCCEEDED( hr ))
+ {
+ if (data_out)
+ *data_out = data;
+ else
+ {
+ hr = OleSetClipboard( data );
+ IDataObject_Release( data );
+ }
+ }
+
+ return hr;
}
-static BOOL copy_or_cut(ME_TextEditor *editor, BOOL cut)
+HRESULT editor_copy_or_cut( ME_TextEditor *editor, BOOL cut, BOOL ui, ME_Cursor *start, int count,
+ IDataObject **data_out )
{
- 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;
+ return E_ACCESSDENIED;
}
- num_chars -= offs;
- result = ME_Copy(editor, sel_start, num_chars);
- if (result && cut)
+ hr = editor_copy( editor, start, count, data_out );
+ if (SUCCEEDED(hr) && cut)
{
- ME_InternalDeleteText(editor, sel_start, num_chars, FALSE);
- ME_CommitUndo(editor);
- ME_UpdateRepaint(editor, TRUE);
+ ME_InternalDeleteText( editor, start, count, FALSE );
+ ME_CommitUndo( editor );
+ ME_UpdateRepaint( editor, TRUE );
}
- return result;
+ return hr;
+}
+
+static BOOL copy_or_cut( ME_TextEditor *editor, BOOL cut )
+{
+ HRESULT hr;
+ int offs, count;
+ int start_cursor = ME_GetSelectionOfs( editor, &offs, &count );
+ ME_Cursor *sel_start = &editor->pCursors[start_cursor];
+
+ if (editor->cPasswordMask) return FALSE;
+
+ count -= offs;
+ hr = editor_copy_or_cut( editor, cut, TRUE, sel_start, count, NULL );
+ if (FAILED( hr )) MessageBeep( MB_ICONERROR );
+
+ return SUCCEEDED( hr );
}
/* helper to send a msg filter notification */
diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h
index 0503a671ea1..07bcc1aa4b9 100644
--- a/dlls/riched20/editor.h
+++ b/dlls/riched20/editor.h
@@ -270,6 +270,8 @@ void ME_StreamInFill(ME_InStream *stream) DECLSPEC_HIDDEN;
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;
+HRESULT editor_copy_or_cut( ME_TextEditor *editor, BOOL cut, BOOL ui, ME_Cursor *start, int count,
+ IDataObject **data_out ) DECLSPEC_HIDDEN;
ME_Paragraph *editor_first_para( ME_TextEditor *editor ) DECLSPEC_HIDDEN;
/* table.c */
diff --git a/dlls/riched20/richole.c b/dlls/riched20/richole.c
index 923984e96b1..6e3ae8ded96 100644
--- a/dlls/riched20/richole.c
+++ b/dlls/riched20/richole.c
@@ -2594,28 +2594,53 @@ 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;
+ IDataObject **data_out = NULL;
+
+ 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 );
+ }
+ cursor_from_char_ofs( editor, start, &cursor );
+
+ if (v && V_VT(v) == (VT_UNKNOWN | VT_BYREF) && V_UNKNOWNREF( v ))
+ data_out = (IDataObject **)V_UNKNOWNREF( v );
+
+ return editor_copy_or_cut( editor, cut, FALSE, &cursor, end - start, data_out );
+}
+
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)
@@ -5365,25 +5390,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..a5f439aece7 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_(__FILE__,line)(SUCCEEDED(hr), "SetRange failed: 0x%08x\n", hr);
+ hr = ITextRange_Copy(range, NULL);
+ ok_(__FILE__,line)(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();
}
--
2.23.0
More information about the wine-devel
mailing list