-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Filtered views #237
Filtered views #237
Changes from all commits
7da237a
53dadb7
169c30e
86b3816
2179708
6dea716
197546c
50e3282
12aa722
bd2728e
125146e
e0829f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,34 @@ | ||
|
||
import vqt.tree as vq_tree | ||
import vqt.basics as vq_basics | ||
import vivisect.base as viv_base | ||
import envi.qt.memory as e_q_memory | ||
import visgraph.pathcore as vg_path | ||
import envi.qt.memcanvas as e_q_memcanvas | ||
import vivisect.qt.ctxmenu as v_q_ctxmenu | ||
|
||
try: | ||
from PyQt5 import QtGui | ||
from PyQt5.QtWidgets import QMenu | ||
from PyQt5.QtCore import QSortFilterProxyModel | ||
except: | ||
from PyQt4 import QtGui | ||
from PyQt4.QtGui import QMenu | ||
from PyQt4.QtCore import QSortFilterProxyModel | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At least for PyQt4 QSortFilterProxyModel should be imported from PyQt4.QtGui. Not sure about PyQt5. |
||
|
||
from vqt.main import * | ||
from vqt.common import * | ||
from vivisect.const import * | ||
|
||
class VivFilterModel(QSortFilterProxyModel): | ||
def __init__(self, parent=None): | ||
QSortFilterProxyModel.__init__(self, parent=parent) | ||
self.setDynamicSortFilter(True) | ||
self.setFilterKeyColumn(-1) | ||
|
||
def __getattr__(self, name): | ||
return getattr(self.sourceModel(), name) | ||
|
||
class VivNavModel(e_q_memory.EnviNavModel): | ||
pass | ||
|
||
|
@@ -86,8 +100,8 @@ def __init__(self, vw=None, vwqgui=None, **kwargs): | |
|
||
def doubleClickedSignal(self, idx): | ||
if idx.isValid() and self._viv_navcol != None: | ||
pnode = idx.internalPointer() | ||
expr = pnode.rowdata[self._viv_navcol] | ||
# we need to access the selected navcol, but the current index will be the cell double-clicked | ||
expr = idx.sibling(idx.row(), self._viv_navcol).data() | ||
vqtevent('envi:nav:expr', ('viv',expr,None)) | ||
return True | ||
|
||
|
@@ -130,24 +144,56 @@ def vivSetData(self, va, col, val): | |
if not pnode: | ||
return | ||
|
||
idx = self.model().createIndex(pnode.row(), col, pnode) | ||
idx = self.model().sourceModel().createIndex(pnode.row(), col, pnode) | ||
# We are *not* the edit role... | ||
self.model().setData(idx, val, role=None) | ||
self.model().sourceModel().setData(idx, val, role=QtCore.Qt.DisplayRole) | ||
|
||
def vivGetData(self, va, col): | ||
pnode = self._viv_va_nodes.get(va) | ||
if not pnode: | ||
return None | ||
return pnode.rowdata[col] | ||
|
||
class VivFilterView(QWidget): | ||
''' | ||
This is the primary window for the VQViv*Views if they want to include filtering | ||
''' | ||
window_title = '__undefined__' | ||
view_type = None | ||
|
||
def __init__(self, vw, vwqgui, *args, **kwargs): | ||
QWidget.__init__(self) | ||
|
||
self.view = self.view_type(vw, vwqgui, *args, **kwargs) | ||
self.ffilt = VQFilterWidget(self) | ||
|
||
layout = vq_basics.VBox(self.view, self.ffilt) | ||
self.setLayout(layout) | ||
|
||
self.ffilt.filterChanged.connect(self.textFilterChanged) | ||
self.setWindowTitle(self.view.window_title) | ||
|
||
def textFilterChanged(self): | ||
regExp = QtCore.QRegExp(self.ffilt.text(), | ||
self.ffilt.caseSensitivity(), | ||
self.ffilt.patternSyntax()) | ||
|
||
self.view.filterModel.setFilterRegExp(regExp) | ||
|
||
def __getattr__(self, name): | ||
return getattr(self.view, name) | ||
|
||
class VQVivLocView(VQVivTreeView): | ||
|
||
loctypes = () | ||
|
||
def __init__(self, vw, vwqgui): | ||
VQVivTreeView.__init__(self, vw, vwqgui) | ||
model = VivNavModel(self._viv_navcol, parent=self, columns=self.columns) | ||
self.setModel(model) | ||
# whether we use it or not, include a sort/filter proxy model | ||
self.navModel = VivNavModel(self._viv_navcol, parent=self, columns=self.columns) | ||
self.filterModel = VivFilterModel() | ||
self.filterModel.setSourceModel(self.navModel) | ||
self.setModel(self.filterModel) | ||
self.vqLoad() | ||
self.vqSizeColumns() | ||
|
||
|
@@ -169,7 +215,7 @@ def VWE_ADDLOCATION(self, vw, event, einfo): | |
def vivAddLocation(self, lva, lsize, ltype, linfo): | ||
print "FIXME OVERRIDE" | ||
|
||
class VQVivStringsView(VQVivLocView): | ||
class VQVivStringsViewPart(VQVivLocView): | ||
|
||
columns = ('Address','String') | ||
loctypes = (LOC_STRING, LOC_UNI) | ||
|
@@ -181,7 +227,7 @@ def vivAddLocation(self, lva, lsize, ltype, linfo): | |
s = s.decode('utf-16le', 'ignore') | ||
self.vivAddRow(lva, '0x%.8x' % lva, repr(s)) | ||
|
||
class VQVivImportsView(VQVivLocView): | ||
class VQVivImportsViewPart(VQVivLocView): | ||
|
||
columns = ('Address', 'Library', 'Function') | ||
loctypes = (LOC_IMPORT,) | ||
|
@@ -191,7 +237,7 @@ def vivAddLocation(self, lva, lsize, ltype, linfo): | |
libname, funcname = linfo.split('.', 1) | ||
self.vivAddRow(lva, '0x%.8x' % lva, libname, funcname) | ||
|
||
class VQVivStructsView(VQVivLocView): | ||
class VQVivStructsViewPart(VQVivLocView): | ||
columns = ('Address', 'Structure', 'Loc Name') | ||
loctypes = (LOC_STRUCT,) | ||
window_title = 'Structures' | ||
|
@@ -200,14 +246,17 @@ def vivAddLocation(self, lva, lsize, ltype, linfo): | |
sym = self.vw.getSymByAddr(lva) | ||
self.vivAddRow(lva, '0x%.8x' % lva, linfo, str(sym)) | ||
|
||
class VQVivExportsView(VQVivTreeView): | ||
class VQVivExportsViewPart(VQVivTreeView): | ||
|
||
window_title = 'Exports' | ||
columns = ('Address', 'File', 'Export') | ||
|
||
def __init__(self, vw, vwqgui): | ||
VQVivTreeView.__init__(self, vw, vwqgui) | ||
self.setModel( VivNavModel(self._viv_navcol, self, columns=self.columns) ) | ||
self.navModel = VivNavModel(self._viv_navcol, self, columns=self.columns) | ||
self.filterModel = VivFilterModel() | ||
self.filterModel.setSourceModel(self.navModel) | ||
self.setModel(self.filterModel) | ||
self.vqLoad() | ||
self.vqSizeColumns() | ||
|
||
|
@@ -238,15 +287,89 @@ def vqLoad(self): | |
for va, size, sname, fname in self.vw.getSegments(): | ||
self.vivAddRow(va, fname, sname, '0x%.8x' % va, str(size)) | ||
|
||
class VQVivFunctionsView(VQVivTreeView): | ||
|
||
class VQFilterWidget(QLineEdit): | ||
filterChanged = QtCore.pyqtSignal() | ||
|
||
def __init__(self, parent=None): | ||
QLineEdit.__init__(self, parent=parent) | ||
|
||
self.setClearButtonEnabled(True) | ||
self.m_patternGroup = QActionGroup(self) | ||
self.textChanged.connect(self.filterChanged) | ||
|
||
self.menu = QMenu(self) | ||
self.m_caseSensitivityAction = self.menu.addAction("Case Sensitive") | ||
self.m_caseSensitivityAction.setCheckable(True) | ||
self.m_caseSensitivityAction.toggled.connect(self.filterChanged) | ||
|
||
|
||
self.menu.addSeparator(); | ||
self.m_patternGroup.setExclusive(True); | ||
|
||
self.patternAction = self.menu.addAction("Fixed String"); | ||
self.patternAction.setData(QtCore.QVariant(int(QtCore.QRegExp.FixedString))); | ||
self.patternAction.setCheckable(True); | ||
self.patternAction.setChecked(True); | ||
self.m_patternGroup.addAction(self.patternAction); | ||
|
||
self.patternAction = self.menu.addAction("Regular Expression"); | ||
self.patternAction.setCheckable(True); | ||
self.patternAction.setData(QtCore.QVariant(int(QtCore.QRegExp.RegExp2))); | ||
self.m_patternGroup.addAction(self.patternAction); | ||
|
||
self.patternAction = self.menu.addAction("Wildcard"); | ||
self.patternAction.setCheckable(True); | ||
self.patternAction.setData(QtCore.QVariant(int(QtCore.QRegExp.Wildcard))); | ||
self.m_patternGroup.addAction(self.patternAction); | ||
|
||
self.m_patternGroup.triggered.connect(self.filterChanged); | ||
|
||
self.icon = QtGui.QIcon(QtGui.QPixmap(":/images/find.png")) | ||
self.optionsButton = QToolButton() | ||
self.optionsButton.setCursor(QtCore.Qt.ArrowCursor) | ||
self.optionsButton.setFocusPolicy(QtCore.Qt.NoFocus) | ||
self.optionsButton.setStyleSheet("* { border: none; }") | ||
self.optionsButton.setIcon(self.icon) | ||
self.optionsButton.setMenu(self.menu) | ||
self.optionsButton.setPopupMode(QToolButton.InstantPopup) | ||
|
||
self.optionsAction = QWidgetAction(self) | ||
self.optionsAction.setDefaultWidget(self.optionsButton) | ||
self.addAction(self.optionsAction, QLineEdit.LeadingPosition) | ||
|
||
def caseSensitivity(self): | ||
return (QtCore.Qt.CaseInsensitive, QtCore.Qt.CaseSensitive)[self.m_caseSensitivityAction.isChecked()] | ||
|
||
def setCaseSensitivity(self, cs): | ||
self.m_caseSensitivityAction.setChecked(cs == QtCore.Qt.CaseSensitive) | ||
|
||
def patternSyntax(self): | ||
return self.patternSyntaxFromAction(self.m_patternGroup.checkedAction()) | ||
|
||
def setPatternSyntax(self, s): | ||
for a in self.m_patternGroup.actions(): | ||
if (self.patternSyntaxFromAction(a) == s): | ||
a.setChecked(True) | ||
break | ||
|
||
def patternSyntaxFromAction(self, a): | ||
return int(a.data()) | ||
|
||
class VQVivFunctionsViewPart(VQVivTreeView): | ||
|
||
_viv_navcol = 0 | ||
window_title = 'Functions' | ||
columns = ('Name','Address', 'Size', 'Ref Count') | ||
|
||
def __init__(self, vw, vwqgui): | ||
VQVivTreeView.__init__(self, vw, vwqgui) | ||
self.setModel( VivNavModel(self._viv_navcol, self, columns=self.columns) ) | ||
VQVivTreeView.__init__(self, vw, vwqgui, withfilter=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tried to open the function listing
Might need to pop the withfilter kwarg in VQVivTreeView. |
||
|
||
self.navModel = VivNavModel(self._viv_navcol, self, columns=self.columns) | ||
self.filterModel = VivFilterModel() | ||
self.filterModel.setSourceModel(self.navModel) | ||
self.setModel(self.filterModel) | ||
|
||
self.vqLoad() | ||
self.vqSizeColumns() | ||
|
||
|
@@ -347,12 +470,14 @@ def reprComplex(vw, item): | |
VASET_COMPLEX: reprComplex, | ||
} | ||
|
||
class VQVivVaSetView(VQVivTreeView): | ||
class VQVivVaSetViewPart(VQVivTreeView): | ||
|
||
window_title = 'Va Set' | ||
_viv_navcol = 0 | ||
|
||
def __init__(self, vw, vwqgui, setname): | ||
self._va_setname = setname | ||
self.window_title = 'Va Set: %s' % setname | ||
|
||
setdef = vw.getVaSetDef( setname ) | ||
cols = [ cname for (cname,ctype) in setdef ] | ||
|
@@ -362,7 +487,6 @@ def __init__(self, vw, vwqgui, setname): | |
self.setModel( VivNavModel(self._viv_navcol, self, columns=cols) ) | ||
self.vqLoad() | ||
self.vqSizeColumns() | ||
self.setWindowTitle('Va Set: %s' % setname) | ||
|
||
def VWE_SETVASETROW(self, vw, event, einfo): | ||
setname, row = einfo | ||
|
@@ -395,7 +519,7 @@ def reprRow(self, row): | |
|
||
return row | ||
|
||
class VQXrefView(VQVivTreeView): | ||
class VQXrefViewPart(VQVivTreeView): | ||
|
||
_viv_navcol = 0 | ||
|
||
|
@@ -416,15 +540,18 @@ def __init__(self, vw, vwqgui, xrefs=(), title='Xrefs'): | |
|
||
self.vqSizeColumns() | ||
|
||
class VQVivNamesView(VQVivTreeView): | ||
class VQVivNamesViewPart(VQVivTreeView): | ||
|
||
_viv_navcol = 0 | ||
window_title = 'Workspace Names' | ||
columns = ('Address', 'Name') | ||
|
||
def __init__(self, vw, vwqgui): | ||
VQVivTreeView.__init__(self, vw, vwqgui) | ||
self.setModel( VivNavModel(self._viv_navcol, self, columns=self.columns) ) | ||
self.navModel = VivNavModel(self._viv_navcol, self, columns=self.columns) | ||
self.filterModel = VivFilterModel() | ||
self.filterModel.setSourceModel(self.navModel) | ||
self.setModel(self.filterModel) | ||
self.vqLoad() | ||
self.vqSizeColumns() | ||
|
||
|
@@ -444,3 +571,32 @@ def vivAddName(self, nifo): | |
else: | ||
self.vivSetData(va, 1, name) | ||
|
||
|
||
#FIXME: is this a good time to use a @decorator? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could introduce a wrapper that sets an instance attribute on every instance of these classes (not sure how you'd do it at the class attribute level), but you'd have to wrap the every instance of the class you want, so it adds a layer of indirection, and the actual wrapping introduces more complexity. I'd say pass for now and remove the FIXME. If we every have to add more of these, it might be worth adding, but right now it's more complexity than what is necessary, and adds a fair bit of overhead. |
||
# Filtering VQTreeViews | ||
class VQVivFunctionsView(VivFilterView): | ||
view_type = VQVivFunctionsViewPart | ||
|
||
class VQVivNamesView(VivFilterView): | ||
view_type = VQVivNamesViewPart | ||
|
||
class VQVivExportsView(VivFilterView): | ||
view_type = VQVivExportsViewPart | ||
|
||
class VQVivVaSetView(VivFilterView): | ||
view_type = VQVivVaSetViewPart | ||
|
||
class VQXrefView(VivFilterView): | ||
view_type = VQXrefViewPart | ||
|
||
|
||
# Filtering VQVivLocViews | ||
class VQVivStringsView(VivFilterView): | ||
view_type = VQVivStringsViewPart | ||
|
||
class VQVivImportsView(VivFilterView): | ||
view_type = VQVivImportsViewPart | ||
|
||
class VQVivStructsView(VivFilterView): | ||
view_type = VQVivStructsViewPart | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these are good catches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow, these should be applied before we get back to this PR. i'll probably put these in a smaller PR