EM_EXGETSEL does not relate to text selection order. How do I do determine the caret position in a piece of selected text?

StackOverflow https://stackoverflow.com/questions/18393031

  •  26-06-2022
  •  | 
  •  

سؤال

When I do:

SendMessage(editControlHWND, EM_EXGETSEL, 0, (LPARAM)&charRange);

I get the selected range of text. However, I want to know where the caret is in this selection, ie at the end, OR at the beginning.

ie, has the user selected the text 'backwards', as in something like dragging from right to left.

EM_EXGETSEL will always have the smaller number in cpMin, so clearly does not relate to the selection order.

I obviously can't get the caret position with EM_EXGETSEL for comparison in this situation because a chunk of stuff is already selected.

Is there any way to get the caret's current individual position (so that I can compare it to cpMin/cpMax)? Or alternatively, is there any way of determining where the caret is in a block of selected text?

EDIT:

My explanation for why I want to do this: I insert text programmatically into a read-only RichEdit control which the user can select text from. However, when text is added at the end, it has to move the caret to the end and insert the text, and this can happen when text has been selected/the user is currently selecting text.

It's this last one that is the bother. I use EM_EXGETSEL and EM_EXSETSEL to get and set the selected text before and after text is programmatically entered. By default, EM_EXGETSEL will always put the smaller number in cpMin, meaning that if the user is currently selecting text backwards (ie right to left), and text is added to the control, the position of the caret in the selection area changes from the beginning to the end, because I feed these numbers directly into EM_EXSETSEL. I know that EM_EXSETSEL is capable of backwards selection (I have tested this with the larger number in cpMin and the smaller one in cpMax), but EM_EXGETSEL does not give any indication that the user has selected text backwards.

Therefore I need to know the caret position to compare it to cpMin or cpMax to check if it is at the beginning of the selection or the end, and act accordingly.

هل كانت مفيدة؟

المحلول 4

I've managed to do this, although it was a little complicated to get there due to my lack of understanding on the concept of sub-classing. ><

I used Spy++ to look at what messages were being sent when I was selecting text.

This was apparently exclusively EM_GETPASSWORDCHAR messages.

So I did:

case EM_GETPASSWORDCHAR:
    {
        if(hwnd == editControlHwnd)
        {
            CHARRANGE tempCharRange;
            SendMessage(editControlHwnd, EM_EXGETSEL, 0, (LPARAM)&tempCharRange);
            SetSelectionDirection(tempCharRange.cpMin, tempCharRange.cpMax);
            return CallWindowProc(oldWndProc, hwnd, uMsg, wParam, lParam);
        }
    }

With:

void SubWindow::SetSelectionDirection(int newCpMin, int newCpMax) //Set selectionDirection to false if selecting backwards, true if selecting forwards
{
    if((newCpMin != prevCpMin) && (newCpMax == prevCpMax))
        selectionDirection = false;
    else if((newCpMin == prevCpMin) && (newCpMax != prevCpMax))
        selectionDirection = true;

    prevCpMin = newCpMin;
    prevCpMax = newCpMax;
}

Where bool selectionDirection;, int prevCpMin; and int prevCpMax; are private class member variables.

This way I compare the new selected area with the previously selected area to see which end has changed, and which one hasn't.

I don't know if what I'm doing here is a bad way of actually working this out, but if there's a better way to do this, I haven't found it. That's why I'm posting this as the answer in the event that it helps someone else in my position.

نصائح أخرى

Just came across this post while looking into the same problem.

I was able to resolve by tracking changes to the selection notified by EN_SELCHANGE and comparing the results at WM_LBUTTONUP.

There's no easy way to do this. EM_GETSEL and EM_EXGETSEL return the range of the current selection. Only if there is no selection do they return the position of the caret.

Note that the caret can't be in a block of selected text - it is always at the end or beginning.

You could probably implement a solution fairly easily by sub-classing the control, and using EM_GETSEL to query and store the position of the caret after any key or mouse input. E.g.

LRESULT WINAPI EditControlSubclassProc(...)
{
    LRESULT lRes = CallWindowProc(...); // call original window procedure
    if ((uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST)
    ||  (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST))
    {
        DWORD dwStart, dwEnd;
        SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
        if (dwStart == dwEnd)
        {
            // no current selection, so simply store the position of the caret
            g_dwCaretPos = dwStart;
        }
    }
    return lRes;
}

This way you will always know where the caret was the last time there was input that didn't result in a selection. You can then compare it to the range of the selection to determine which end the selection was anchored at, and therefore know the caret is at the other end.

It seems that EM_LINEFROMCHAR and EM_LINEINDEX with (WPARAM == -1) can be used.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top