QScintilla/SearchReplaceWidget.py

changeset 3011
18292228c724
parent 2965
d133c7edd88a
child 3017
6a8cb7632448
child 3057
10516539f238
equal deleted inserted replaced
3010:befeff46ec0f 3011:18292228c724
6 """ 6 """
7 Module implementing the search and replace widget. 7 Module implementing the search and replace widget.
8 """ 8 """
9 9
10 from PyQt4.QtCore import pyqtSignal, Qt, pyqtSlot 10 from PyQt4.QtCore import pyqtSignal, Qt, pyqtSlot
11 from PyQt4.QtGui import QWidget, QHBoxLayout, QToolButton, QScrollArea, QSizePolicy, \ 11 from PyQt4.QtGui import QWidget, QHBoxLayout, QToolButton, QScrollArea, \
12 QFrame 12 QSizePolicy, QFrame
13 13
14 from .Editor import Editor 14 from .Editor import Editor
15 15
16 from E5Gui.E5Action import E5Action 16 from E5Gui.E5Action import E5Action
17 from E5Gui import E5MessageBox 17 from E5Gui import E5MessageBox
53 self.replaceHistory = vm.getSRHistory('replace') 53 self.replaceHistory = vm.getSRHistory('replace')
54 self.ui = Ui_ReplaceWidget() 54 self.ui = Ui_ReplaceWidget()
55 whatsThis = self.trUtf8(r""" 55 whatsThis = self.trUtf8(r"""
56 <b>Find and Replace</b> 56 <b>Find and Replace</b>
57 <p>This dialog is used to find some text and replace it with another text. 57 <p>This dialog is used to find some text and replace it with another text.
58 By checking the various checkboxes, the search can be made more specific. The search 58 By checking the various checkboxes, the search can be made more specific.
59 string might be a regular expression. In a regular expression, special characters 59 The search string might be a regular expression. In a regular expression,
60 interpreted are:</p> 60 special characters interpreted are:</p>
61 """ 61 """
62 ) 62 )
63 else: 63 else:
64 from .Ui_SearchWidget import Ui_SearchWidget 64 from .Ui_SearchWidget import Ui_SearchWidget
65 self.ui = Ui_SearchWidget() 65 self.ui = Ui_SearchWidget()
66 whatsThis = self.trUtf8(r""" 66 whatsThis = self.trUtf8(r"""
67 <b>Find</b> 67 <b>Find</b>
68 <p>This dialog is used to find some text. By checking the various checkboxes, the search 68 <p>This dialog is used to find some text. By checking the various checkboxes,
69 can be made more specific. The search string might be a regular expression. In a regular 69 the search can be made more specific. The search string might be a regular
70 expression, special characters interpreted are:</p> 70 expression. In a regular expression, special characters interpreted are:</p>
71 """ 71 """
72 ) 72 )
73 self.ui.setupUi(self) 73 self.ui.setupUi(self)
74 if not replace: 74 if not replace:
75 self.ui.wrapCheckBox.setChecked(True) 75 self.ui.wrapCheckBox.setChecked(True)
76 76
77 whatsThis += self.trUtf8(r""" 77 whatsThis += self.trUtf8(r"""
78 <table border="0"> 78 <table border="0">
79 <tr><td><code>.</code></td><td>Matches any character</td></tr> 79 <tr><td><code>.</code></td><td>Matches any character</td></tr>
80 <tr><td><code>\(</code></td><td>This marks the start of a region for tagging a match.</td></tr> 80 <tr><td><code>\(</code></td><td>This marks the start of a region for tagging a
81 <tr><td><code>\)</code></td><td>This marks the end of a tagged region.</td></tr> 81 match.</td></tr>
82 <tr><td><code>\)</code></td><td>This marks the end of a tagged region.
83 </td></tr>
82 <tr><td><code>\\n</code></td> 84 <tr><td><code>\\n</code></td>
83 <td>Where <code>n</code> is 1 through 9 refers to the first through ninth tagged region 85 <td>Where <code>n</code> is 1 through 9 refers to the first through ninth
84 when replacing. For example, if the search string was <code>Fred\([1-9]\)XXX</code> and 86 tagged region when replacing. For example, if the search string was
85 the replace string was <code>Sam\1YYY</code>, when applied to <code>Fred2XXX</code> this 87 <code>Fred\([1-9]\)XXX</code> and the replace string was <code>Sam\1YYY</code>,
86 would generate <code>Sam2YYY</code>.</td></tr> 88 when applied to <code>Fred2XXX</code> this would generate <code>Sam2YYY</code>.
89 </td></tr>
87 <tr><td><code>\&lt;</code></td> 90 <tr><td><code>\&lt;</code></td>
88 <td>This matches the start of a word using Scintilla's definitions of words.</td></tr> 91 <td>This matches the start of a word using Scintilla's definitions of words.
92 </td></tr>
89 <tr><td><code>\&gt;</code></td> 93 <tr><td><code>\&gt;</code></td>
90 <td>This matches the end of a word using Scintilla's definition of words.</td></tr> 94 <td>This matches the end of a word using Scintilla's definition of words.
95 </td></tr>
91 <tr><td><code>\\x</code></td> 96 <tr><td><code>\\x</code></td>
92 <td>This allows you to use a character x that would otherwise have a special meaning. For 97 <td>This allows you to use a character x that would otherwise have a special
93 example, \\[ would be interpreted as [ and not as the start of a character set.</td></tr> 98 meaning. For example, \\[ would be interpreted as [ and not as the start of a
99 character set.</td></tr>
94 <tr><td><code>[...]</code></td> 100 <tr><td><code>[...]</code></td>
95 <td>This indicates a set of characters, for example, [abc] means any of the characters a, 101 <td>This indicates a set of characters, for example, [abc] means any of the
96 b or c. You can also use ranges, for example [a-z] for any lower case character.</td></tr> 102 characters a, b or c. You can also use ranges, for example [a-z] for any lower
103 case character.</td></tr>
97 <tr><td><code>[^...]</code></td> 104 <tr><td><code>[^...]</code></td>
98 <td>The complement of the characters in the set. For example, [^A-Za-z] means any 105 <td>The complement of the characters in the set. For example, [^A-Za-z] means
99 character except an alphabetic character.</td></tr> 106 any character except an alphabetic character.</td></tr>
100 <tr><td><code>^</code></td> 107 <tr><td><code>^</code></td>
101 <td>This matches the start of a line (unless used inside a set, see above).</td></tr> 108 <td>This matches the start of a line (unless used inside a set, see above).
109 </td></tr>
102 <tr><td><code>$</code></td> <td>This matches the end of a line.</td></tr> 110 <tr><td><code>$</code></td> <td>This matches the end of a line.</td></tr>
103 <tr><td><code>*</code></td> 111 <tr><td><code>*</code></td>
104 <td>This matches 0 or more times. For example, <code>Sa*m</code> matches <code>Sm</code>, 112 <td>This matches 0 or more times. For example, <code>Sa*m</code> matches
105 <code>Sam</code>, <code>Saam</code>, <code>Saaam</code> and so on.</td></tr> 113 <code>Sm</code>, <code>Sam</code>, <code>Saam</code>, <code>Saaam</code>
114 and so on.</td></tr>
106 <tr><td><code>+</code></td> 115 <tr><td><code>+</code></td>
107 <td>This matches 1 or more times. For example, <code>Sa+m</code> matches 116 <td>This matches 1 or more times. For example, <code>Sa+m</code> matches
108 <code>Sam</code>, <code>Saam</code>, <code>Saaam</code> and so on.</td></tr> 117 <code>Sam</code>, <code>Saam</code>, <code>Saaam</code> and so on.</td></tr>
109 </table> 118 </table>
110 """) 119 """)
111 self.setWhatsThis(whatsThis) 120 self.setWhatsThis(whatsThis)
112 121
113 self.ui.closeButton.setIcon(UI.PixmapCache.getIcon("close.png")) 122 self.ui.closeButton.setIcon(UI.PixmapCache.getIcon("close.png"))
114 self.ui.findPrevButton.setIcon(UI.PixmapCache.getIcon("1leftarrow.png")) 123 self.ui.findPrevButton.setIcon(
115 self.ui.findNextButton.setIcon(UI.PixmapCache.getIcon("1rightarrow.png")) 124 UI.PixmapCache.getIcon("1leftarrow.png"))
125 self.ui.findNextButton.setIcon(
126 UI.PixmapCache.getIcon("1rightarrow.png"))
116 127
117 if replace: 128 if replace:
118 self.ui.replaceButton.setIcon( 129 self.ui.replaceButton.setIcon(
119 UI.PixmapCache.getIcon("editReplace.png")) 130 UI.PixmapCache.getIcon("editReplace.png"))
120 self.ui.replaceSearchButton.setIcon( 131 self.ui.replaceSearchButton.setIcon(
121 UI.PixmapCache.getIcon("editReplaceSearch.png")) 132 UI.PixmapCache.getIcon("editReplaceSearch.png"))
122 self.ui.replaceAllButton.setIcon( 133 self.ui.replaceAllButton.setIcon(
123 UI.PixmapCache.getIcon("editReplaceAll.png")) 134 UI.PixmapCache.getIcon("editReplaceAll.png"))
124 135
125 self.ui.findtextCombo.lineEdit().returnPressed.connect(self.__findByReturnPressed) 136 self.ui.findtextCombo.lineEdit().returnPressed.connect(
137 self.__findByReturnPressed)
126 if replace: 138 if replace:
127 self.ui.replacetextCombo.lineEdit().returnPressed.connect( 139 self.ui.replacetextCombo.lineEdit().returnPressed.connect(
128 self.on_replaceButton_clicked) 140 self.on_replaceButton_clicked)
129 141
130 self.findNextAct = E5Action(self.trUtf8('Find Next'), 142 self.findNextAct = E5Action(self.trUtf8('Find Next'),
268 E5MessageBox.information(self, self.windowTitle(), 280 E5MessageBox.information(self, self.windowTitle(),
269 self.trUtf8("'{0}' was not found.").format(txt)) 281 self.trUtf8("'{0}' was not found.").format(txt))
270 282
271 def __findByReturnPressed(self): 283 def __findByReturnPressed(self):
272 """ 284 """
273 Private slot to handle the returnPressed signal of the findtext combobox. 285 Private slot to handle the returnPressed signal of the findtext
286 combobox.
274 """ 287 """
275 if self.__findBackwards: 288 if self.__findBackwards:
276 self.findPrev() 289 self.findPrev()
277 else: 290 else:
278 self.findNext() 291 self.findNext()
421 if backwards: 434 if backwards:
422 if after: 435 if after:
423 line, index = self.__selections[ind][2:] 436 line, index = self.__selections[ind][2:]
424 else: 437 else:
425 if ind > 0: 438 if ind > 0:
426 line, index = self.__selections[ind - 1][2:] 439 line, index = \
440 self.__selections[ind - 1][2:]
427 else: 441 else:
428 if after: 442 if after:
429 if ind < len(self.__selections) - 1: 443 if ind < len(self.__selections) - 1:
430 line, index = self.__selections[ind + 1][:2] 444 line, index = \
445 self.__selections[ind + 1][:2]
431 else: 446 else:
432 line, index = self.__selections[ind][:2] 447 line, index = self.__selections[ind][:2]
433 break 448 break
434 else: 449 else:
435 break 450 break
439 self.ui.wordCheckBox.isChecked(), 454 self.ui.wordCheckBox.isChecked(),
440 self.ui.wrapCheckBox.isChecked(), 455 self.ui.wrapCheckBox.isChecked(),
441 not backwards, 456 not backwards,
442 line, index) 457 line, index)
443 if ok: 458 if ok:
444 lineFrom, indexFrom, lineTo, indexTo = aw.getSelection() 459 lineFrom, indexFrom, lineTo, indexTo = \
445 if lineFrom < boundary[0] or lineFrom > boundary[2] or \ 460 aw.getSelection()
446 indexFrom < boundary[1] or indexFrom > boundary[3] or \ 461 if lineFrom < boundary[0] or \
447 indexTo < boundary[1] or indexTo > boundary[3]: 462 lineFrom > boundary[2] or \
463 indexFrom < boundary[1] or \
464 indexFrom > boundary[3] or \
465 indexTo < boundary[1] or \
466 indexTo > boundary[3]:
448 ok = False 467 ok = False
449 break 468 break
450 if not ok: 469 if not ok:
451 if self.ui.wrapCheckBox.isChecked(): 470 if self.ui.wrapCheckBox.isChecked():
452 # try it again 471 # try it again
460 self.ui.wordCheckBox.isChecked(), 479 self.ui.wordCheckBox.isChecked(),
461 self.ui.wrapCheckBox.isChecked(), 480 self.ui.wrapCheckBox.isChecked(),
462 not backwards, 481 not backwards,
463 line, index) 482 line, index)
464 if ok: 483 if ok:
465 lineFrom, indexFrom, lineTo, indexTo = aw.getSelection() 484 lineFrom, indexFrom, lineTo, indexTo = \
485 aw.getSelection()
466 if len(self.__selections) > 1: 486 if len(self.__selections) > 1:
467 for sel in self.__selections: 487 for sel in self.__selections:
468 if lineFrom == sel[0] and \ 488 if lineFrom == sel[0] and \
469 indexFrom >= sel[1] and \ 489 indexFrom >= sel[1] and \
470 indexTo <= sel[3]: 490 indexTo <= sel[3]:
543 @param editor reference to the editor (Editor) 563 @param editor reference to the editor (Editor)
544 """ 564 """
545 if not self.__finding and isinstance(editor, Editor): 565 if not self.__finding and isinstance(editor, Editor):
546 if editor.hasSelectedText(): 566 if editor.hasSelectedText():
547 selections = editor.getSelections() 567 selections = editor.getSelections()
548 line1, index1, line2, index2 = self.__selectionBoundary(selections) 568 line1, index1, line2, index2 = \
569 self.__selectionBoundary(selections)
549 if line1 != line2: 570 if line1 != line2:
550 self.ui.selectionCheckBox.setEnabled(True) 571 self.ui.selectionCheckBox.setEnabled(True)
551 self.ui.selectionCheckBox.setChecked(True) 572 self.ui.selectionCheckBox.setChecked(True)
552 self.__selections = selections 573 self.__selections = selections
553 return 574 return
564 self.__doReplace(False) 585 self.__doReplace(False)
565 586
566 @pyqtSlot() 587 @pyqtSlot()
567 def on_replaceSearchButton_clicked(self): 588 def on_replaceSearchButton_clicked(self):
568 """ 589 """
569 Private slot to replace one occurrence of text and search for the next one. 590 Private slot to replace one occurrence of text and search for the next
591 one.
570 """ 592 """
571 self.__doReplace(True) 593 self.__doReplace(True)
572 594
573 def __doReplace(self, searchNext): 595 def __doReplace(self, searchNext):
574 """ 596 """
575 Private method to replace one occurrence of text. 597 Private method to replace one occurrence of text.
576 598
577 @param searchNext flag indicating to search for the next occurrence (boolean). 599 @param searchNext flag indicating to search for the next occurrence
600 (boolean).
578 """ 601 """
579 self.__finding = True 602 self.__finding = True
580 603
581 # Check enabled status due to dual purpose usage of this method 604 # Check enabled status due to dual purpose usage of this method
582 if not self.ui.replaceButton.isEnabled() and \ 605 if not self.ui.replaceButton.isEnabled() and \
673 for ind in range(len(self.__selections)): 696 for ind in range(len(self.__selections)):
674 if lineFrom == self.__selections[ind][0]: 697 if lineFrom == self.__selections[ind][0]:
675 after = indexTo > self.__selections[ind][3] 698 after = indexTo > self.__selections[ind][3]
676 if after: 699 if after:
677 if ind < len(self.__selections) - 1: 700 if ind < len(self.__selections) - 1:
678 line, index = self.__selections[ind + 1][:2] 701 line, index = \
702 self.__selections[ind + 1][:2]
679 else: 703 else:
680 line, index = self.__selections[ind][:2] 704 line, index = self.__selections[ind][:2]
681 break 705 break
682 else: 706 else:
683 break 707 break
685 self.ui.regexpCheckBox.isChecked(), 709 self.ui.regexpCheckBox.isChecked(),
686 self.ui.caseCheckBox.isChecked(), 710 self.ui.caseCheckBox.isChecked(),
687 self.ui.wordCheckBox.isChecked(), 711 self.ui.wordCheckBox.isChecked(),
688 False, True, line, index) 712 False, True, line, index)
689 if ok: 713 if ok:
690 lineFrom, indexFrom, lineTo, indexTo = aw.getSelection() 714 lineFrom, indexFrom, lineTo, indexTo = \
691 if lineFrom < boundary[0] or lineFrom > boundary[2] or \ 715 aw.getSelection()
692 indexFrom < boundary[1] or indexFrom > boundary[3] or \ 716 if lineFrom < boundary[0] or \
693 indexTo < boundary[1] or indexTo > boundary[3]: 717 lineFrom > boundary[2] or \
718 indexFrom < boundary[1] or \
719 indexFrom > boundary[3] or \
720 indexTo < boundary[1] or \
721 indexTo > boundary[3]:
694 ok = False 722 ok = False
695 break 723 break
696 724
697 if not ok: 725 if not ok:
698 aw.selectAll(False) 726 aw.selectAll(False)
827 @param vm reference to the viewmanager object 855 @param vm reference to the viewmanager object
828 @param parent parent widget of this widget (QWidget) 856 @param parent parent widget of this widget (QWidget)
829 """ 857 """
830 super().__init__(parent) 858 super().__init__(parent)
831 859
832 self.__searchReplaceWidget = SearchReplaceWidget(replace, vm, self, True) 860 self.__searchReplaceWidget = \
861 SearchReplaceWidget(replace, vm, self, True)
833 srHeight = self.__searchReplaceWidget.height() 862 srHeight = self.__searchReplaceWidget.height()
834 863
835 self.__layout = QHBoxLayout(self) 864 self.__layout = QHBoxLayout(self)
836 self.setLayout(self.__layout) 865 self.setLayout(self.__layout)
837 self.__layout.setContentsMargins(0, 0, 0, 0) 866 self.__layout.setContentsMargins(0, 0, 0, 0)
863 self.__layout.addWidget(self.__scroller) 892 self.__layout.addWidget(self.__scroller)
864 self.__layout.addWidget(self.__rightButton) 893 self.__layout.addWidget(self.__rightButton)
865 894
866 self.setMaximumHeight(srHeight) 895 self.setMaximumHeight(srHeight)
867 896
868 self.__searchReplaceWidget.searchListChanged.connect(self.searchListChanged) 897 self.__searchReplaceWidget.searchListChanged.connect(
898 self.searchListChanged)
869 self.__leftButton.clicked[()].connect(self.__slideLeft) 899 self.__leftButton.clicked[()].connect(self.__slideLeft)
870 self.__rightButton.clicked[()].connect(self.__slideRight) 900 self.__rightButton.clicked[()].connect(self.__slideRight)
871 901
872 def findNext(self): 902 def findNext(self):
873 """ 903 """
907 super().show() 937 super().show()
908 self.__enableScrollerButtons() 938 self.__enableScrollerButtons()
909 939
910 def __slideLeft(self): 940 def __slideLeft(self):
911 """ 941 """
912 Private slot to move the widget to the left, i.e. show contents to the right. 942 Private slot to move the widget to the left, i.e. show contents to the
943 right.
913 """ 944 """
914 self.__slide(True) 945 self.__slide(True)
915 946
916 def __slideRight(self): 947 def __slideRight(self):
917 """ 948 """
918 Private slot to move the widget to the right, i.e. show contents to the left. 949 Private slot to move the widget to the right, i.e. show contents to
950 the left.
919 """ 951 """
920 self.__slide(False) 952 self.__slide(False)
921 953
922 def __slide(self, toLeft): 954 def __slide(self, toLeft):
923 """ 955 """

eric ide

mercurial