0

I've got a simple chat program. I use "CreateWindow" function for the typing box:

chat_handle_11 = CreateWindow("EDIT", "", WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | WS_EX_CONTROLPARENT, 226, 447, 424, 23, hWnd, NULL, NULL, NULL);
SendMessage(chat_handle_11, EM_LIMITTEXT, ChatTextLimitInBox, 0L);

When I paste any text containing new line characters (using the right mouse click or ctrl+v), for example:

Test line 1 text
Test line 2 text
Test line 3 text

Only the first line is pasted to the typing window:

Test line 1 text

I'd like to change the text on-paste, to ignore new line characters:

Test line 1 text Test line 2 text Test line 3 text

I tried to handle the WM_PASTE message, unfortunately it didn't work:

switch (message)
{
case WM_PASTE:
{
    MessageBox(NULL, "pasting", "pasting", MB_YESNO | MB_ICONQUESTION);
    break;
}
...

The MessageBox was never shown. Is WM_PASTE the correct message in this case?

Additionally, I tried to add "ES_MULTILINE" to the CreateWindow, but then, when I attempt to paste the text containing multiple lines, no text is pasted at all, I can only hear the "beep" sound.

I know I could remove new lines by detecting for clipboard changes and then overwrite it, but this solution would "invade" users clipboard, so I don't want to use it.

I would be very appreciate any help.

Mona
  • 337
  • 3
  • 15
  • 1
    `ES_MULTILINE` must be if you want multiple lines. also `WS_EX_CONTROLPARENT` must be in extended style, not in style – RbMm Dec 29 '17 at 12:24
  • Yes, but then pasting multiple lines doesn't work at all. I actually want a single line in the typing window. The best example I can find now is Google Chrome address bar. When we paste any multi-line texts to it, it's converted to single line with removed new line characters. – Mona Dec 29 '17 at 12:27
  • 2
    `WM_PASTE` - you subclass edit control ? – RbMm Dec 29 '17 at 12:31
  • Thanks @RbMm. I didn't subclass it, just tried to handle this message in the same CALLBACK where WM_CREATE is executed. It seems to work now (the message box is shown), so I only need to figure out how to replace that text on paste :) – Mona Dec 29 '17 at 12:44
  • 1
    yes, the `WM_PASTE` got exactly edit control to where you try paste text, but not it parent – RbMm Dec 29 '17 at 12:48
  • 2
    in what problem get context of clipboard yourself on `WM_PASTE`, process text yourself (simplest way replace both '\r' and '\n' to space , but can do whatever. and finally yourself send `EM_SETSEL` + `EM_REPLACESEL` and not call edit original handler – RbMm Dec 29 '17 at 13:06
  • Thank you, works great! I didn't have to use EM_SETSEL to keep the original "pasting" position. I'll write the updated code in answer. – Mona Dec 29 '17 at 14:11

1 Answers1

1

Thanks to @RbMm for help. I was able to fix the problem.

  1. I didn't use the subclass for the Edit Control and I tried to handle the WM_PASTE message in the parent window.

Fixed code:

chat_handle_11 = CreateWindow("EDIT", "", WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | WS_EX_CONTROLPARENT, 226, 447, 424, 23, hWnd, NULL, NULL, NULL);
SendMessage(chat_handle_11, EM_LIMITTEXT, ChatTextLimitInBox, 0L);
SetWindowSubclass(chat_handle_11, EditBoxForPasteFixes, 0, 0);

Then the new CALLBACK:

LRESULT CALLBACK EditBoxForPasteFixes(HWND handle, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
    switch (uMsg) {
    case WM_PASTE:
    {
        try {
            wstring ClipboardText = GetClipboardText();
            find_and_replace_ws(ClipboardText, L"\r\n", L" ");
            find_and_replace_ws(ClipboardText, L"\r", L" ");
            find_and_replace_ws(ClipboardText, L"\n", L" ");
            //We don't need to SETSEL, so we keep original position for pasting
            //SendMessage(handle, EM_SETSEL, WPARAM(0), LPARAM(-1));
            SendMessageW(handle, EM_REPLACESEL, WPARAM(TRUE), LPARAM(ClipboardText.c_str()));
        }
        catch (...) {
            return FALSE;
        }
        return TRUE;
        break;
    }

    /*case WM_LBUTTONDOWN:
        //std::wcout << handle << L" click\n"; //click event works
        break;*/
    case WM_NCDESTROY:
    {
        RemoveWindowSubclass(handle, EditBoxForPasteFixes, 0);
        // fall through
    }
    default:
    {
        return DefSubclassProc(handle, uMsg, wParam, lParam);
    }
    }
    return 0;
}

And GetClipboardText function:

std::wstring GetClipboardText()
{
    bool Failed = false;
    std::wstring ReturnText = L"";
    // Try opening the clipboard
    if (!OpenClipboard(nullptr)) {
        Failed = true;
    }
    // Get handle of clipboard object for ANSI text
    if (!Failed) {
        //HANDLE hData = GetClipboardData(CF_TEXT);
        HANDLE hData = GetClipboardData(CF_UNICODETEXT);
        if (hData == nullptr) {
            Failed = true;
        }

        // Lock the handle to get the actual text pointer
        if (!Failed) {
            wchar_t * pszText = static_cast<wchar_t*>(GlobalLock(hData));
            if (pszText == nullptr) {
                Failed = true;
            }
            if (!Failed) {
                std::wstring text(pszText);
                ReturnText = text;
            }
            // Release the lock
            GlobalUnlock(hData);
        }
        // Release the clipboard
        CloseClipboard();
    }
    return ReturnText;
}

For find_and_replace_ws I use the boost function, but can be replaced by anything else:

void find_and_replace_ws(wstring& source, wstring const& find, wstring const& replace)
{
    boost::replace_all(source, find, replace);
    /*for (std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
    {
    source.replace(i, find.length(), replace);
    i += replace.length() - find.length() + 1;
    }*/
}

Not a perfect code, I know, but enough for my needs :)

Mona
  • 337
  • 3
  • 15
  • 1
    You could simplify the code by removing the `failed` variable. Instead use the function results directly in the `if` statements, e. g. `if(OpenClipboard(nullptr)) { HANDLE hData = GetClipboardData(CF_UNICODETEXT); /* ... and so on ... */}` – zett42 Dec 29 '17 at 15:20