#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
**searchAndReplace.py**
**Platform:**
Windows, Linux, Mac Os X.
**Description:**
Defines the :class:`SearchAndReplace` class.
**Others:**
"""
#**********************************************************************************************************************
#*** Future imports.
#**********************************************************************************************************************
from __future__ import unicode_literals
#**********************************************************************************************************************
#*** External imports.
#**********************************************************************************************************************
import functools
import os
from PyQt4.QtCore import QChar
from PyQt4.QtCore import QObject
from PyQt4.QtCore import QEvent
from PyQt4.QtCore import QString
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QComboBox
#**********************************************************************************************************************
#*** Internal imports.
#**********************************************************************************************************************
import foundations.common
import foundations.exceptions
import foundations.strings
import foundations.ui.common
import foundations.verbose
import umbra.ui.common
from umbra.components.factory.scriptEditor.models import PatternsModel
from umbra.components.factory.scriptEditor.nodes import PatternNode
#**********************************************************************************************************************
#*** Module attributes.
#**********************************************************************************************************************
__author__ = "Thomas Mansencal"
__copyright__ = "Copyright (C) 2008 - 2014 - Thomas Mansencal"
__license__ = "GPL V3.0 - http://www.gnu.org/licenses/"
__maintainer__ = "Thomas Mansencal"
__email__ = "[email protected]"
__status__ = "Production"
__all__ = ["LOGGER", "UI_FILE", "SearchAndReplace"]
LOGGER = foundations.verbose.installLogger()
UI_FILE = os.path.join(os.path.dirname(__file__), "ui", "Search_And_Replace.ui")
#**********************************************************************************************************************
#*** Module classes and definitions.
#**********************************************************************************************************************
[docs]class ValidationFilter(QObject):
"""
Defines a `QObject <http://doc.qt.nokia.com/qobject.html>`_ subclass used as an event filter
for the :class:`SearchAndReplace` class.
"""
[docs] def eventFilter(self, object, event):
"""
Reimplements the **QObject.eventFilter** method.
:param object: Object.
:type object: QObject
:param event: Event.
:type event: QEvent
:return: Event filtered.
:rtype: bool
"""
if event.type() == QEvent.KeyPress:
if event.key() in (Qt.Key_Enter, Qt.Key_Return):
object.search()
elif event.key() in (Qt.Key_Escape,):
object.close()
return True
else:
return QObject.eventFilter(self, object, event)
[docs]class SearchAndReplace(foundations.ui.common.QWidgetFactory(uiFile=UI_FILE)):
"""
Defines the default search and replace dialog used by the **ScriptEditor** Component.
"""
def __init__(self, parent, *args, **kwargs):
"""
Initializes the class.
:param parent: Object parent.
:type parent: QObject
:param \*args: Arguments.
:type \*args: \*
:param \*\*kwargs: Keywords arguments.
:type \*\*kwargs: \*\*
"""
LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__))
super(SearchAndReplace, self).__init__(parent, *args, **kwargs)
# --- Setting class attributes. ---
self.__container = parent
self.__searchPatternsModel = None
self.__replaceWithPatternsModel = None
self.__maximumStoredPatterns = 15
SearchAndReplace.__initializeUi(self)
#******************************************************************************************************************
#*** Attributes properties.
#******************************************************************************************************************
@property
def container(self):
"""
Property for **self.__container** attribute.
:return: self.__container.
:rtype: QObject
"""
return self.__container
@container.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def container(self, value):
"""
Setter for **self.__container** attribute.
:param value: Attribute value.
:type value: QObject
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "container"))
@container.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def container(self):
"""
Deleter for **self.__container** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "container"))
@property
def searchPatternsModel(self):
"""
Property for **self.__searchPatternsModel** attribute.
:return: self.__searchPatternsModel.
:rtype: PatternsModel
"""
return self.__searchPatternsModel
@searchPatternsModel.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def searchPatternsModel(self, value):
"""
Setter for **self.__searchPatternsModel** attribute.
:param value: Attribute value.
:type value: PatternsModel
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "searchPatternsModel"))
@searchPatternsModel.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def searchPatternsModel(self):
"""
Deleter for **self.__searchPatternsModel** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "searchPatternsModel"))
@property
def replaceWithPatternsModel(self):
"""
Property for **self.__replaceWithPatternsModel** attribute.
:return: self.__replaceWithPatternsModel.
:rtype: PatternsModel
"""
return self.__replaceWithPatternsModel
@replaceWithPatternsModel.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def replaceWithPatternsModel(self, value):
"""
Setter for **self.__replaceWithPatternsModel** attribute.
:param value: Attribute value.
:type value: PatternsModel
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "replaceWithPatternsModel"))
@replaceWithPatternsModel.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def replaceWithPatternsModel(self):
"""
Deleter for **self.__replaceWithPatternsModel** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "replaceWithPatternsModel"))
@property
def maximumStoredPatterns(self):
"""
Property for **self.__maximumStoredPatterns** attribute.
:return: self.__maximumStoredPatterns.
:rtype: int
"""
return self.__maximumStoredPatterns
@maximumStoredPatterns.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def maximumStoredPatterns(self, value):
"""
Setter for **self.__maximumStoredPatterns** attribute.
:param value: Attribute value.
:type value: int
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "maximumStoredPatterns"))
@maximumStoredPatterns.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def maximumStoredPatterns(self):
"""
Deleter for **self.__maximumStoredPatterns** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "maximumStoredPatterns"))
#******************************************************************************************************************
#*** Class methods.
#******************************************************************************************************************
[docs] def show(self):
"""
Reimplements the :meth:`QWidget.show` method.
"""
selectedText = self.__container.getCurrentEditor().getSelectedText()
selectedText and self.insertPattern(selectedText, self.__searchPatternsModel)
self.Search_comboBox.lineEdit().selectAll()
self.Search_comboBox.setFocus()
super(SearchAndReplace, self).show()
self.raise_()
def __initializeUi(self):
"""
Initializes the Widget ui.
"""
umbra.ui.common.setWindowDefaultIcon(self)
for model, settingsKey, comboBox in \
(("_SearchAndReplace__searchPatternsModel", "recentSearchPatterns", self.Search_comboBox),
("_SearchAndReplace__replaceWithPatternsModel", "recentReplaceWithPatterns", self.Replace_With_comboBox)):
self.__dict__[model] = PatternsModel()
patterns = foundations.common.orderedUniqify([foundations.strings.toString(pattern) for pattern in \
self.__container.settings.getKey(self.__container.settingsSection,
settingsKey).toStringList()])
[PatternNode(parent=self.__dict__[model].rootNode, name=pattern) \
for pattern in patterns[:self.__maximumStoredPatterns]]
comboBox.setInsertPolicy(QComboBox.InsertAtTop)
comboBox.setModel(self.__dict__[model])
comboBox.completer().setCaseSensitivity(Qt.CaseSensitive)
# Signals / Slots.
self.__dict__[model].patternInserted.connect(
functools.partial(self.__patternsModel__patternInserted, settingsKey, comboBox))
self.Wrap_Around_checkBox.setChecked(True)
self.installEventFilter(ValidationFilter(self))
# Signals / Slots.
self.Search_pushButton.clicked.connect(self.__Search_pushButton__clicked)
self.Replace_pushButton.clicked.connect(self.__Replace_pushButton__clicked)
self.Replace_All_pushButton.clicked.connect(self.__Replace_All_pushButton__clicked)
self.Close_pushButton.clicked.connect(self.__Close_pushButton__clicked)
def __patternsModel__patternInserted(self, settingsKey, comboBox, index):
"""
Defines the slot triggered by a pattern when inserted into a patterns Model.
:param settingsKey: Pattern Model settings key.
:type settingsKey: unicode
:param comboBox: Pattern Model attached comboBox.
:type comboBox: QComboBox
:param index: Inserted pattern index.
:type index: QModelIndex
"""
patternsModel = self.sender()
LOGGER.debug("> Storing '{0}' model patterns in '{1}' settings key.".format(patternsModel, settingsKey))
self.__container.settings.setKey(self.__container.settingsSection,
settingsKey,
[patternNode.name for patternNode in \
patternsModel.rootNode.children[:self.maximumStoredPatterns]])
comboBox.setCurrentIndex(index.row())
def __Search_pushButton__clicked(self, checked):
"""
Defines the slot triggered by **Search_pushButton** Widget when clicked.
:param checked: Checked state.
:type checked: bool
"""
self.search()
def __Replace_pushButton__clicked(self, checked):
"""
Defines the slot triggered by **Replace_pushButton** Widget when clicked.
:param checked: Checked state.
:type checked: bool
"""
self.replace()
def __Replace_All_pushButton__clicked(self, checked):
"""
Defines the slot triggered by **Replace_All_pushButton** Widget when clicked.
:param checked: Checked state.
:type checked: bool
"""
self.replaceAll()
def __Close_pushButton__clicked(self, checked):
"""
Defines the slot triggered by **Close_pushButton** Widget when clicked.
:param checked: Checked state.
:type checked: bool
"""
self.close()
def __getSettings(self):
"""
Returns the current search and replace settings.
:return: Settings.
:rtype: dict
"""
return {"caseSensitive" : self.Case_Sensitive_checkBox.isChecked(),
"wholeWord" : self.Whole_Word_checkBox.isChecked(),
"regularExpressions" : self.Regular_Expressions_checkBox.isChecked(),
"backwardSearch" : self.Backward_Search_checkBox.isChecked(),
"wrapAround" : self.Wrap_Around_checkBox.isChecked()}
# Oncilla: Statement commented by auto-documentation process:
# Oncilla: Statement commented by auto-documentation process: @staticmethod
[docs] def insertPattern(pattern, model, index=0):
"""
Inserts given pattern into given Model.
:param pattern: Pattern.
:type pattern: unicode
:param model: Model.
:type model: PatternsModel
:param index: Insertion indes.
:type index: int
:return: Method success.
:rtype: bool
"""
if not pattern:
return False
pattern = pattern.replace(QChar(QChar.ParagraphSeparator), QString("\n"))
pattern = foundations.common.getFirstItem(foundations.strings.toString(pattern).split("\n"))
model.insertPattern(foundations.strings.toString(pattern), index)
return True
[docs] def search(self):
"""
Searchs current editor Widget for search pattern.
:return: Method success.
:rtype: bool
"""
editor = self.__container.getCurrentEditor()
searchPattern = self.Search_comboBox.currentText()
replacementPattern = self.Replace_With_comboBox.currentText()
if not editor or not searchPattern:
return False
self.insertPattern(searchPattern, self.__searchPatternsModel)
self.insertPattern(replacementPattern, self.__replaceWithPatternsModel)
settings = self.__getSettings()
LOGGER.debug("> 'Search' on '{0}' search pattern with '{1}' settings.".format(searchPattern, settings))
return editor.search(searchPattern, **settings)
[docs] def replace(self):
"""
Replaces current editor Widget current search pattern occurence with replacement pattern.
:return: Method success.
:rtype: bool
"""
editor = self.__container.getCurrentEditor()
searchPattern = self.Search_comboBox.currentText()
replacementPattern = self.Replace_With_comboBox.currentText()
if not editor or not searchPattern:
return False
self.insertPattern(searchPattern, self.__searchPatternsModel)
self.insertPattern(replacementPattern, self.__replaceWithPatternsModel)
settings = self.__getSettings()
LOGGER.debug("> 'Replace' on search '{0}' pattern, '{1}' replacement pattern with '{2}' settings.".format(
searchPattern, replacementPattern, settings))
return editor.replace(searchPattern, replacementPattern, **settings)
[docs] def replaceAll(self):
"""
Replaces current editor Widget search pattern occurrences with replacement pattern.
:return: Method success.
:rtype: bool
"""
editor = self.__container.getCurrentEditor()
searchPattern = self.Search_comboBox.currentText()
replacementPattern = self.Replace_With_comboBox.currentText()
if not editor or not searchPattern:
return False
self.insertPattern(searchPattern, self.__searchPatternsModel)
self.insertPattern(replacementPattern, self.__replaceWithPatternsModel)
settings = self.__getSettings()
settings.update({"backwardSearch" : False,
"wrapAround" : False})
LOGGER.debug("> 'Replace All' on search '{0}' pattern, '{1}' replacement pattern with '{2}' settings.".format(
searchPattern, replacementPattern, settings))
return editor.replaceAll(searchPattern, replacementPattern, **settings)