Search code examples
c++winapirichedit-control

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


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.


Solution

  • 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.