-1

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.

Interminable
  • 1,338
  • 3
  • 21
  • 52

4 Answers4

1

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.

AndyDF
  • 59
  • 5
0

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.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • Yeah I'm only interested in the beginning or the end. Not exactly sure how the caret could end up being inside the selection itself... Anyway, I think I get what you're saying. Is there an way to deselect the text without moving the caret? Or would I have to simulate a keypress of some kind that either does not move the caret, OR moves the caret a little and then back again? – Interminable Aug 23 '13 at 01:19
  • Yes you can deselect using the `EM_SETSEL` message. – Jonathan Potter Aug 23 '13 at 01:26
  • Yes, but that won't tell me which end was the caret's original position, will it? By deselect I mean deselect the selected text, but maintain the caret's position at either the beginning or the end of the originally selected block of text. – Interminable Aug 23 '13 at 01:31
  • No, you'd need to use a method similar to my suggestion to work out the caret's position, and then use `EM_SETSEL` to set it to that position. – Jonathan Potter Aug 23 '13 at 01:36
  • My referring to simulating keypresses etc was because this needs to work independently of user input. – Interminable Aug 23 '13 at 01:41
  • The whole concept of caret position is surely meaningless without user input? – Jonathan Potter Aug 23 '13 at 02:14
  • I have added an explanation for this to my question. – Interminable Aug 23 '13 at 08:39
0

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

Codeguard
  • 7,787
  • 2
  • 38
  • 41
  • Sadly these both seem to be pulling back the line `cpMin` is on rather than the line the caret itself is on when a block of text is selected. – Interminable Aug 23 '13 at 11:53
0

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.

Community
  • 1
  • 1
Interminable
  • 1,338
  • 3
  • 21
  • 52