kill_ring.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. """ A generic Emacs-style kill ring, as well as a Qt-specific version.
  2. """
  3. #-----------------------------------------------------------------------------
  4. # Imports
  5. #-----------------------------------------------------------------------------
  6. # System library imports
  7. from qtpy import QtCore, QtWidgets, QtGui
  8. #-----------------------------------------------------------------------------
  9. # Classes
  10. #-----------------------------------------------------------------------------
  11. class KillRing(object):
  12. """ A generic Emacs-style kill ring.
  13. """
  14. def __init__(self):
  15. self.clear()
  16. def clear(self):
  17. """ Clears the kill ring.
  18. """
  19. self._index = -1
  20. self._ring = []
  21. def kill(self, text):
  22. """ Adds some killed text to the ring.
  23. """
  24. self._ring.append(text)
  25. def yank(self):
  26. """ Yank back the most recently killed text.
  27. Returns
  28. -------
  29. A text string or None.
  30. """
  31. self._index = len(self._ring)
  32. return self.rotate()
  33. def rotate(self):
  34. """ Rotate the kill ring, then yank back the new top.
  35. Returns
  36. -------
  37. A text string or None.
  38. """
  39. self._index -= 1
  40. if self._index >= 0:
  41. return self._ring[self._index]
  42. return None
  43. class QtKillRing(QtCore.QObject):
  44. """ A kill ring attached to Q[Plain]TextEdit.
  45. """
  46. #--------------------------------------------------------------------------
  47. # QtKillRing interface
  48. #--------------------------------------------------------------------------
  49. def __init__(self, text_edit):
  50. """ Create a kill ring attached to the specified Qt text edit.
  51. """
  52. assert isinstance(text_edit, (QtWidgets.QTextEdit, QtWidgets.QPlainTextEdit))
  53. super(QtKillRing, self).__init__()
  54. self._ring = KillRing()
  55. self._prev_yank = None
  56. self._skip_cursor = False
  57. self._text_edit = text_edit
  58. text_edit.cursorPositionChanged.connect(self._cursor_position_changed)
  59. def clear(self):
  60. """ Clears the kill ring.
  61. """
  62. self._ring.clear()
  63. self._prev_yank = None
  64. def kill(self, text):
  65. """ Adds some killed text to the ring.
  66. """
  67. self._ring.kill(text)
  68. def kill_cursor(self, cursor):
  69. """ Kills the text selected by the give cursor.
  70. """
  71. text = cursor.selectedText()
  72. if text:
  73. cursor.removeSelectedText()
  74. self.kill(text)
  75. def yank(self):
  76. """ Yank back the most recently killed text.
  77. """
  78. text = self._ring.yank()
  79. if text:
  80. self._skip_cursor = True
  81. cursor = self._text_edit.textCursor()
  82. cursor.insertText(text)
  83. self._prev_yank = text
  84. def rotate(self):
  85. """ Rotate the kill ring, then yank back the new top.
  86. """
  87. if self._prev_yank:
  88. text = self._ring.rotate()
  89. if text:
  90. self._skip_cursor = True
  91. cursor = self._text_edit.textCursor()
  92. cursor.movePosition(QtGui.QTextCursor.Left,
  93. QtGui.QTextCursor.KeepAnchor,
  94. n = len(self._prev_yank))
  95. cursor.insertText(text)
  96. self._prev_yank = text
  97. #--------------------------------------------------------------------------
  98. # Protected interface
  99. #--------------------------------------------------------------------------
  100. #------ Signal handlers ----------------------------------------------------
  101. def _cursor_position_changed(self):
  102. if self._skip_cursor:
  103. self._skip_cursor = False
  104. else:
  105. self._prev_yank = None