EM_SETSEL交换参数

时间:2012-01-04 10:46:50

标签: c++ winapi

我使用EM_SETSEL消息在编辑控件中选择文本。我需要从末尾到中间选择一些文本,以便插入位置位于文本的中间。 MSDN documentation声明如下:

  

起始值可能大于结束值。两个值中的较低者指定选择中第一个字符的字符位置。较高的值指定超出选择范围的第一个字符的位置。

     

起始值是选择的锚点,结束值是活动结束。如果用户使用SHIFT键调整选择的大小,则活动结束可以移动,但锚点保持不变。

但似乎较小的价值总是成为锚,例如我无法达到预期的行为。

代码示例(其中“parent”是CWnd *):

TRACE("EM_SETSEL(%d, %d)\n", pos1, pos2);
parent->SendMessage(EM_SETSEL, pos1, pos2);
parent->SendMessage(EM_GETSEL, (WPARAM)&pos1, (LPARAM)&pos2);
TRACE("EM_GETSEL(%d, %d)\n", pos1, pos2);

产生输出:

EM_SETSEL(5, 1)
EM_GETSEL(1, 5)

是否有另一种方法可以获得所需的选择?

2 个答案:

答案 0 :(得分:0)

这可以通过一个相当难看的kludge来完成。基本上没有办法告诉控件选择文本并将光标留在左侧,但你可以通过模拟按键使控件自己完成。

void EditSelSel(HWND hwndEdit, int iFirst, int iSecond)
{
    if (iFirst <= iSecond)
        SendMessage(hwndEdit, EM_SETSEL, iFirst, iSecond);
    else
    {
        SendMessage(hwndEdit, EM_SETSEL, iFirst, iFirst);

        BYTE bState[256]{}, bNewState[256]{};
        if (GetKeyboardState(bState))
        {
            memcpy(bNewState, bState, sizeof(bNewState));
            bNewState[VK_SHIFT] |= 128;
            if (SetKeyboardState(bNewState))
            {
                int i = iFirst - iSecond;
                while (i-- > 0)
                {
                    SendMessage(hwndEdit, WM_KEYDOWN, VK_LEFT, 0);
                }
                SendMessage(hwndEdit, WM_KEYUP, VK_LEFT, 0);
                SetKeyboardState(bState);
            }
        }
    }
}

这可以通过将光标定位在选择范围的右端来实现。然后我们使用SetKeyboardState来诱导控件认为 Shift 键被按下,然后模拟足够的 Left 键按下以将范围移动到左端

丑陋,但它确实有效,所以希望有人发现它很有用。

答案 1 :(得分:0)

关于EM_GETSEL / EM_SETSEL:

  • EM_GETSEL检索左/右位置
  • EM_SETSEL设置主播/有效职位

EM_SETSEL使用锚点/活动位置,允许您轻松地将插入符号放在选择的左侧/右侧,因此我不确定为什么在另一个答案中使用了kludge。

EM_GETSEL是一个尴尬的窗口消息,需要一个kludge。这个kludge暂时将选择更改为0个字符,以便检索活动位置,但是,当我使用它时,我还没有看到任何明显的变化。

要检索锚点/活动位置:

  • 使用EM_GETSEL检索左/右位置
  • 使用EM_SETSEL暂时将选择设置为0个字符,使插入符号处于活动位置
  • 使用EM_GETSEL检索有效位置
  • 使用EM_SETSEL恢复原始选择

用于设置选择的一些示例AutoHotkey代码:

q:: ;Notepad - set active position (caret) at right
PostMessage, 0xB1, 5, 10, Edit1, A ;EM_SETSEL := 0xB1
return

w:: ;Notepad - set active position (caret) at left
PostMessage, 0xB1, 10, 5, Edit1, A ;EM_SETSEL := 0xB1
return

一些示例AutoHotkey函数用于获取/设置选择:

JEE_EditGetRange(hCtl, ByRef vPos1, ByRef vPos2)
{
    VarSetCapacity(vPos1, 4), VarSetCapacity(vPos2, 4)
    SendMessage, 0xB0, % &vPos1, % &vPos2,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0 ;(left, right)
    vPos1 := NumGet(&vPos1, 0, "UInt"), vPos2 := NumGet(&vPos2, 0, "UInt")
}

;==================================================

JEE_EditSetRange(hCtl, vPos1, vPos2, vDoScroll:=0)
{
    SendMessage, 0xB1, % vPos1, % vPos2,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1 ;(anchor, active)
    if vDoScroll
        SendMessage, 0xB7, 0, 0,, % "ahk_id " hCtl ;EM_SCROLLCARET := 0xB7
}

;==================================================

;note: although this involves deselecting and selecting it seems to happen invisibly
JEE_EditGetRangeAnchorActive(hCtl, ByRef vPos1, ByRef vPos2)
{
    ;get selection
    VarSetCapacity(vPos1, 4), VarSetCapacity(vPos2, 4)
    SendMessage, 0xB0, % &vPos1, % &vPos2,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0
    vPos1 := NumGet(&vPos1, 0, "UInt"), vPos2 := NumGet(&vPos2, 0, "UInt")
    if (vPos1 = vPos2)
        return
    vPos1X := vPos1, vPos2X := vPos2

    ;set selection to 0 characters and get active position
    SendMessage, 0xB1, -1, 0,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1
    VarSetCapacity(vPos2, 4)
    SendMessage, 0xB0, % &vPos2, 0,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0
    vPos2 := NumGet(&vPos2, 0, "UInt")

    ;restore selection
    vPos1 := (vPos2 = vPos2X) ? vPos1X : vPos2X
    SendMessage, 0xB1, % vPos1, % vPos2,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1 ;(anchor, active)
}

LINKS:

我最初在AutoHotkey论坛发布的上述功能:
GUI命令:完整的RETHINK - AutoHotkey社区
https://autohotkey.com/boards/viewtopic.php?f=5&t=25893&p=138292#p138292