Esempio n. 1
0
    def create_field(self, mode):
        self.buttons = []
        for x in range(mode[0]):
            buttons_row = []
            for y in range(mode[1]):
                button = QToolButton(self)
                button.installEventFilter(self)
                button.setObjectName(str(x) + ' ' + str(y))
                palette = QPalette()
                brush = QBrush(QColor(155, 155, 155))
                brush.setStyle(Qt.SolidPattern)
                palette.setBrush(QPalette.Active, QPalette.Button, brush)
                brush = QBrush(QColor(255, 255, 255))
                brush.setStyle(Qt.SolidPattern)
                palette.setBrush(QPalette.Disabled, QPalette.Button, brush)
                button.setPalette(palette)
                button.clicked.connect(self.take_a_step)

                # if self.mines_map[x][y] == 1:
                #     button.setIcon(QIcon('mine.png'))
                size_policy = QSizePolicy(QSizePolicy.Expanding,
                                          QSizePolicy.Expanding)
                size_policy.setHorizontalStretch(1)
                size_policy.setHorizontalStretch(1)
                button.setSizePolicy(size_policy)
                self.grid_layout.addWidget(button, x, y)
                buttons_row.append(button)
            self.buttons.append(buttons_row)

        self.close_btn = QPushButton('close', self)
        self.vertical_layout.addItem(self.grid_layout)
        self.vertical_layout.addWidget(self.close_btn)
        self.show()
Esempio n. 2
0
class _CompactToolButton(QFrame):
    def __init__(self, action: QAction, menu: QMenu, parent):
        super(_CompactToolButton, self).__init__(parent)
        self.overlay = _TTOverlayToolButton(self)

        iconsize = int(
            get_pixelmetric(QStyle.PM_LargeIconSize) * get_scalefactor(self))
        self.upButton = QToolButton(self)
        self.upButton.setProperty("TTInternal", QtCore.QVariant(True))
        self.upButton.setAutoRaise(True)
        self.upButton.setDefaultAction(action)
        self.upButton.setIconSize(QtCore.QSize(iconsize, iconsize))
        self.upButton.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)
        self.upButton.setStyle(TTToolButtonStyle())
        self.upButton.setMaximumHeight(iconsize + 5)

        self.vlayout = QVBoxLayout(self)
        self.vlayout.setContentsMargins(0, 0, 0, 0)
        self.vlayout.setSpacing(0)
        self.vlayout.setDirection(QBoxLayout.TopToBottom)

        self.upButton.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
        self.upButton.setPopupMode(QToolButton.DelayedPopup)
        self.vlayout.addWidget(self.upButton)

        self.downButton = QToolButton(self)
        self.downButton.setProperty("TTInternal", QtCore.QVariant(True))
        self.downButton.setAutoRaise(True)
        self.downButton.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly)
        self.downButton.setPopupMode(QToolButton.InstantPopup)
        self.downButton.setMinimumHeight(25)
        self.downButton.setSizePolicy(QSizePolicy.Preferred,
                                      QSizePolicy.Minimum)
        self.downButton.setText(action.text())
        self.downButton.setToolTip(action.toolTip())
        self.downButton.setStyle(TTToolButtonStyle())

        if menu:
            self.downButton.setMenu(menu)
            menu.aboutToHide.connect(lambda: self.set_hover(False))

        self.vlayout.addWidget(self.downButton)
        self.setLayout(self.vlayout)

        self.hover = _TTHover(self, self.upButton, self.downButton)
        self.upButton.installEventFilter(self.hover)
        self.downButton.installEventFilter(self.hover)

    def set_hover(self, hover: bool):
        self.overlay.paint = hover
        self.update()
Esempio n. 3
0
class ComboBoxItem(QWidget):

    itemOpSignal = pyqtSignal(str)

    def __init__(self, qq, username, user_icon):
        super().__init__()
        self.username = username
        self.qq = qq
        self.user_icon = user_icon
        self.initUi()

    def initUi(self):
        lb_username = QLabel(self.username, self)
        lb_qq = QLabel(self.qq, self)
        lb_icon = QLabel(self)
        lb_icon.setPixmap(QPixmap(self.user_icon))
        self.bt_close = QToolButton(self)
        self.bt_close.setIcon(QIcon("res/close.png"))
        self.bt_close.setAutoRaise(True)

        vlayout = QVBoxLayout()
        vlayout.addWidget(lb_username)
        vlayout.addWidget(lb_qq)

        hlayout = QHBoxLayout()
        hlayout.addWidget(lb_icon)
        hlayout.addLayout(vlayout)
        hlayout.addStretch(1)
        hlayout.addWidget(self.bt_close)
        hlayout.setContentsMargins(5, 5, 5, 5)
        hlayout.setSpacing(5)

        self.setLayout(hlayout)

        self.bt_close.installEventFilter(self)
        self.installEventFilter(self)

    def eventFilter(self, object, event):
        if object is self:
            if event.type() == QEvent.Enter:
                self.setStyleSheet("QWidget{color:white}")
            elif event.type() == QEvent.Leave:
                self.setStyleSheet("QWidget{color:black}")
        elif object is self.bt_close:
            if event.type() == QEvent.MouseButtonPress:
                self.itemOpSignal.emit(self.qq)
        return QWidget.eventFilter(self, object, event)
Esempio n. 4
0
class QCustomTitleBar(QFrame):
    onMousePressEvent = pyqtSignal(QEvent)
    onMouseMoveEvent = pyqtSignal(QEvent)
    onMouseReleaseEvent = pyqtSignal(QEvent)
    onCloseButtonClickedEvent = pyqtSignal(QWidget)

    def __init__(self, parent):
        super(QCustomTitleBar, self).__init__(parent)
        # print ( "[create] QCustomTitleBar for parent", parent )
        self.dragging = False
        self.createFromDraging = False
        self.mouseStartPos = QCursor.pos()

        if parent.metaObject().indexOfSignal(
                QMetaObject.normalizedSignature("contentsChanged")) != -1:
            parent.contentsChanged.connect(self.onFrameContentsChanged)

        myLayout = QHBoxLayout(self)
        myLayout.setContentsMargins(0, 0, 0, 0)
        myLayout.setSpacing(0)

        self.setLayout(myLayout)

        self.caption = QLabel(self)
        self.caption.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
        self.caption.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Preferred)

        self.sysMenuButton = QToolButton(self)
        self.onIconChange()
        self.sysMenuButton.setObjectName("sysMenu")
        self.sysMenuButton.setFocusPolicy(QtCore.Qt.NoFocus)
        self.sysMenuButton.installEventFilter(self)
        myLayout.addWidget(self.sysMenuButton)
        myLayout.addWidget(self.caption, 1)

        self.minimizeButton = QToolButton(self)
        self.minimizeButton.setObjectName("minimizeButton")
        self.minimizeButton.setFocusPolicy(QtCore.Qt.NoFocus)
        # self.minimizeButton.clicked.connect ( parent.showMinimized )
        self.minimizeButton.clicked.connect(
            lambda: parent.setWindowState(Qt.WindowMinimized))
        myLayout.addWidget(self.minimizeButton)

        self.maximizeButton = QToolButton(self)
        self.maximizeButton.setObjectName("maximizeButton")
        self.maximizeButton.setFocusPolicy(QtCore.Qt.NoFocus)
        self.maximizeButton.clicked.connect(self.toggleMaximizedParent)
        myLayout.addWidget(self.maximizeButton)

        self.closeButton = QToolButton(self)
        self.closeButton.setObjectName("closeButton")
        self.closeButton.setFocusPolicy(QtCore.Qt.NoFocus)
        self.closeButton.clicked.connect(self.onCloseButtonClicked)
        myLayout.addWidget(self.closeButton)

        parent.windowTitleChanged.connect(self.caption.setText)
        parent.windowIconChanged.connect(self.onIconChange)

        self.onFrameContentsChanged(parent)

        # self.setMouseTracking ( True )

    def updateWindowStateButtons(self):
        if self.maximizeButton != None:
            if self.parentWidget().windowState() & QtCore.Qt.WindowMaximized:
                self.maximizeButton.setObjectName("restoreButton")
            else:
                self.maximizeButton.setObjectName("maximizeButton")

            self.style().unpolish(self.maximizeButton)
            self.style().polish(self.maximizeButton)

    def setActive(self, active):
        self.caption.setObjectName("active" if active else "inactive")
        self.style().unpolish(self.caption)
        self.style().polish(self.caption)

    def toggleMaximizedParent(self):
        if self.parentWidget().windowState() & QtCore.Qt.WindowMaximized:
            self.parentWidget().showNormal()
        else:
            self.parentWidget().showMaximized()
        self.updateWindowStateButtons()

    def toggleMinimizedParent(self):
        flags = self.parentWidget().windowFlags()
        self.parentWidget().setWindowFlags(flags
                                           | QtCore.Qt.CustomizeWindowHint
                                           & ~QtCore.Qt.WindowTitleHint)
        # self.setWindowFlags ( flags | QtCore.Qt.CustomizeWindowHint & ~QtCore.Qt.WindowTitleHint )
        self.parentWidget().showMinimized()
        self.parentWidget().setWindowFlags(
            self.parentWidget().windowFlags()
            & (~QtCore.Qt.CustomContextMenu & ~QtCore.Qt.WindowTitleHint)
            | QtCore.Qt.FramelessWindowHint)

    def showSystemMenu(self, p):
        pass

    def onCloseButtonClicked(self):
        # print ( "[QCustomTitleBar] close requested. %s" % self.parentWidget () )
        self.onCloseButtonClickedEvent.emit(self)
        self.parentWidget().close()

    def onIconChange(self):
        icon = self.parentWidget().windowIcon()
        if icon is None:
            icon = qApp.windowIcon()
            if icon is None:
                pass
        self.sysMenuButton.setIcon(icon)

    def onFrameContentsChanged(self, newContents):
        flags = self.parentWidget().windowFlags()
        self.minimizeButton.setVisible(
            flags & QtCore.Qt.WindowMinimizeButtonHint
            or flags & QtCore.Qt.MSWindowsFixedSizeDialogHint)
        self.maximizeButton.setVisible(
            flags & QtCore.Qt.WindowMaximizeButtonHint
            or flags & QtCore.Qt.MSWindowsFixedSizeDialogHint)
        self.closeButton.setVisible(flags & QtCore.Qt.WindowCloseButtonHint)

        winTitle = self.parentWidget().windowTitle()
        if len(winTitle) == 0:
            self.caption.setText(
                "[QCustomTitleBar] onFrameContentsChanged NoneWindowTitle")
            return
        self.caption.setText(winTitle)

    def onBeginDrag(self):
        self.dragging = True

    def mousePressEvent(self, e):

        self.onMousePressEvent.emit(e)

        if e.button() == QtCore.Qt.LeftButton and qApp.widgetAt(
                QCursor.pos()) != self.sysMenuButton:
            self.onBeginDrag()
            self.mouseStartPos = e.globalPos()
            # self.windowHandle ().startSystemMove ()
            # qWarning ( "QCustomTitleBar::onBeginDrag" )

        super().mousePressEvent(e)

    def mouseMoveEvent(self, e):

        self.onMouseMoveEvent.emit(e)

        if self.dragging:
            # self.window ().move ( e.globalPos () - self.mouseStartPos )
            offset = self.mouseStartPos - e.globalPos()
            self.mouseStartPos = e.globalPos()
            self.window().move(self.window().pos() - offset)
            e.accept()
            # qWarning ( "QCustomTitleBar::mouseMoveEvent" )
        else:
            super().mouseMoveEvent(e)

    def mouseReleaseEvent(self, e):

        self.onMouseReleaseEvent.emit(e)

        if not self.dragging:
            if e.button() == QtCore.Qt.RightButton and self.rect().contains(
                    self.mapFromGlobal(QCursor.pos())):
                e.accept()
                self.showSystemMenu(QCursor.pos())
            else:
                e.ignore()
            return
        self.dragging = False

        if self.createFromDraging:
            self.releaseMouse()
            self.createFromDraging = False

        # qWarning ( "QCustomTitleBar::mouseReleaseEvent" )

        super().mouseReleaseEvent(e)

    def mouseDoubleClickEvent(self, e):
        e.accept()
        self.toggleMaximizedParent()

    def eventFilter(self, o, e):
        if o == self.sysMenuButton:
            if e.type() == QEvent.MouseButtonPress:
                if e.button() == QtCore.Qt.LeftButton and qApp.widgetAt(
                        QCursor.pos()) == self.sysMenuButton:
                    self.showSystemMenu(
                        self.mapToGlobal(self.rect().bottomLeft()))
                    return True
            if e.type() == QEvent.MouseButtonDblClick:
                if e.button() == QtCore.Qt.LeftButton and qApp.widgetAt(
                        QCursor.pos()) == self.sysMenuButton:
                    self.parentWidget().close()
                    return True

        return super().eventFilter(o, e)
Esempio n. 5
0
class SearchWidget(QFrame):
    """Widget, appeared, when Ctrl+F pressed.
    Has different forms for different search modes
    """

    Normal = 'normal'
    Good = 'good'
    Bad = 'bad'
    Incorrect = 'incorrect'

    visibilityChanged = pyqtSignal(bool)
    """
    visibilityChanged(visible)

    **Signal** emitted, when widget has been shown or hidden
    """  # pylint: disable=W0105

    searchInDirectoryStartPressed = pyqtSignal(type(re.compile('')), list, str)
    """
    searchInDirectoryStartPressed(regEx, mask, path)

    **Signal** emitted, when 'search in directory' button had been pressed
    """  # pylint: disable=W0105

    searchInDirectoryStopPressed = pyqtSignal()
    """
    searchInDirectoryStopPressed()

    **Signal** emitted, when 'stop search in directory' button had been pressed
    """  # pylint: disable=W0105

    replaceCheckedStartPressed = pyqtSignal(str)
    """
    replaceCheckedStartPressed(replText)

    **Signal** emitted, when 'replace checked' button had been pressed
    """  # pylint: disable=W0105

    replaceCheckedStopPressed = pyqtSignal()
    """
    replaceCheckedStartPressed()

    **Signal** emitted, when 'stop replacing checked' button had been pressed
    """  # pylint: disable=W0105

    searchRegExpChanged = pyqtSignal(type(re.compile('')))
    """
    searchRegExpValidStateChanged(regEx)

    **Signal** emitted, when search regexp has been changed.
    If reg exp is invalid - regEx object contains empty pattern
    """  # pylint: disable=W0105

    searchNext = pyqtSignal()
    """
    searchNext()

    **Signal** emitted, when 'Search Next' had been pressed
    """  # pylint: disable=W0105

    searchPrevious = pyqtSignal()
    """
    searchPrevious()

    **Signal** emitted, when 'Search Previous' had been pressed
    """  # pylint: disable=W0105

    replaceFileOne = pyqtSignal(str)
    """
    replaceFileOne(replText)

    **Signal** emitted, when 'Replace' had been pressed
    """  # pylint: disable=W0105

    replaceFileAll = pyqtSignal(str)
    """
    replaceFileAll(replText)

    **Signal** emitted, when 'Replace All' had been pressed
    """  # pylint: disable=W0105

    def __init__(self, plugin):
        QFrame.__init__(self, core.workspace())
        self._mode = None
        self.plugin = plugin
        uic.loadUi(os.path.join(os.path.dirname(__file__), 'SearchWidget.ui'), self)

        self.cbSearch.setCompleter(None)
        self.cbReplace.setCompleter(None)
        self.cbMask.setCompleter(None)

        self.fsModel = QDirModel(self.cbPath.lineEdit())
        self.fsModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)

        self.cbPath.lineEdit().setCompleter(QCompleter(self.fsModel,
                                                       self.cbPath.lineEdit()))
        self._pathBackspaceShortcut = QShortcut(QKeySequence("Ctrl+Backspace"), self.cbPath, self._onPathBackspace)
        self._pathBackspaceShortcut.setContext(Qt.WidgetWithChildrenShortcut)

        # TODO QDirModel is deprecated but QCompleter does not yet handle
        # QFileSystemodel - please update when possible."""
        self.cbSearch.setCompleter(None)
        self.pbSearchStop.setVisible(False)
        self.pbReplaceCheckedStop.setVisible(False)

        self._progress = QProgressBar(self)
        self._progress.setAlignment(Qt.AlignCenter)
        self._progress.setToolTip(self.tr("Search in progress..."))
        self._progress.setMaximumSize(QSize(80, 16))
        core.mainWindow().statusBar().insertPermanentWidget(1, self._progress)
        self._progress.setVisible(False)

        # cd up action
        self.tbCdUp = QToolButton(self.cbPath.lineEdit())
        self.tbCdUp.setIcon(QIcon(":/enkiicons/go-up.png"))
        self.tbCdUp.setCursor(Qt.ArrowCursor)
        self.tbCdUp.installEventFilter(self)  # for drawing button

        self.cbSearch.installEventFilter(self)  # for catching Tab and Shift+Tab
        self.cbReplace.installEventFilter(self)  # for catching Tab and Shift+Tab
        self.cbPath.installEventFilter(self)  # for catching Tab and Shift+Tab
        self.cbMask.installEventFilter(self)  # for catching Tab and Shift+Tab

        self._closeShortcut = QShortcut(QKeySequence("Esc"), self)
        self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut)
        self._closeShortcut.activated.connect(self.hide)

        # connections
        self.cbSearch.lineEdit().textChanged.connect(self._onSearchRegExpChanged)

        self.cbSearch.lineEdit().returnPressed.connect(self._onReturnPressed)
        self.cbReplace.lineEdit().returnPressed.connect(self._onReturnPressed)
        self.cbPath.lineEdit().returnPressed.connect(self._onReturnPressed)
        self.cbMask.lineEdit().returnPressed.connect(self._onReturnPressed)

        self.cbRegularExpression.stateChanged.connect(self._onSearchRegExpChanged)
        self.cbCaseSensitive.stateChanged.connect(self._onSearchRegExpChanged)
        self.cbWholeWord.stateChanged.connect(self._onSearchRegExpChanged)

        self.tbCdUp.clicked.connect(self._onCdUpPressed)

        self.pbNext.pressed.connect(self.searchNext)
        self.pbPrevious.pressed.connect(self.searchPrevious)
        self.pbSearchStop.pressed.connect(self.searchInDirectoryStopPressed)
        self.pbReplaceCheckedStop.pressed.connect(self.replaceCheckedStopPressed)

        core.mainWindow().hideAllWindows.connect(self.hide)
        core.workspace().escPressed.connect(self.hide)

        core.workspace().currentDocumentChanged.connect(
            lambda old, new: self.setVisible(self.isVisible() and new is not None))

    def show(self):
        """Reimplemented function. Sends signal
        """
        super(SearchWidget, self).show()
        self.visibilityChanged.emit(self.isVisible())

    def hide(self):
        """Reimplemented function.
        Sends signal, returns focus to workspace
        """
        super(SearchWidget, self).hide()
        core.workspace().focusCurrentDocument()
        self.visibilityChanged.emit(self.isVisible())

    def setVisible(self, visible):
        """Reimplemented function. Sends signal
        """
        super(SearchWidget, self).setVisible(visible)
        self.visibilityChanged.emit(self.isVisible())

    def _regExEscape(self, text):
        """Improved version of re.escape()
        Doesn't escape space, comma, underscore.
        Escapes \n and \t
        """
        text = re.escape(text)
        # re.escape escapes space, comma, underscore, but, it is not necessary and makes text not readable
        for symbol in (' ,_=\'"/:@#%&'):
            text = text.replace('\\' + symbol, symbol)

        text = text.replace('\\\n', '\\n')
        text = text.replace('\\\t', '\\t')

        return text

    def _makeEscapeSeqsVisible(self, text):
        """Replace invisible \n and \t with escape sequences
        """
        text = text.replace('\\', '\\\\')
        text = text.replace('\t', '\\t')
        text = text.replace('\n', '\\n')
        return text

    def setMode(self, mode):
        """Change search mode.
        i.e. from "Search file" to "Replace directory"
        """
        if self._mode == mode and self.isVisible():
            if core.workspace().currentDocument() is not None and \
               not core.workspace().currentDocument().hasFocus():
                self.cbSearch.lineEdit().selectAll()
                self.cbSearch.setFocus()

        self._mode = mode

        # Set Search and Replace text
        document = core.workspace().currentDocument()
        if document is not None and \
           document.hasFocus() and \
           document.qutepart.selectedText:
            searchText = document.qutepart.selectedText

            self.cbReplace.setEditText(self._makeEscapeSeqsVisible(searchText))

            if self.cbRegularExpression.checkState() == Qt.Checked:
                searchText = self._regExEscape(searchText)
            self.cbSearch.setEditText(searchText)

        if not self.cbReplace.lineEdit().text() and \
                self.cbSearch.lineEdit().text() and \
                not self.cbRegularExpression.checkState() == Qt.Checked:
            replaceText = self.cbSearch.lineEdit().text().replace('\\', '\\\\')
            self.cbReplace.setEditText(replaceText)

        # Move focus to Search edit
        self.cbSearch.setFocus()
        self.cbSearch.lineEdit().selectAll()

        # Set search path
        if mode & MODE_FLAG_DIRECTORY and \
           not (self.isVisible() and self.cbPath.isVisible()):
            try:
                searchPath = os.path.abspath(str(os.path.curdir))
            except OSError:  # current directory might have been deleted
                pass
            else:
                self.cbPath.setEditText(searchPath)

        # Set widgets visibility flag according to state
        widgets = (self.wSearch, self.pbPrevious, self.pbNext, self.pbSearch, self.wReplace, self.wPath,
                   self.pbReplace, self.pbReplaceAll, self.pbReplaceChecked, self.wOptions, self.wMask)
        #                         wSear  pbPrev pbNext pbSear wRepl  wPath  pbRep  pbRAll pbRCHK wOpti wMask
        visible = \
            {MODE_SEARCH:               (1,     1,     1,     0,     0,     0,     0,     1,     1,    1,    0,),
             MODE_REPLACE:               (1,     1,     1,     0,     1,     0,     1,     1,     0,    1,    0,),
             MODE_SEARCH_DIRECTORY:      (1,     0,     0,     1,     0,     1,     0,     0,     0,    1,    1,),
             MODE_REPLACE_DIRECTORY:     (1,     0,     0,     1,     1,     1,     0,     0,     1,    1,    1,),
             MODE_SEARCH_OPENED_FILES:   (1,     0,     0,     1,     0,     0,     0,     0,     0,    1,    1,),
             MODE_REPLACE_OPENED_FILES:  (1,     0,     0,     1,     1,     0,     0,     0,     1,    1,    1,)}

        for i, widget in enumerate(widgets):
            widget.setVisible(visible[mode][i])

        # Search next button text
        if mode == MODE_REPLACE:
            self.pbNext.setText('Next')
        else:
            self.pbNext.setText('Next↵')

        # Finaly show all with valid size
        self.show()  # show before updating widgets and labels
        self._updateLabels()
        self._updateWidgets()

    def eventFilter(self, object_, event):
        """ Event filter for mode switch tool button
        Draws icons in the search and path lineEdits
        """
        if event.type() == QEvent.Paint and object_ is self.tbCdUp:  # draw CdUp button in search path QLineEdit
            toolButton = object_
            lineEdit = self.cbPath.lineEdit()
            lineEdit.setContentsMargins(lineEdit.height(), 0, 0, 0)

            height = lineEdit.height()
            availableRect = QRect(0, 0, height, height)

            if toolButton.rect() != availableRect:
                toolButton.setGeometry(availableRect)

            painter = QPainter(toolButton)
            toolButton.icon().paint(painter, availableRect)

            return True

        elif event.type() == QEvent.KeyPress:  # Tab and Shift+Tab in QLineEdits

            if event.key() == Qt.Key_Tab:
                self._moveFocus(1)
                return True
            elif event.key() == Qt.Key_Backtab:
                self._moveFocus(-1)
                return True

        return QFrame.eventFilter(self, object_, event)

    def _onReturnPressed(self):
        """Return or Enter pressed on widget.
        Search next or Replace next
        """
        if self.pbReplace.isVisible():
            self.pbReplace.click()
        elif self.pbNext.isVisible():
            self.pbNext.click()
        elif self.pbSearch.isVisible():
            self.pbSearch.click()
        elif self.pbSearchStop.isVisible():
            self.pbSearchStop.click()

    def _onPathBackspace(self):
        """Ctrl+Backspace pressed on path.
        Remove 1 path level.
        Default behavior would be to remove one word on Linux or all on Windows
        """
        path = self.cbPath.currentText()
        if path.endswith('/') or \
           path.endswith('\\'):
            path = path[:-1]

        head, tail = os.path.split(path)
        if head and \
           head != path:
            if not head.endswith(os.sep):
                head += os.sep
            self.cbPath.lineEdit().setText(head)

    def _moveFocus(self, step):
        """Move focus forward or backward according to step.
        Standard Qt Keyboard focus algorithm doesn't allow circular navigation
        """
        allFocusableWidgets = (self.cbSearch, self.cbReplace, self.cbPath, self.cbMask)
        visibleWidgets = [widget for widget in allFocusableWidgets
                          if widget.isVisible()]

        try:
            focusedIndex = visibleWidgets.index(QApplication.focusWidget())
        except ValueError:
            print('Invalid focused widget in Search Widget', file=sys.stderr)
            return

        nextFocusedIndex = (focusedIndex + step) % len(visibleWidgets)

        visibleWidgets[nextFocusedIndex].setFocus()
        visibleWidgets[nextFocusedIndex].lineEdit().selectAll()

    def _updateLabels(self):
        """Update 'Search' 'Replace' 'Path' labels geometry
        """
        width = 0

        if self.lSearch.isVisible():
            width = max(width, self.lSearch.minimumSizeHint().width())

        if self.lReplace.isVisible():
            width = max(width, self.lReplace.minimumSizeHint().width())

        if self.lPath.isVisible():
            width = max(width, self.lPath.minimumSizeHint().width())

        self.lSearch.setMinimumWidth(width)
        self.lReplace.setMinimumWidth(width)
        self.lPath.setMinimumWidth(width)

    def _updateWidgets(self):
        """Update geometry of widgets with buttons
        """
        width = 0

        if self.wSearchRight.isVisible():
            width = max(width, self.wSearchRight.minimumSizeHint().width())

        if self.wReplaceRight.isVisible():
            width = max(width, self.wReplaceRight.minimumSizeHint().width())

        if self.wPathRight.isVisible():
            width = max(width, self.wPathRight.minimumSizeHint().width())

        self.wSearchRight.setMinimumWidth(width)
        self.wReplaceRight.setMinimumWidth(width)
        self.wPathRight.setMinimumWidth(width)

    def updateComboBoxes(self):
        """Update comboboxes with last used texts
        """
        searchText = self.cbSearch.currentText()
        replaceText = self.cbReplace.currentText()
        maskText = self.cbMask.currentText()

        # search
        if searchText:
            index = self.cbSearch.findText(searchText)

            if index == -1:
                self.cbSearch.addItem(searchText)

        # replace
        if replaceText:
            index = self.cbReplace.findText(replaceText)

            if index == -1:
                self.cbReplace.addItem(replaceText)

        # mask
        if maskText:
            index = self.cbMask.findText(maskText)

            if index == -1:
                self.cbMask.addItem(maskText)

    def _searchPatternTextAndFlags(self):
        """Get search pattern and flags
        """
        pattern = self.cbSearch.currentText()

        pattern = pattern.replace('\u2029', '\n')  # replace unicode paragraph separator with habitual \n

        if not self.cbRegularExpression.checkState() == Qt.Checked:
            pattern = re.escape(pattern)

        if self.cbWholeWord.checkState() == Qt.Checked:
            pattern = r'\b' + pattern + r'\b'

        flags = 0
        if not self.cbCaseSensitive.checkState() == Qt.Checked:
            flags = re.IGNORECASE
        return pattern, flags

    def getRegExp(self):
        """Read search parameters from controls and present it as a regular expression
        """
        pattern, flags = self._searchPatternTextAndFlags()
        return re.compile(pattern, flags)

    def isSearchRegExpValid(self):
        """Try to compile search pattern to check if it is valid
        Returns bool result and text error
        """
        pattern, flags = self._searchPatternTextAndFlags()
        try:
            re.compile(pattern, flags)
        except re.error as ex:
            return False, str(ex)

        return True, None

    def _getSearchMask(self):
        """Get search mask as list of patterns
        """
        mask = [s.strip() for s in self.cbMask.currentText().split(' ')]
        # remove empty
        mask = [_f for _f in mask if _f]
        return mask

    def setState(self, state):
        """Change line edit color according to search result
        """
        widget = self.cbSearch.lineEdit()

        color = {SearchWidget.Normal: QApplication.instance().palette().color(QPalette.Base),
                 SearchWidget.Good: QColor(Qt.green),
                 SearchWidget.Bad: QColor(Qt.red),
                 SearchWidget.Incorrect: QColor(Qt.darkYellow)}

        stateColor = color[state]
        if state != SearchWidget.Normal:
            stateColor.setAlpha(100)

        pal = widget.palette()
        pal.setColor(widget.backgroundRole(), stateColor)
        widget.setPalette(pal)

    def setSearchInProgress(self, inProgress):
        """Search thread started or stopped
        """
        self.pbSearchStop.setVisible(inProgress)
        self.pbSearch.setVisible(not inProgress)
        self._updateWidgets()
        self._progress.setVisible(inProgress)

    def onSearchProgressChanged(self, value, total):
        """Signal from the thread, progress changed
        """
        self._progress.setValue(value)
        self._progress.setMaximum(total)

    def setReplaceInProgress(self, inProgress):
        """Replace thread started or stopped
        """
        self.pbReplaceCheckedStop.setVisible(inProgress)
        self.pbReplaceChecked.setVisible(not inProgress)
        self._updateWidgets()

    def setSearchInFileActionsEnabled(self, enabled):
        """Set enabled state for Next, Prev, Replace, ReplaceAll
        """
        for button in (self.pbNext, self.pbPrevious, self.pbReplace, self.pbReplaceAll):
            button.setEnabled(enabled)

    def _onSearchRegExpChanged(self):
        """User edited search text or checked/unchecked checkboxes
        """
        valid, error = self.isSearchRegExpValid()
        if valid:
            self.setState(self.Normal)
            core.mainWindow().statusBar().clearMessage()
            self.pbSearch.setEnabled(len(self.getRegExp().pattern) > 0)
        else:
            core.mainWindow().statusBar().showMessage(error, 3000)
            self.setState(self.Incorrect)
            self.pbSearch.setEnabled(False)
            self.searchRegExpChanged.emit(re.compile(''))
            return

        self.searchRegExpChanged.emit(self.getRegExp())

    def _onCdUpPressed(self):
        """User pressed "Up" button, need to remove one level from search path
        """
        text = self.cbPath.currentText()
        if not os.path.exists(text):
            return

        editText = os.path.normpath(os.path.join(text, os.path.pardir))
        self.cbPath.setEditText(editText)

    def on_pbSearch_pressed(self):
        """Handler of click on "Search" button (for search in directory)
        """
        self.setState(SearchWidget.Normal)

        self.searchInDirectoryStartPressed.emit(self.getRegExp(),
                                                self._getSearchMask(),
                                                self.cbPath.currentText())

    def on_pbReplace_pressed(self):
        """Handler of click on "Replace" (in file) button
        """
        self.replaceFileOne.emit(self.cbReplace.currentText())

    def on_pbReplaceAll_pressed(self):
        """Handler of click on "Replace all" (in file) button
        """
        self.replaceFileAll.emit(self.cbReplace.currentText())

    def on_pbReplaceChecked_pressed(self):
        """Handler of click on "Replace checked" (in directory) button
        """
        self.replaceCheckedStartPressed.emit(self.cbReplace.currentText())

    def on_pbBrowse_pressed(self):
        """Handler of click on "Browse" button. Explores FS for search directory path
        """
        path = QFileDialog.getExistingDirectory(self, self.tr("Search path"), self.cbPath.currentText())

        if path:
            self.cbPath.setEditText(path)
Esempio n. 6
0
class SearchWidget(QFrame):
    """Widget, appeared, when Ctrl+F pressed.
    Has different forms for different search modes
    """

    Normal = 'normal'
    Good = 'good'
    Bad = 'bad'
    Incorrect = 'incorrect'

    visibilityChanged = pyqtSignal(bool)
    """
    visibilityChanged(visible)

    **Signal** emitted, when widget has been shown or hidden
    """  # pylint: disable=W0105

    searchInDirectoryStartPressed = pyqtSignal(type(re.compile('')), list, str)
    """
    searchInDirectoryStartPressed(regEx, mask, path)

    **Signal** emitted, when 'search in directory' button had been pressed
    """  # pylint: disable=W0105

    searchInDirectoryStopPressed = pyqtSignal()
    """
    searchInDirectoryStopPressed()

    **Signal** emitted, when 'stop search in directory' button had been pressed
    """  # pylint: disable=W0105

    replaceCheckedStartPressed = pyqtSignal(str)
    """
    replaceCheckedStartPressed(replText)

    **Signal** emitted, when 'replace checked' button had been pressed
    """  # pylint: disable=W0105

    replaceCheckedStopPressed = pyqtSignal()
    """
    replaceCheckedStartPressed()

    **Signal** emitted, when 'stop replacing checked' button had been pressed
    """  # pylint: disable=W0105

    searchRegExpChanged = pyqtSignal(type(re.compile('')))
    """
    searchRegExpValidStateChanged(regEx)

    **Signal** emitted, when search regexp has been changed.
    If reg exp is invalid - regEx object contains empty pattern
    """  # pylint: disable=W0105

    searchNext = pyqtSignal()
    """
    searchNext()

    **Signal** emitted, when 'Search Next' had been pressed
    """  # pylint: disable=W0105

    searchPrevious = pyqtSignal()
    """
    searchPrevious()

    **Signal** emitted, when 'Search Previous' had been pressed
    """  # pylint: disable=W0105

    replaceFileOne = pyqtSignal(str)
    """
    replaceFileOne(replText)

    **Signal** emitted, when 'Replace' had been pressed
    """  # pylint: disable=W0105

    replaceFileAll = pyqtSignal(str)
    """
    replaceFileAll(replText)

    **Signal** emitted, when 'Replace All' had been pressed
    """  # pylint: disable=W0105

    def __init__(self, plugin):
        QFrame.__init__(self, core.workspace())
        self._mode = None
        self.plugin = plugin
        uic.loadUi(os.path.join(os.path.dirname(__file__), 'SearchWidget.ui'),
                   self)

        self.cbSearch.setCompleter(None)
        self.cbReplace.setCompleter(None)
        self.cbMask.setCompleter(None)

        self.fsModel = QDirModel(self.cbPath.lineEdit())
        self.fsModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)

        self.cbPath.lineEdit().setCompleter(
            QCompleter(self.fsModel, self.cbPath.lineEdit()))
        self._pathBackspaceShortcut = QShortcut(QKeySequence("Ctrl+Backspace"),
                                                self.cbPath,
                                                self._onPathBackspace)
        self._pathBackspaceShortcut.setContext(Qt.WidgetWithChildrenShortcut)

        # TODO QDirModel is deprecated but QCompleter does not yet handle
        # QFileSystemodel - please update when possible."""
        self.cbSearch.setCompleter(None)
        self.pbSearchStop.setVisible(False)
        self.pbReplaceCheckedStop.setVisible(False)

        self._progress = QProgressBar(self)
        self._progress.setAlignment(Qt.AlignCenter)
        self._progress.setToolTip(self.tr("Search in progress..."))
        self._progress.setMaximumSize(QSize(80, 16))
        core.mainWindow().statusBar().insertPermanentWidget(1, self._progress)
        self._progress.setVisible(False)

        # cd up action
        self.tbCdUp = QToolButton(self.cbPath.lineEdit())
        self.tbCdUp.setIcon(QIcon(":/enkiicons/go-up.png"))
        self.tbCdUp.setCursor(Qt.ArrowCursor)
        self.tbCdUp.installEventFilter(self)  # for drawing button

        self.cbSearch.installEventFilter(
            self)  # for catching Tab and Shift+Tab
        self.cbReplace.installEventFilter(
            self)  # for catching Tab and Shift+Tab
        self.cbPath.installEventFilter(self)  # for catching Tab and Shift+Tab
        self.cbMask.installEventFilter(self)  # for catching Tab and Shift+Tab

        self._closeShortcut = QShortcut(QKeySequence("Esc"), self)
        self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut)
        self._closeShortcut.activated.connect(self.hide)

        # connections
        self.cbSearch.lineEdit().textChanged.connect(
            self._onSearchRegExpChanged)

        self.cbSearch.lineEdit().returnPressed.connect(self._onReturnPressed)
        self.cbReplace.lineEdit().returnPressed.connect(self._onReturnPressed)
        self.cbPath.lineEdit().returnPressed.connect(self._onReturnPressed)
        self.cbMask.lineEdit().returnPressed.connect(self._onReturnPressed)

        self.cbRegularExpression.stateChanged.connect(
            self._onSearchRegExpChanged)
        self.cbCaseSensitive.stateChanged.connect(self._onSearchRegExpChanged)
        self.cbWholeWord.stateChanged.connect(self._onSearchRegExpChanged)

        self.tbCdUp.clicked.connect(self._onCdUpPressed)

        self.pbNext.pressed.connect(self.searchNext)
        self.pbPrevious.pressed.connect(self.searchPrevious)
        self.pbSearchStop.pressed.connect(self.searchInDirectoryStopPressed)
        self.pbReplaceCheckedStop.pressed.connect(
            self.replaceCheckedStopPressed)

        core.mainWindow().hideAllWindows.connect(self.hide)
        core.workspace().escPressed.connect(self.hide)

        core.workspace().currentDocumentChanged.connect(
            lambda old, new: self.setVisible(self.isVisible() and new is
                                             not None))

    def show(self):
        """Reimplemented function. Sends signal
        """
        super(SearchWidget, self).show()
        self.visibilityChanged.emit(self.isVisible())

    def hide(self):
        """Reimplemented function.
        Sends signal, returns focus to workspace
        """
        super(SearchWidget, self).hide()
        core.workspace().focusCurrentDocument()
        self.visibilityChanged.emit(self.isVisible())

    def setVisible(self, visible):
        """Reimplemented function. Sends signal
        """
        super(SearchWidget, self).setVisible(visible)
        self.visibilityChanged.emit(self.isVisible())

    def _regExEscape(self, text):
        """Improved version of re.escape()
        Doesn't escape space, comma, underscore.
        Escapes \n and \t
        """
        text = re.escape(text)
        # re.escape escapes space, comma, underscore, but, it is not necessary and makes text not readable
        for symbol in (' ,_=\'"/:@#%&'):
            text = text.replace('\\' + symbol, symbol)

        text = text.replace('\\\n', '\\n')
        text = text.replace('\\\t', '\\t')

        return text

    def _makeEscapeSeqsVisible(self, text):
        """Replace invisible \n and \t with escape sequences
        """
        text = text.replace('\\', '\\\\')
        text = text.replace('\t', '\\t')
        text = text.replace('\n', '\\n')
        return text

    def setMode(self, mode):
        """Change search mode.
        i.e. from "Search file" to "Replace directory"
        """
        if self._mode == mode and self.isVisible():
            if core.workspace().currentDocument() is not None and \
               not core.workspace().currentDocument().hasFocus():
                self.cbSearch.lineEdit().selectAll()
                self.cbSearch.setFocus()

        self._mode = mode

        # Set Search and Replace text
        document = core.workspace().currentDocument()
        if document is not None and \
           document.hasFocus() and \
           document.qutepart.selectedText:
            searchText = document.qutepart.selectedText

            self.cbReplace.setEditText(self._makeEscapeSeqsVisible(searchText))

            if self.cbRegularExpression.checkState() == Qt.Checked:
                searchText = self._regExEscape(searchText)
            self.cbSearch.setEditText(searchText)

        if not self.cbReplace.lineEdit().text() and \
                self.cbSearch.lineEdit().text() and \
                not self.cbRegularExpression.checkState() == Qt.Checked:
            replaceText = self.cbSearch.lineEdit().text().replace('\\', '\\\\')
            self.cbReplace.setEditText(replaceText)

        # Move focus to Search edit
        self.cbSearch.setFocus()
        self.cbSearch.lineEdit().selectAll()

        # Set search path
        if mode & MODE_FLAG_DIRECTORY and \
           not (self.isVisible() and self.cbPath.isVisible()):
            try:
                searchPath = os.path.abspath(str(os.path.curdir))
            except OSError:  # current directory might have been deleted
                pass
            else:
                self.cbPath.setEditText(searchPath)

        # Set widgets visibility flag according to state
        widgets = (self.wSearch, self.pbPrevious, self.pbNext, self.pbSearch,
                   self.wReplace, self.wPath, self.pbReplace,
                   self.pbReplaceAll, self.pbReplaceChecked, self.wOptions,
                   self.wMask)
        #                         wSear  pbPrev pbNext pbSear wRepl  wPath  pbRep  pbRAll pbRCHK wOpti wMask
        visible = \
            {MODE_SEARCH:               (1,     1,     1,     0,     0,     0,     0,     1,     1,    1,    0,),
             MODE_REPLACE:               (1,     1,     1,     0,     1,     0,     1,     1,     0,    1,    0,),
             MODE_SEARCH_DIRECTORY:      (1,     0,     0,     1,     0,     1,     0,     0,     0,    1,    1,),
             MODE_REPLACE_DIRECTORY:     (1,     0,     0,     1,     1,     1,     0,     0,     1,    1,    1,),
             MODE_SEARCH_OPENED_FILES:   (1,     0,     0,     1,     0,     0,     0,     0,     0,    1,    1,),
             MODE_REPLACE_OPENED_FILES:  (1,     0,     0,     1,     1,     0,     0,     0,     1,    1,    1,)}

        for i, widget in enumerate(widgets):
            widget.setVisible(visible[mode][i])

        # Search next button text
        if mode == MODE_REPLACE:
            self.pbNext.setText('Next')
        else:
            self.pbNext.setText('Next↵')

        # Finaly show all with valid size
        self.show()  # show before updating widgets and labels
        self._updateLabels()
        self._updateWidgets()

    def eventFilter(self, object_, event):
        """ Event filter for mode switch tool button
        Draws icons in the search and path lineEdits
        """
        if event.type(
        ) == QEvent.Paint and object_ is self.tbCdUp:  # draw CdUp button in search path QLineEdit
            toolButton = object_
            lineEdit = self.cbPath.lineEdit()
            lineEdit.setContentsMargins(lineEdit.height(), 0, 0, 0)

            height = lineEdit.height()
            availableRect = QRect(0, 0, height, height)

            if toolButton.rect() != availableRect:
                toolButton.setGeometry(availableRect)

            painter = QPainter(toolButton)
            toolButton.icon().paint(painter, availableRect)

            return True

        elif event.type(
        ) == QEvent.KeyPress:  # Tab and Shift+Tab in QLineEdits

            if event.key() == Qt.Key_Tab:
                self._moveFocus(1)
                return True
            elif event.key() == Qt.Key_Backtab:
                self._moveFocus(-1)
                return True

        return QFrame.eventFilter(self, object_, event)

    def _onReturnPressed(self):
        """Return or Enter pressed on widget.
        Search next or Replace next
        """
        if self.pbReplace.isVisible():
            self.pbReplace.click()
        elif self.pbNext.isVisible():
            self.pbNext.click()
        elif self.pbSearch.isVisible():
            self.pbSearch.click()
        elif self.pbSearchStop.isVisible():
            self.pbSearchStop.click()

    def _onPathBackspace(self):
        """Ctrl+Backspace pressed on path.
        Remove 1 path level.
        Default behavior would be to remove one word on Linux or all on Windows
        """
        path = self.cbPath.currentText()
        if path.endswith('/') or \
           path.endswith('\\'):
            path = path[:-1]

        head, tail = os.path.split(path)
        if head and \
           head != path:
            if not head.endswith(os.sep):
                head += os.sep
            self.cbPath.lineEdit().setText(head)

    def _moveFocus(self, step):
        """Move focus forward or backward according to step.
        Standard Qt Keyboard focus algorithm doesn't allow circular navigation
        """
        allFocusableWidgets = (self.cbSearch, self.cbReplace, self.cbPath,
                               self.cbMask)
        visibleWidgets = [
            widget for widget in allFocusableWidgets if widget.isVisible()
        ]

        try:
            focusedIndex = visibleWidgets.index(QApplication.focusWidget())
        except ValueError:
            print('Invalid focused widget in Search Widget', file=sys.stderr)
            return

        nextFocusedIndex = (focusedIndex + step) % len(visibleWidgets)

        visibleWidgets[nextFocusedIndex].setFocus()
        visibleWidgets[nextFocusedIndex].lineEdit().selectAll()

    def _updateLabels(self):
        """Update 'Search' 'Replace' 'Path' labels geometry
        """
        width = 0

        if self.lSearch.isVisible():
            width = max(width, self.lSearch.minimumSizeHint().width())

        if self.lReplace.isVisible():
            width = max(width, self.lReplace.minimumSizeHint().width())

        if self.lPath.isVisible():
            width = max(width, self.lPath.minimumSizeHint().width())

        self.lSearch.setMinimumWidth(width)
        self.lReplace.setMinimumWidth(width)
        self.lPath.setMinimumWidth(width)

    def _updateWidgets(self):
        """Update geometry of widgets with buttons
        """
        width = 0

        if self.wSearchRight.isVisible():
            width = max(width, self.wSearchRight.minimumSizeHint().width())

        if self.wReplaceRight.isVisible():
            width = max(width, self.wReplaceRight.minimumSizeHint().width())

        if self.wPathRight.isVisible():
            width = max(width, self.wPathRight.minimumSizeHint().width())

        self.wSearchRight.setMinimumWidth(width)
        self.wReplaceRight.setMinimumWidth(width)
        self.wPathRight.setMinimumWidth(width)

    def updateComboBoxes(self):
        """Update comboboxes with last used texts
        """
        searchText = self.cbSearch.currentText()
        replaceText = self.cbReplace.currentText()
        maskText = self.cbMask.currentText()

        # search
        if searchText:
            index = self.cbSearch.findText(searchText)

            if index == -1:
                self.cbSearch.addItem(searchText)

        # replace
        if replaceText:
            index = self.cbReplace.findText(replaceText)

            if index == -1:
                self.cbReplace.addItem(replaceText)

        # mask
        if maskText:
            index = self.cbMask.findText(maskText)

            if index == -1:
                self.cbMask.addItem(maskText)

    def _searchPatternTextAndFlags(self):
        """Get search pattern and flags
        """
        pattern = self.cbSearch.currentText()

        pattern = pattern.replace(
            '\u2029',
            '\n')  # replace unicode paragraph separator with habitual \n

        if not self.cbRegularExpression.checkState() == Qt.Checked:
            pattern = re.escape(pattern)

        if self.cbWholeWord.checkState() == Qt.Checked:
            pattern = r'\b' + pattern + r'\b'

        flags = 0
        if not self.cbCaseSensitive.checkState() == Qt.Checked:
            flags = re.IGNORECASE
        return pattern, flags

    def getRegExp(self):
        """Read search parameters from controls and present it as a regular expression
        """
        pattern, flags = self._searchPatternTextAndFlags()
        return re.compile(pattern, flags)

    def isSearchRegExpValid(self):
        """Try to compile search pattern to check if it is valid
        Returns bool result and text error
        """
        pattern, flags = self._searchPatternTextAndFlags()
        try:
            re.compile(pattern, flags)
        except re.error as ex:
            return False, str(ex)

        return True, None

    def _getSearchMask(self):
        """Get search mask as list of patterns
        """
        mask = [s.strip() for s in self.cbMask.currentText().split(' ')]
        # remove empty
        mask = [_f for _f in mask if _f]
        return mask

    def setState(self, state):
        """Change line edit color according to search result
        """
        widget = self.cbSearch.lineEdit()

        color = {
            SearchWidget.Normal:
            QApplication.instance().palette().color(QPalette.Base),
            SearchWidget.Good:
            QColor(Qt.green),
            SearchWidget.Bad:
            QColor(Qt.red),
            SearchWidget.Incorrect:
            QColor(Qt.darkYellow)
        }

        stateColor = color[state]
        if state != SearchWidget.Normal:
            stateColor.setAlpha(100)

        pal = widget.palette()
        pal.setColor(widget.backgroundRole(), stateColor)
        widget.setPalette(pal)

    def setSearchInProgress(self, inProgress):
        """Search thread started or stopped
        """
        self.pbSearchStop.setVisible(inProgress)
        self.pbSearch.setVisible(not inProgress)
        self._updateWidgets()
        self._progress.setVisible(inProgress)

    def onSearchProgressChanged(self, value, total):
        """Signal from the thread, progress changed
        """
        self._progress.setValue(value)
        self._progress.setMaximum(total)

    def setReplaceInProgress(self, inProgress):
        """Replace thread started or stopped
        """
        self.pbReplaceCheckedStop.setVisible(inProgress)
        self.pbReplaceChecked.setVisible(not inProgress)
        self._updateWidgets()

    def setSearchInFileActionsEnabled(self, enabled):
        """Set enabled state for Next, Prev, Replace, ReplaceAll
        """
        for button in (self.pbNext, self.pbPrevious, self.pbReplace,
                       self.pbReplaceAll):
            button.setEnabled(enabled)

    def _onSearchRegExpChanged(self):
        """User edited search text or checked/unchecked checkboxes
        """
        valid, error = self.isSearchRegExpValid()
        if valid:
            self.setState(self.Normal)
            core.mainWindow().statusBar().clearMessage()
            self.pbSearch.setEnabled(len(self.getRegExp().pattern) > 0)
        else:
            core.mainWindow().statusBar().showMessage(error, 3000)
            self.setState(self.Incorrect)
            self.pbSearch.setEnabled(False)
            self.searchRegExpChanged.emit(re.compile(''))
            return

        self.searchRegExpChanged.emit(self.getRegExp())

    def _onCdUpPressed(self):
        """User pressed "Up" button, need to remove one level from search path
        """
        text = self.cbPath.currentText()
        if not os.path.exists(text):
            return

        editText = os.path.normpath(os.path.join(text, os.path.pardir))
        self.cbPath.setEditText(editText)

    def on_pbSearch_pressed(self):
        """Handler of click on "Search" button (for search in directory)
        """
        self.setState(SearchWidget.Normal)

        self.searchInDirectoryStartPressed.emit(self.getRegExp(),
                                                self._getSearchMask(),
                                                self.cbPath.currentText())

    def on_pbReplace_pressed(self):
        """Handler of click on "Replace" (in file) button
        """
        self.replaceFileOne.emit(self.cbReplace.currentText())

    def on_pbReplaceAll_pressed(self):
        """Handler of click on "Replace all" (in file) button
        """
        self.replaceFileAll.emit(self.cbReplace.currentText())

    def on_pbReplaceChecked_pressed(self):
        """Handler of click on "Replace checked" (in directory) button
        """
        self.replaceCheckedStartPressed.emit(self.cbReplace.currentText())

    def on_pbBrowse_pressed(self):
        """Handler of click on "Browse" button. Explores FS for search directory path
        """
        path = QFileDialog.getExistingDirectory(self, self.tr("Search path"),
                                                self.cbPath.currentText())

        if path:
            self.cbPath.setEditText(path)