]> granicus.if.org Git - python/commitdiff
Issue #5124: Paste with selection should always replace.
authorTerry Jan Reedy <tjreedy@udel.edu>
Sat, 11 Jun 2016 06:06:18 +0000 (02:06 -0400)
committerTerry Jan Reedy <tjreedy@udel.edu>
Sat, 11 Jun 2016 06:06:18 +0000 (02:06 -0400)
This is how paste work on Windows, Mac, modern Linux apps, and ttk widgets.
The exception was X11 tk widgets.  Original patch by Serhiy Storchake.

Lib/idlelib/PyShell.py
Lib/idlelib/idle_test/test_editmenu.py [new file with mode: 0644]

index ea0d7de9ef99d44dfcec2326c3de364bff9fe922..3ea1a7d8c8dd8a2816493cfcfcc383361ca6e126 100755 (executable)
@@ -1408,6 +1408,17 @@ class PseudoInputFile(PseudoFile):
         self.shell.close()
 
 
+def fix_x11_paste(root):
+    "Make paste replace selection on x11.  See issue #5124."
+    if root._windowingsystem == 'x11':
+        for cls in 'Text', 'Entry', 'Spinbox':
+            root.bind_class(
+                cls,
+                '<<Paste>>',
+                'catch {%W delete sel.first sel.last}\n' +
+                        root.bind_class(cls, '<<Paste>>'))
+
+
 usage_msg = """\
 
 USAGE: idle  [-deins] [-t title] [file]*
@@ -1537,8 +1548,10 @@ def main():
                                     'editor-on-startup', type='bool')
     enable_edit = enable_edit or edit_start
     enable_shell = enable_shell or not enable_edit
+
     # start editor and/or shell windows:
     root = Tk(className="Idle")
+    root.withdraw()
 
     # set application icon
     icondir = os.path.join(os.path.dirname(__file__), 'Icons')
@@ -1553,7 +1566,7 @@ def main():
         root.tk.call('wm', 'iconphoto', str(root), "-default", *icons)
 
     fixwordbreaks(root)
-    root.withdraw()
+    fix_x11_paste(root)
     flist = PyShellFileList(root)
     macosxSupport.setupApp(root, flist)
 
diff --git a/Lib/idlelib/idle_test/test_editmenu.py b/Lib/idlelib/idle_test/test_editmenu.py
new file mode 100644 (file)
index 0000000..d352240
--- /dev/null
@@ -0,0 +1,98 @@
+'''Test (selected) IDLE Edit menu items.
+
+Edit modules have their own test files files
+'''
+from test.test_support import requires
+requires('gui')
+import Tkinter as tk
+import unittest
+from idlelib import PyShell
+
+class PasteTest(unittest.TestCase):
+    '''Test pasting into widgets that allow pasting.
+
+    On X11, replacing selections requires tk fix.
+    '''
+    @classmethod
+    def setUpClass(cls):
+        cls.root = root = tk.Tk()
+        PyShell.fix_x11_paste(root)
+        cls.text = tk.Text(root)
+        cls.entry = tk.Entry(root)
+        cls.spin = tk.Spinbox(root)
+        root.clipboard_clear()
+        root.clipboard_append('two')
+
+    @classmethod
+    def tearDownClass(cls):
+        del cls.text, cls.entry, cls.spin
+        cls.root.clipboard_clear()
+        cls.root.update_idletasks()
+        cls.root.update()
+        cls.root.destroy()
+        del cls.root
+
+    def test_paste_text_no_selection(self):
+        "Test pasting into text without a selection."
+        text = self.text
+        tag, ans = '', 'onetwo\n'
+        text.delete('1.0', 'end')
+        text.insert('1.0', 'one', tag)
+        text.event_generate('<<Paste>>')
+        self.assertEqual(text.get('1.0', 'end'), ans)
+
+    def test_paste_text_selection(self):
+        "Test pasting into text with a selection."
+        text = self.text
+        tag, ans = 'sel', 'two\n'
+        text.delete('1.0', 'end')
+        text.insert('1.0', 'one', tag)
+        text.event_generate('<<Paste>>')
+        self.assertEqual(text.get('1.0', 'end'), ans)
+
+    def test_paste_entry_no_selection(self):
+        "Test pasting into an entry without a selection."
+        # On 3.6, generated <<Paste>> fails without empty select range
+        # for 'no selection'.  Live widget works fine.
+        entry = self.entry
+        end, ans = 0, 'onetwo'
+        entry.delete(0, 'end')
+        entry.insert(0, 'one')
+        entry.select_range(0, end)  # see note
+        entry.event_generate('<<Paste>>')
+        self.assertEqual(entry.get(), ans)
+
+    def test_paste_entry_selection(self):
+        "Test pasting into an entry with a selection."
+        entry = self.entry
+        end, ans = 'end', 'two'
+        entry.delete(0, 'end')
+        entry.insert(0, 'one')
+        entry.select_range(0, end)
+        entry.event_generate('<<Paste>>')
+        self.assertEqual(entry.get(), ans)
+
+    def test_paste_spin_no_selection(self):
+        "Test pasting into a spinbox without a selection."
+        # See note above for entry.
+        spin = self.spin
+        end, ans = 0, 'onetwo'
+        spin.delete(0, 'end')
+        spin.insert(0, 'one')
+        spin.selection('range', 0, end)  # see note
+        spin.event_generate('<<Paste>>')
+        self.assertEqual(spin.get(), ans)
+
+    def test_paste_spin_selection(self):
+        "Test pasting into a spinbox with a selection."
+        spin = self.spin
+        end, ans = 'end', 'two'
+        spin.delete(0, 'end')
+        spin.insert(0, 'one')
+        spin.selection('range', 0, end)
+        spin.event_generate('<<Paste>>')
+        self.assertEqual(spin.get(), ans)
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)