Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Linux-like copy/paste with mouse


  • Please log in to reply
19 replies to this topic
Serenity
  • Members
  • 1271 posts
  • Last active:
  • Joined: 07 Nov 2004
This script emulates the copy selected text to clipboard behaviour common to Linux. An optional paste with middle mouse button is included in the script. Unfortunately certain types of edit controls do not return the selected text with ControlGet. Feel free to tweak this - the mouse move detection routine isn't perfect.

SetKeyDelay, -1
CoordMode, Mouse, Screen

~LButton::

WinGetClass, class, A
MouseGetPos, mx, my, , control, 1
x1 = %mx%
y1 = %my%

loop
{
  GetKeyState, keystate, LButton
  if keystate = U
  {
    MouseGetPos, mx, my
    x2 = %mx%
    y2 = %my%
    break
  }
}

x := (x2 - x1)
y := (y2 - y1)

if (x >= 6)
  gosub, get
if (y >= 6)
  gosub, get
if (x <= -6)
  gosub, get
if (y <= -6)
  gosub, get
return

get:
ControlGet, text, Selected, , %control%, ahk_class %class%
if text != 
{
  clipboard = %text%

;--------------------------------
; remove this line if you prefer selected text to stay highlighted:
  controlsend, %control%, {left}{right}, ahk_class %class% 
;--------------------------------

/*
  tooltip, you selected:`n%text%`nfrom: %class%`, %control% 
  sleep, 1000
  tooltip
*/
  return
}
return

~Mbutton::
Send, %text%
return

"Anything worth doing is worth doing slowly." - Mae West
Posted Image

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Nice idea, Serenity!

Very few controls tell if they have selected texts. Many more react to Ctrl-C, copying (any) selection to the clipboard directly. Here is another version of your script using Ctrl-C:
CoordMode Mouse, Screen

~LButton::
   MouseGetPos x0, y0            ; save start mouse position
   Loop
   {
     Sleep 20                    ; yield time to others
     GetKeyState keystate, LButton
     IfEqual keystate, U, {
       MouseGetPos x, y          ; position when button released
       break
     }
   }
   if (x-x0 > 5 or x-x0 < -5 or y-y0 > 5 or y-y0 < -5)
   {                             ; mouse has moved
      clip0 := ClipBoardAll      ; save old clipboard
      ClipBoard =
      Send ^c                    ; selection -> clipboard
      ClipWait 1, 1              ; restore clipboard if no data
      IfEqual ClipBoard,, SetEnv ClipBoard, %clip0%
   }
return


Serenity
  • Members
  • 1271 posts
  • Last active:
  • Joined: 07 Nov 2004
Thanks Laszlo. :) Your version works much better with Ctrl+C, and I didn't know you could do expressions like this:

if (x-x0 > 5 or x-x0 < -5 or y-y0 > 5 or y-y0 < -5)

I get this error message from time to time although I am unsure what triggers it:

Error: GetClipboardData

Line#
013: }
014: if (x-x0 > 5 or x-x0 < -5 or y-y0 > 5 or y-y0 < -5)
015: {
016: clip0 = ClipBoardAll
017: ClipBoard =
018: Send,^c
019: ClipWait,1,1
---> 020: if ClipBoard =
020: ClipBoard = %clip0%
021: }
022: Return
025: Send,^v
026: Return
027: Exit
028: Exit


"Anything worth doing is worth doing slowly." - Mae West
Posted Image

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004

I get this error message from time to time although I am unsure what triggers it:

Error: GetClipboardData
---> 020: if ClipBoard =

That happens when the internal call to GetClipboardData() fails to retrieve the plain text or list of copied files that resides on the clipboard. If you find out any more about the conditions that trigger this error, please let me know. Also, if anyone else gets this error, it would help to know the circumstances.

Thanks.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
This GetClipboardData error is strange. I have not seen it in my Win2k laptop. (Everyone: please try to reproduce it!) Serenity, have you tried increasing the ClipWait time? Maybe your PC is slow and the clipboard was still busy when AHK wanted to check it.

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
It's a very unexpected error because at that stage, the clipboard is already opened and fully owned by the script. I've never heard of GetClipboardData() failing under these conditions, especially since it's called with CF_TEXT (plain text) or CF_HDROP (list of copied files), which are very mainstream.

However, I have seen GetClipboardData() fail for ClipboardAll, and for this reason ClipboardAll ignores the error and simply tries to get as much data as it can.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
...just thinkin...
Maybe the clipboard still changes after ClipWait saw data there, and accessing it causes errors. You could try
ClipWait 1, 1
clip2 := ClipBoard
IfEqual clip2,, SetEnv ClipBoard, %clip0%
If it does not help, we can try waiting until the clipboard does not change for a while.
clip0 := ClipBoardAll      ; save old clipboard
   ClipBoard =
   Send ^c                    ; selection -> clipboard
   ClipWait 1, 1              ; wait for some data
   clip1 := ClipBoard         ; current state of clipboard
   Loop
   {
      Sleep 20
      clip2 := ClipBoard
      IfEqual clip1,%clip2%, break ; exit if no change for 20 ms
      clip1 := ClipBoard      ; current state of clipboard
   }


Tekl
  • Members
  • 814 posts
  • Last active: May 03 2009 03:28 PM
  • Joined: 24 Sep 2004
Hi,

I can reproduce the Error with the following Hotkey:

#q::
   Send, ^c
   ClipWait, 0.5, 1
   msgbox %Clipboard%
Return
Now if I've opened an archive in WinRAR and no file is selected (clicking on white space) this hotkey gives me that error.

Tekl

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Thanks for the script; it produces the same error for me.

The next update will contain the following change:
Fixed the Clipboard variable to avoid producing an error when a set of zero files was copied onto the clipboard. [thanks Serenity & Tekl]

MrNomer
  • Members
  • 1 posts
  • Last active: Apr 16 2005 09:07 PM
  • Joined: 16 Apr 2005
I'm a newbie here but thought I'd add a couple cents...

couldn't the expression

if (x-x0 > 5 or x-x0 < -5 or y-y0 > 5 or y-y0 < -5)

be reduced to
if sqr((x-x0)^2+(y-y0)^2) > 5

and eliminate several logical OR's?

Sorry ... so new .. I'm not even sure if AHK has a square root function. LOL

Thanks to all for sharing code. Looking forward to learning this.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Assuming you defined your own square root function, and replace ^ with ** (the power operator in AHK) it should work. (But it does not, because negative base is not allowed, and so the result is wrong.) In any case, your condition is different. The original one allows the mouse to move in a little square without the selection copied to the clipboard, you specify a circle. But it doesn't really matter.

If you care about fast execution, function calls, multiplications are usually slower than a series of comparisons. Especially, with short circuits (not calculating further when the overall result becomes determined from a few conditions). Much depend on the compiler, though. Stupid compilation of conditionals might flush the processor pipeline unnecessarily, causing wait states.

utch
  • Members
  • 1 posts
  • Last active: Mar 03 2008 10:56 PM
  • Joined: 06 Feb 2008
CoordMode Mouse, Screen

~LButton::
   MouseGetPos x0, y0            ; save start mouse position
   Loop
   {
     Sleep 20                    ; yield time to others
     GetKeyState keystate, LButton
     IfEqual keystate, U, {
       MouseGetPos x, y          ; position when button released
       break
     }
   }
   if (x-x0 > 5 or x-x0 < -5 or y-y0 > 5 or y-y0 < -5)
   {                             ; mouse has moved
      clip0 := ClipBoardAll      ; save old clipboard
      ClipBoard =
      Send ^c                    ; selection -> clipboard
      ClipWait 1, 1              ; restore clipboard if no data
      IfEqual ClipBoard,, SetEnv ClipBoard, %clip0%
   }
return

~Mbutton::
Send, ^v						  ;Paste via middle mouse click
return
On an even cooler note, I pasted the code above using this script!

JazzSinger
  • Members
  • 1 posts
  • Last active: Feb 07 2008 12:36 AM
  • Joined: 07 Feb 2008
Utch: great script. But when I select text by double-clicking, it doesn't work the same way as when I select by click-and-drag. Any way to fix that?

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

great script

Thx

when I select text by double-clicking, it doesn't work the same way as when I select by click-and-drag

It was discussed a few times in the Forum. Windows handles the mouse differently, so there is no perfect Linux emulation possible. What you need is to detect when a new selection is made, but it is application dependent. Often double clicks select words, triple clicks select sentences or lines, quadruple clicks select paragraphs… In other applications triple clicks deselect the last selected words, so you don’t know if a click selected something new by just monitoring the mouse. Also, if you drag something around it may stay selected, or may not.

A possible solution could be to send Ctrl-C always when the mouse button is released. If the clipboard becomes empty, restore its previous content. However, at dragging you might not want to change the clipboard, but the script cannot tell. There are speed and memory problems, too. If you select many directories to drag somewhere, Ctrl-C might need a lot of time to update the clipboard. During the wait time the system may look frozen.

airjrdn
  • Members
  • 37 posts
  • Last active: Jan 03 2013 07:33 PM
  • Joined: 25 Feb 2008
I took a couple of scripts and pieced them together that seems to handle the double click. Here's the code:
CoordMode Mouse, Screen

~LButton::
If A_PriorHotkey = %A_ThisHotkey%
{
   If A_TimeSincePriorHotkey < 300 ; 300 ms to double click, adjust if needed
   {
      clip0 := ClipBoardAll      ; save old clipboard
      ClipBoard =
      Send ^c                    ; selection -> clipboard
      ClipWait 1, 1              ; restore clipboard if no data
      IfEqual ClipBoard,, SetEnv ClipBoard, %clip0%
   }
   else
   {
      MouseGetPos x0, y0            ; save start mouse position
      Loop
      {
        Sleep 20                    ; yield time to others
        GetKeyState keystate, LButton
        IfEqual keystate, U, {
          MouseGetPos x, y          ; position when button released
          break
        }
         }
      if (x-x0 > 5 or x-x0 < -5 or y-y0 > 5 or y-y0 < -5)
      {                             ; mouse has moved
         clip0 := ClipBoardAll      ; save old clipboard
         ClipBoard =
         Send ^c                    ; selection -> clipboard
         ClipWait 1, 1              ; restore clipboard if no data
         IfEqual ClipBoard,, SetEnv ClipBoard, %clip0%
      }
      return
   }
}

~Mbutton::
Send, ^v                    ;Paste via middle mouse click
return

I've found that I sometimes highlight the text I'm going to paste over though, which, in this case, replaces what's on the clipboard already. :) I don't know how Linux handles that, but it's something to get used to if you decided to use a script like this.