#SingleInstance force #NoEnv SetBatchLines -1 VarSetCapacity(A1,4) VarSetCapacity(A2,4) ^LButton:: $LButton:: MouseGetPos mX, mY, Win,Ctrl ; Info about pointer location ControlGetPos cX, cY,,,%Ctrl%, ahk_id %Win% ; Top-left corner pos of Ctrl SendMessage 0xD7,,% (mY-cY)<<16|(mX-cX),%Ctrl%,ahk_id %Win% ; CharFromPos c := ErrorLevel & 0xFFFF ; char index < 64K SendMessage 0xB0, &A1, &A2, %Ctrl%, ahk_id %Win% ; GetSel c1 := *(&A1) + (*(&A1+1)<<8) ; Selection start c2 := *(&A2) + (*(&A2+1)<<8) ; Selection end If (Ctrl <> "Edit1" or c < c1 or c2 <= c) ; Clicked not in edit1 selection SendInput {LButton Down} Return ^LButton Up:: $LButton Up:: If (Ctrl <> "Edit1" or c < c1 or c2 <= c) { ; Clicked not in edit1 selection SendInput {LButton Up} Return } MouseGetPos mX, mY ; Pointer location SendMessage 0xD7,,% (mY-cY)<<16|(mX-cX),%Ctrl%,ahk_id %Win% ; CharFromPos at button up c := ErrorLevel & 0xFFFF ; char index < 64K If (c1 <= c and c < c2) { ; Pointer still in selection SendInput {LButton} Return } ControlGet T, Selected,,%Ctrl%,ahk_id %Win% StringReplace T, T, `r,,All If Asc(A_ThisHotKey) = Asc("^") ; Ctrl-Drag: duplicates SendInput {LButton}{RAW}%T% Else If (c < c1) ; Deleting selection keeps caret SendInput {DEL}{LButton}{RAW}%T% Else { ; Deleting selection moves caret SendInput {LButton}{RAW}%T% ; Insert text Sleep 50 ; Time to update SendMessage 0xB1, c1, c2, %Ctrl%, ahk_id %Win% ; SetSel re-select SendMessage 0xC2, 1, "", %Ctrl%, ahk_id %Win% ; ReplaceSel = deletes SendMessage 0xB1, c+c1-c2, c+c1-c2, %Ctrl%, ahk_id %Win% ; SetSel set insertion point } ReturnThere are some other limitations, too, like single character selections are hard to move.
Drag & Drop in Edit Controls
CleanNews.in : Bite sized latest news headlines from India with zero bloat
Awesome.
btw. do you see any chance to add a little cursor that moves along the line when dragging highlighted text? Don't know how to describe it properly.
You can see what I mean, when you highlight some text within the edit control where you write you posting within phpbb. When dragging the highlighted text a cursor/caret moves along the line. It helps to see where/at what position the dragged text would paste.
______________________________
Cheers
AGU
You could always try changing the mouse cursor while dragging.. but I'm not sure if it would work as smoothly as you want it too..do you see any chance to add a little cursor that moves along the line when dragging highlighted text?
Edit: Whoops- Laszlo just said that!
The index (position) of the character closest to the mouse pointer is calculated using the index of the line, so edit controls can be handled with up to 64K lines, each at most 64K characters long. (64K lines > 1300 pages of text with 50 lines per page.)
Messages are used to insert/delete selections. They are faster than sending keystrokes.
#SingleInstance force #NoEnv Process Priority,,High SetBatchLines -1 #UseHook VarSetCapacity(A1,4) VarSetCapacity(A2,4) ^LButton:: LButton:: MouseGetPos mX, mY, Win,Ctrl ; Info about pointer location ControlGetPos cX, cY,,,%Ctrl%, ahk_id %Win% ; Top-left corner pos of Ctrl c := Char(mX-cX, mY-cY, Ctrl, Win) ; Char# SendMessage 0xB0, &A1, &A2, %Ctrl%, ahk_id %Win% ; GetSel c1 := *(&A1)+(*(&A1+1)<<8)+(*(&A1+2)<<16)+(*(&A1+3)<<24) ; Selection start c2 := *(&A2)+(*(&A2+1)<<8)+(*(&A2+2)<<16)+(*(&A2+3)<<24) ; Selection end If (InStr(Ctrl,"Edit") <> 1 or c < c1 or c2 <= c) ; Clicked not in Edit, or in selection SendInput {LButton Down} Return ~^LButton Up:: ~LButton Up:: If (InStr(Ctrl,"Edit") <> 1 or c < c1 or c2 <= c) ; Clicked not in Edit, or in selection Return MouseGetPos mX, mY ; Pointer location c := Char(mX-cX, mY-cY, Ctrl, Win) ; Char# If (c1 <= c and c < c2) { ; Pointer still in selection Click ; Unselect Return } ControlGet T, Selected,,%Ctrl%,ahk_id %Win% ; Save selected text If A_ThisHotKey = ~^LButton Up ; Ctrl-Drag: duplicates { SendMessage 0xB1, c, c, %Ctrl%, ahk_id %Win% ; SetSel set insertion point SendMessage 0xC2, 1, &T, %Ctrl%, ahk_id %Win% ; ReplaceSel = insert T Return } SendMessage 0xC2, 0, "", %Ctrl%, ahk_id %Win% ; ReplaceSel = delete selection IfLess c,%c1%, SendMessage 0xB1, c, c, %Ctrl%, ahk_id %Win% ; SetSel mouse -> insertion point Else SendMessage 0xB1,c+c1-c2,c+c1-c2, %Ctrl% ,ahk_id %Win% ; SetSel insertion point moved left SendMessage 0xC2, 1, &T, %Ctrl%, ahk_id %Win% ; ReplaceSel = insert T Return Char(x,y,Ctrl,Win) { ; Char# of pixel (x,y) SendMessage 0xD7,,% y<<16|x,%Ctrl%,ahk_id %Win% ; CharFromPos c = %ErrorLevel% ; line# || char# (16,16-bit) SendMessage 0xBB,ErrorLevel>>16,,%Ctrl%,ahk_id %Win% ; LineIndex Return Errorlevel + ((c-Errorlevel) & 0xFFFF) ; Char# > 16-bit }To Do:
1. Auto-scroll at the edges when dragging
2. Showing actual insertion point with special pointer
3. Noch mehr Wünsche?
EM_CHARFROMPOS 0xD7 wParam: not used. lParam: The low/high-order word contains the horizontal/vertical coordinate. Relative to the upper-left corner of the control's client area. --> : The low-order word = zero-based index of the character nearest the specified point. Relative to the beginning of the control. If the point is beyond the last visible character in a line: the line delimiter. The high-order word = zero-based index of the line that contains the character. If the specified point is beyond the last character: the last line in the control. EM_GETFIRSTVISIBLELINE 0xCE wParam: 0 lParam: 0 --> : Zero-based index of the uppermost visible line in a multiline edit control EM_GETSEL 0xB0 wParam: Pointer to buffer that receives the starting position of the selection or NULL. lParam: Pointer to buffer <-- the first nonselected char after the end of selection or NULL. --> : Low-order word = Zero-based value with the starting position of the selection High-order word = position of first character after the last selected character. If either of these values exceeds 65,535, the return value is -1. (Better to use values returned in wParam and lParam because they are full 32-bit values.) EM_LINEINDEX 0xBB wParam: Zero-based line number. -1: the current line number (with the caret). lParam: not used. --> : Index of the first char of line specified in the wParam parameter -1 if line number is greater than the number of lines. EM_LINESCROLL 0xB6 wParam: The number of characters to scroll horizontally. lParam: The number of lines to scroll vertically. --> : If the message is sent to a multiline edit control, TRUE, else FALSE EM_POSFROMCHAR 0xD6 wParam: The zero-based index of the character. lParam: not used. --> : CLIENT area coordinates of the character. Low/High-order word: horizontal/vertical coordinate Negative if the specified character is not displayed in the edit control's client area. If the character is line delimiter, the coordinates indicate a point beyond the last visible char If the specified index > index of the last char in the control: -1. EM_REPLACESEL 0xC2 wParam: Specifies whether the replacement operation can be undone (TRUE) or not (FALSE). lParam: Pointer to a null-terminated string containing the replacement text. (If there is no selection, the replacement text is inserted at the current location of the caret.) EM_SETSEL 0xB1 wParam: Specifies the Starting character position of the selection. lParam: Specifies the Ending character position of the selection. (start <,> end OK) (Displays flashing caret at the End position regardless of the relative values of Start and End) EM_SETTABSTOPS 0xCB wParam: Number of tab stops contained in the array. 0: lParam parameter is ignored, default tab stops are set at every 32 dialog template units. 1: tab stops are set at every n dialog template units, n is the distance pointed to by lParam. >1: lParam is a pointer to an array of tab stops. lParam: Pointer to array of unsigned integers specifying the tab stops, in dialog template units. If wParam = 1: lParam is a pointer to unsigned integer = distance between all tab stops. --> : TRUE/FALSE If all the tabs are set, or not.I collected them from MSDN.
We deal with normal edit controls, and the issue is not how much text we can enter, but how to find the index of the character nearest to the mouse pointer. The EM_CHARFROMPOS message returns only a 16-bit index, which makes the first script fail at longer than 64K char texts. The trick that works is to get the line index of this character, which is also 16 bits. Another message finds the 32-bit index of the first character of this line, which allows the computation of longer than 16-bit character indices.The EM_EXLIMITTEXT message sets an upper limit to the amount of text the user can type or paste into a rich edit control.
Normal Edit controls are limited to 64K by default. I haven't tested the limit on an AutoHotkey Edit control but if Chris didn't extend it then I would be surprised if it accepted any input over 64K (pasted or manually entered). The EM_EXLIMITTEXT message can also be used to extend a normal Edit control. At least I'm pretty sure I've used it on an Edit control (maybe I'm mistaken). If it doesn't work then you could try a EM_LIMITTEXT Message instead .We deal with normal edit controls, and the issue is not how much text we can enter, but how to find the index of the character nearest to the mouse pointer.
I haven't in a while. I had previously run into this issue when putting together an Editor using another language. IIRC the default limit may only apply to Win9.x systems (not sure about Win2K).The size of edit controls might be OS specific. In my XP SP2, a GUI edit control does not have the 64K char limit. Nor does Notepad. EM_EXLIMITTEXT might be necessary for older Windows versions, I don't know. Have you tried?
P.S. - Added a link for EM_LIMITTEXT above for Edit controls
Edit: The link gives the limits for edit controls in different versions of Windows near the bottom
- If you drag beyond the edges of the edit control, it scrolls vertically or horizontally (even diagonally)
- During drag the selection flashes
- The actual insertion point in text is shown with a flashing caret (vertical line). The mouse pointer can be at a different place, the caret is the nearest valid position in the text.
- At insertion the top line in the control remains the same. Otherwise, if there was some scrolling, the line with the insertion point gets to the top or the bottom.
- The text dragged to a new position gets selected. This way you see the result better, and if you missed the right insertion point, you can just continue dragging.
#SingleInstance force #NoEnv Process Priority,,High SetBatchLines -1 #UseHook ; No need for $ in hotkeys VarSetCapacity(A1,4) VarSetCapacity(A2,4) ; Room for 32-bit Integers ^LButton:: LButton:: MouseGetPos mX, mY, Win,Ctrl ; Info about pointer location ControlGetPos cX, cY,cW,cH, %Ctrl%, ahk_id %Win% ; Top-left corner, Width, Height of Ctrl c := Char#(mX-cX, mY-cY, Ctrl, Win) ; Char# nearest to mouse SendMessage 0xB0, &A1, &A2, %Ctrl%, ahk_id %Win% ; GetSel c1 := *(&A1)+(*(&A1+1)<<8)+(*(&A1+2)<<16)+(*(&A1+3)<<24) ; Selection start c2 := *(&A2)+(*(&A2+1)<<8)+(*(&A2+2)<<16)+(*(&A2+3)<<24) ; Selection end If (InStr(Ctrl,"Edit") <> 1 or c < c1 or c2 <= c) ; Clicked not in Edit, or in selection SendInput {LButton Down} Else SetTimer Cursor, 100 ; Show insertion point Return ~^LButton Up:: ~LButton Up:: SetTimer Cursor, OFF ; Stop flashing selection, insertion point If (InStr(Ctrl,"Edit") <> 1 or c < c1 or c2 <= c) ; Clicked not in Edit, or in selection Return MouseGetPos mX, mY ; Pointer location c := Char#(mX-cX, mY-cY, Ctrl, Win) ; Char# nearest to mouse If (c1 <= c and c < c2) ; Pointer still in selection Return SendMessage 0xCE, 0, 0, %Ctrl%, ahk_id %Win% ; GetFirstVisibleLine Top of Ctrl tp = %ErrorLevel% SendMessage 0xB1, c1, c2, %Ctrl%, ahk_id %Win% ; SetSel re-select ControlGet T, Selected, , %Ctrl%, ahk_id %Win% ; Save selected text If A_ThisHotKey = ~LButton Up ; Drag moves, Ctrl-Drag duplicates SendMessage 0xC2, 0,"", %Ctrl%, ahk_id %Win% ; ReplaceSel = delete selection If (A_ThisHotKey = "~LButton Up" and c1 < c) ; Deleted text moves insertion point c := c+c1-c2 SendMessage 0xB1, c, c, %Ctrl%, ahk_id %Win% ; SetSel mouse -> insertion point SendMessage 0xC2, 1, &T, %Ctrl%, ahk_id %Win% ; ReplaceSel = insert T SendMessage 0xB1,c,c+c2-c1,%Ctrl%, ahk_id %Win% ; SetSel select moved text SendMessage 0xCE, 0, 0, %Ctrl%, ahk_id %Win% ; GetFirstVisibleLine New-top SendMessage 0xB6,0,tp-ErrorLevel,%Ctrl%,ahk_id %Win% ; Scroll to restore line positions Return Cursor: ; Flashing selection + caret MouseGetPos mX, mY ; Pointer location v := (mY-cY > cH) - (mY-cY < 0) ; Up/Down: -1/1 h := (mX-cX > cW) - (mX-cX < 0) ; Left/Right: -1/1 SendMessage 0xB6, h, v, %Ctrl%, ahk_id %Win% ; Scroll curs := !curs ; Flash If curs SendMessage 0xB1,c1,c2, %Ctrl%, ahk_id %Win% ; SetSel original selection Else { d := Char#(mX-cX, mY-cY, Ctrl, Win) ; Char# nearest to mouse SendMessage 0xB1, d, d, %Ctrl%, ahk_id %Win% ; SetSel mouse -> current insert point } Return Char#(x,y,Ctrl,Win) { ; Char# of pixel (x,y) SendMessage 0xD7,,% y<<16|x,%Ctrl%,ahk_id %Win% ; CharFromPos c = %ErrorLevel% ; line# || char# (16,16-bit) SendMessage 0xBB,ErrorLevel>>16,,%Ctrl%,ahk_id %Win% ; LineIndex Return Errorlevel + ((c-Errorlevel) & 0xFFFF) ; Up to 32-bit Char# }If you like to change the mouse pointer or the caret while dragging, look here. I did not find it necessary, because the flashing selection informs about the dragging, but it is of personal preference.
This text dragging works fine, but being done with an interpreted script, it is not as smooth as dragging in some other controls. If someone turns the script into a dll, it might speed things up, but then we could just use another control supporting drag'n drop, with the same effort.
For testing open the script in Notepad, select everything (Ctrl-A) and Ctrl-Drag it to the end. If you repeat this a few times, you get long text to play with. You could even see what happens if you reach the limit of 64K lines.
- Some applications (e.g. MS Word) crash, when EM messages are sent, which coincide with their own messages. Therefore, the hotkey returns immediately, if the active control is not named Edit*.
- AHK seems to have some problems with ~LButton Up hotkeys. As a workaround, the script below explicitly sends {LButton Up}, with the modifier keys (Ctrl).
- The flashing frequency of the selection is now 2 a second, while 10 scroll messages are sent a second, when needed.
#SingleInstance force #NoEnv Process Priority,,High SetBatchLines -1 #UseHook ; Mouse Hook, no $ in hotkeys VarSetCapacity(A1,4) VarSetCapacity(A2,4) ; Room for 32-bit Integers ^LButton:: ; Duplicate-drag LButton:: ; Move-drag HOTKEYS MouseGetPos mX, mY, Win,Ctrl ; Info about pointer location If (InStr(Ctrl,"Edit") <> 1) { ; Clicked not in Edit control Send {Blind}{LButton Down} ; Standard mouse action, ^ is kept Return ; No drag } ControlGetPos cX, cY,cW,cH, %Ctrl%, ahk_id %Win% ; Top-left corner, Width, Height of Ctrl c := Char#(mX-cX, mY-cY, Ctrl, Win) ; Char# nearest to mouse SendMessage 0xB0, &A1, &A2, %Ctrl%, ahk_id %Win% ; GetSel c1 := *(&A1)+(*(&A1+1)<<8)+(*(&A1+2)<<16)+(*(&A1+3)<<24) ; Selection start c2 := *(&A2)+(*(&A2+1)<<8)+(*(&A2+2)<<16)+(*(&A2+3)<<24) ; Selection end If (c < c1 or c2 <= c) ; Clicked outside of selection Send {Blind}{LButton Down} ; Standard mouse action, ^ is kept Else SetTimer Cursor, 100 ; Flash selection, Show insertion point Return ^LButton Up:: ; Selection moved at releasing button LButton Up:: SetTimer Cursor, OFF ; Stop flashing selection, insertion point Send {Blind}{LButton Up} ; Standard act, ^ kept (~LButton has bug) If (InStr(Ctrl,"Edit") <> 1 or c < c1 or c2 <= c) ; Clicked not in Edit, or in selection Return MouseGetPos mX, mY ; Pointer location c := Char#(mX-cX, mY-cY, Ctrl, Win) ; Char# nearest to mouse If (c1 <= c and c < c2) ; Pointer still in selection Return SendMessage 0xCE, 0, 0, %Ctrl%, ahk_id %Win% ; GetFirstVisibleLine Top of Ctrl tp = %ErrorLevel% SendMessage 0xB1, c1, c2, %Ctrl%, ahk_id %Win% ; SetSel re-select ControlGet T, Selected, , %Ctrl%, ahk_id %Win% ; Save selected text If A_ThisHotKey = LButton Up ; Drag moves, Ctrl-Drag duplicates SendMessage 0xC2, 0,"", %Ctrl%, ahk_id %Win% ; ReplaceSel = delete selection If (A_ThisHotKey = "LButton Up" and c1 < c) ; Deleted text moves insertion point c := c+c1-c2 SendMessage 0xB1, c, c, %Ctrl%, ahk_id %Win% ; SetSel mouse -> insertion point SendMessage 0xC2, 1, &T, %Ctrl%, ahk_id %Win% ; ReplaceSel = insert T SendMessage 0xB1,c,c+c2-c1,%Ctrl%, ahk_id %Win% ; SetSel select moved text SendMessage 0xCE, 0, 0, %Ctrl%, ahk_id %Win% ; GetFirstVisibleLine New-top SendMessage 0xB6,0,tp-ErrorLevel,%Ctrl%,ahk_id %Win% ; Scroll to restore line positions Return Cursor: ; Flashing selection + caret MouseGetPos mX, mY ; Pointer location v := (mY-cY > cH) - (mY-cY < 0) ; Up/Down: -1/1 h := (mX-cX > cW) - (mX-cX < 0) ; Left/Right: -1/1 SendMessage 0xB6, h, v, %Ctrl%, ahk_id %Win% ; Scroll If A_TickCount & 256 ; Flash SendMessage 0xB1,c1,c2, %Ctrl%, ahk_id %Win% ; SetSel original selection Else { d := Char#(mX-cX, mY-cY, Ctrl, Win) ; Char# nearest to mouse SendMessage 0xB1, d, d, %Ctrl%, ahk_id %Win% ; SetSel mouse -> current insert point } Return Char#(x,y,Ctrl,Win) { ; Char# nearest to pixel (x,y) SendMessage 0xD7,,% y<<16|x,%Ctrl%,ahk_id %Win% ; CharFromPos c = %ErrorLevel% ; line# || char# (16,16-bit) SendMessage 0xBB,ErrorLevel>>16,,%Ctrl%,ahk_id %Win% ; LineIndex Return Errorlevel + ((c-Errorlevel) & 0xFFFF) ; Up to 32-bit Char# }