From 74b401f3896c8270fbd3cd65c27976bbffb29b7f Mon Sep 17 00:00:00 2001 From: jstenar <> Date: Sat, 21 Jan 2006 19:36:28 +0000 Subject: [PATCH] pyreadline: Patches for clipboard and some key-bindings. Changed version in setup.py to 1.13-svn. --- doc/ChangeLog | 25 ++++++- readline/PyReadline.py | 58 +++++++++++++++- readline/clipboard.py | 154 +++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 4 files changed, 235 insertions(+), 4 deletions(-) create mode 100644 readline/clipboard.py diff --git a/doc/ChangeLog b/doc/ChangeLog index 08aba64..2efcf90 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,27 @@ +2006-01-21 Jörgen Stenarson + + * Added clipboard functionality. ctypes clipboard code + borrowed from example code posted to ctypes-users. See + http://aspn.activestate.com/ASPN/Mail/Message/ctypes-users/1771866 + * Control-Shift-v moved quoted insert from Control-v + * Control-v straight paste from clipboard + * Alt-v ipython_paste. Does some preprocessing of data. If + clipboard text is shorter than 300 characters and has no + newlines and tabs, then assume it is a path and change all + \ to / and then add \ before all spaces. This means you can + paste paths and use them in magic command %cd. If text is + multiline it assumes tabseparated data should be list of + list, if all is numeric assume it should be an array. + * Control-y yank, alias for Control-v. + * Control-k kill line, move text from cursor to end of line + into clipboard. Should be move to kill buffer but the kill + buffer is not implemented. + * Added kill line and yank functionality. As well as mark and + copy-region-to-clipboard. + * Control-m set-mark sets the copy region mark + * Control-q copies region between mark and cursor to clipboard + * Changed version in setup.py to 1.13-svn + 2006-01-21 Jörgen Stenarson * Patch to get swedish characters working when python @@ -11,4 +35,3 @@ * Changed all python files to conform to 4 space indent. * Added changelog * Added os.path.expanduser to expand out ~/.history paths - \ No newline at end of file diff --git a/readline/PyReadline.py b/readline/PyReadline.py index 569b260..49fd08f 100644 --- a/readline/PyReadline.py +++ b/readline/PyReadline.py @@ -1,3 +1,4 @@ +# -*- coding: ISO-8859-1 -*- ''' an attempt to implement readline for Python in Python using ctypes''' import string @@ -15,6 +16,19 @@ import Console from Console import log from keysyms import key_text_to_keyinfo,printable_chars_in_codepage +import clipboard +import ctypes + +enable_win32_clipboard=True + +#assumes data on clipboard is path if shorter than 300 characters and doesn't contain \t or \n +#and replace \ with / for easier use in ipython +enable_ipython_paste_for_paths=True + +#automatically convert tabseparated data to list of lists or array constructors +enable_ipython_paste_list_of_lists=True + + def quote_char(c): if c in printable_chars_in_codepage: return c @@ -53,6 +67,7 @@ class Readline: self.show_all_if_ambiguous = 'off' self.mark_directories = 'on' self.bell_style = 'none' + self.mark=-1 def _bell(self): '''ring the bell if requested.''' @@ -669,6 +684,9 @@ class Readline: def kill_line(self, e): # (C-k) '''Kill the text from point to the end of the line. ''' + if enable_win32_clipboard: + toclipboard="".join(self.line_buffer[self.line_cursor:]) + clipboard.set_clipboard_text(toclipboard) self.line_buffer[self.line_cursor:] = [] def backward_kill_line(self, e): # (C-x Rubout) @@ -726,6 +744,18 @@ class Readline: yanked right away. By default, this command is unbound.''' pass + def copy_region_to_clipboard(self, e): # () + '''Copy the text in the region to the windows clipboard.''' + if enable_win32_clipboard: + mark=min(self.mark,len(self.line_buffer)) + cursor=min(self.line_cursor,len(self.line_buffer)) + if self.mark==-1: + return + begin=min(cursor,mark) + end=max(cursor,mark) + toclipboard="".join(self.line_buffer[begin:end]) + clipboard.SetClipboardText(str(toclipboard)) + def copy_backward_word(self, e): # () '''Copy the word before point to the kill buffer. The word boundaries are the same as backward-word. By default, this command @@ -738,6 +768,24 @@ class Readline: unbound.''' pass + def paste(self,e): + '''Paste windows clipboard''' + if enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert(False) + self.insert_text(txt) + + def ipython_paste(self,e): + '''Paste windows clipboard. If enable_ipython_paste_list_of_lists is + True then try to convert tabseparated data to repr of list of lists or + repr of array''' + if enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert( + enable_ipython_paste_list_of_lists) + if enable_ipython_paste_for_paths: + if len(txt)<300 and ("\t" not in txt) and ("\n" not in txt): + txt=txt.replace("\\","/").replace(" ",r"\ ") + self.insert_text(txt) + def yank(self, e): # (C-y) '''Yank the top of the kill ring into the buffer at point. ''' pass @@ -948,7 +996,7 @@ class Readline: def set_mark(self, e): # (C-@) '''Set the mark to the point. If a numeric argument is supplied, the mark is set to that position.''' - pass + self.mark=self.line_cursor def exchange_point_and_mark(self, e): # (C-x C-x) '''Swap the point with the mark. The current cursor position is set @@ -1048,7 +1096,13 @@ class Readline: self._bind_key('Meta-d', self.kill_word) self._bind_key('Meta-Delete', self.backward_kill_word) self._bind_key('Control-w', self.unix_word_rubout) - self._bind_key('Control-v', self.quoted_insert) + self._bind_key('Control-Shift-v', self.quoted_insert) + self._bind_key('Control-v', self.paste) + self._bind_key('Alt-v', self.ipython_paste) + self._bind_key('Control-y', self.paste) + self._bind_key('Control-k', self.kill_line) + self._bind_key('Control-m', self.set_mark) + self._bind_key('Control-q', self.copy_region_to_clipboard) # Add keybindings for numpad # first the number keys diff --git a/readline/clipboard.py b/readline/clipboard.py new file mode 100644 index 0000000..dcf5cb4 --- /dev/null +++ b/readline/clipboard.py @@ -0,0 +1,154 @@ +################################### +# +# Based on recipe posted to ctypes-users +# see archive +# http://aspn.activestate.com/ASPN/Mail/Message/ctypes-users/1771866 +# +# + + +################################################################################### +# +# The Python win32clipboard lib functions work well enough ... except that they +# can only cut and paste items from within one application, not across +# applications or processes. +# +# I've written a number of Python text filters I like to run on the contents of +# the clipboard so I need to call the Windows clipboard API with global memory +# for my filters to work properly. +# +# Here's some sample code solving this problem using ctypes. +# +# This is my first work with ctypes. It's powerful stuff, but passing arguments +# in and out of functions is tricky. More sample code would have been helpful, +# hence this contribution. +# +################################################################################### + +from ctypes import * +from win32con import CF_TEXT, GHND + +OpenClipboard = windll.user32.OpenClipboard +EmptyClipboard = windll.user32.EmptyClipboard +GetClipboardData = windll.user32.GetClipboardData +GetClipboardFormatName = windll.user32.GetClipboardFormatNameA +SetClipboardData = windll.user32.SetClipboardData +EnumClipboardFormats = windll.user32.EnumClipboardFormats +CloseClipboard = windll.user32.CloseClipboard +OpenClipboard.argtypes=[c_int] +EnumClipboardFormats.argtypes=[c_int] +CloseClipboard.argtypes=[] +GetClipboardFormatName.argtypes=[c_uint,c_char_p,c_int] +GetClipboardData.argtypes=[c_int] +SetClipboardData.argtypes=[c_int,c_int] + +GlobalLock = windll.kernel32.GlobalLock +GlobalAlloc = windll.kernel32.GlobalAlloc +GlobalUnlock = windll.kernel32.GlobalUnlock +GlobalLock.argtypes=[c_int] +GlobalUnlock.argtypes=[c_int] +memcpy = cdll.msvcrt.memcpy + +def enum(): + OpenClipboard(0) + q=EnumClipboardFormats(0) + while q: + print q, + q=EnumClipboardFormats(q) + CloseClipboard() + +def getformatname(format): + buffer = c_buffer(" "*100) + bufferSize = sizeof(buffer) + OpenClipboard(0) + GetClipboardFormatName(format,buffer,bufferSize) + CloseClipboard() + return buffer.value + +def GetClipboardText(): + text = "" + if OpenClipboard(0): + hClipMem = GetClipboardData(CF_TEXT) + if hClipMem: + GlobalLock.restype = c_char_p + text = GlobalLock(hClipMem) + GlobalUnlock(hClipMem) + CloseClipboard() + return text + +def make_tab(lists): + if hasattr(lists,"tolist"): + lists=lists.tolist() + ut=[] + for rad in lists: + if type(rad) in [list,tuple]: + ut.append("\t".join(["%s"%x for x in rad])) + else: + ut.append("%s"%rad) + return "\n".join(ut) + +def send_data(lists): + SetClipboardText(make_tab(lists)) + +def SetClipboardText(text): + buffer = c_buffer(text) + bufferSize = sizeof(buffer) + hGlobalMem = GlobalAlloc(c_int(GHND), c_int(bufferSize)) + GlobalLock.restype = c_void_p + lpGlobalMem = GlobalLock(c_int(hGlobalMem)) + memcpy(lpGlobalMem, addressof(buffer), c_int(bufferSize)) + GlobalUnlock(c_int(hGlobalMem)) + if OpenClipboard(0): + EmptyClipboard() + SetClipboardData(c_int(CF_TEXT), c_int(hGlobalMem)) + CloseClipboard() + + +def set_clipboard_text(toclipboard): + SetClipboardText(str(toclipboard)) + +def make_list_of_list(txt): + def make_num(x): + try: + return int(x) + except ValueError: + try: + return float(x) + except ValueError: + try: + return complex(x) + except ValueError: + return x + return x + ut=[] + flag=False + for rad in [x for x in txt.split("\r\n") if x!=""]: + raden=[make_num(x) for x in rad.split("\t")] + if str in map(type,raden): + flag=True + ut.append(raden) + return ut,flag + + +def get_clipboard_text_and_convert(paste_list=False): + """Get txt from clipboard. if paste_list==True the convert tab separated + data to list of lists. Enclose list of list in array() if all elements are + numeric""" + txt=GetClipboardText() + if txt: + if paste_list and "\t" in txt: + array,flag=make_list_of_list(txt) + if flag: + txt=repr(array) + else: + txt="array(%s)"%repr(array) + txt="".join([c for c in txt if c not in " \t\r\n"]) + return txt + +if __name__ == '__main__': + txt=GetClipboardText() # display last text clipped + print txt + + + + \ No newline at end of file diff --git a/setup.py b/setup.py index 4c75912..8694b89 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from distutils.core import setup setup(name="readline", - version="1.12", + version="1.13-svn", description="Python implementation of GNU readline", author="Gary Bishop", author_email="gb@cs.unc.edu",