2017-12-23 16:42:42 +00:00
# include "ClassesWidget.h"
2019-02-22 16:50:45 +00:00
# include "core/MainWindow.h"
2017-12-23 16:42:42 +00:00
# include "ui_ClassesWidget.h"
2018-10-17 07:55:53 +00:00
# include "common/Helpers.h"
2019-03-14 09:28:42 +00:00
# include "common/SvgIconEngine.h"
2018-08-17 11:27:07 +00:00
# include "dialogs/EditMethodDialog.h"
2019-02-07 12:40:41 +00:00
# include "dialogs/RenameDialog.h"
2017-12-23 16:42:42 +00:00
2019-03-14 09:28:42 +00:00
# include <QList>
2018-08-17 11:27:07 +00:00
# include <QMenu>
2019-03-14 09:28:42 +00:00
# include <QMouseEvent>
2018-08-17 11:27:07 +00:00
QVariant ClassesModel : : headerData ( int section , Qt : : Orientation , int role ) const
{
switch ( role ) {
case Qt : : DisplayRole :
switch ( section ) {
case NAME :
return tr ( " Name " ) ;
case TYPE :
return tr ( " Type " ) ;
case OFFSET :
return tr ( " Offset " ) ;
case VTABLE :
return tr ( " VTable " ) ;
default :
return QVariant ( ) ;
}
default :
return QVariant ( ) ;
}
}
BinClassesModel : : BinClassesModel ( QObject * parent )
: ClassesModel ( parent )
2017-12-23 16:42:42 +00:00
{
}
2018-08-17 11:27:07 +00:00
void BinClassesModel : : setClasses ( const QList < BinClassDescription > & classes )
{
beginResetModel ( ) ;
this - > classes = classes ;
endResetModel ( ) ;
}
2017-12-23 16:42:42 +00:00
2018-08-17 11:27:07 +00:00
QModelIndex BinClassesModel : : index ( int row , int column , const QModelIndex & parent ) const
2017-12-23 16:42:42 +00:00
{
2019-02-01 14:51:29 +00:00
if ( ! parent . isValid ( ) ) {
2017-12-23 16:42:42 +00:00
return createIndex ( row , column , ( quintptr ) 0 ) ; // root function nodes have id = 0
2019-02-01 14:51:29 +00:00
}
2017-12-23 16:42:42 +00:00
return createIndex ( row , column , ( quintptr ) parent . row ( ) + 1 ) ; // sub-nodes have id = class index + 1
}
2018-08-17 11:27:07 +00:00
QModelIndex BinClassesModel : : parent ( const QModelIndex & index ) const
2017-12-23 16:42:42 +00:00
{
2019-02-01 14:51:29 +00:00
if ( ! index . isValid ( ) ) {
return { } ;
}
2017-12-23 16:42:42 +00:00
2019-02-01 14:51:29 +00:00
if ( index . internalId ( ) = = 0 ) { // root function node
return { } ;
} else { // sub-node
2017-12-23 16:42:42 +00:00
return this - > index ( ( int ) ( index . internalId ( ) - 1 ) , 0 ) ;
2019-02-01 14:51:29 +00:00
}
2017-12-23 16:42:42 +00:00
}
2018-08-17 11:27:07 +00:00
int BinClassesModel : : rowCount ( const QModelIndex & parent ) const
2017-12-23 16:42:42 +00:00
{
2018-03-21 20:32:32 +00:00
if ( ! parent . isValid ( ) ) { // root
2018-08-17 11:27:07 +00:00
return classes . count ( ) ;
2017-12-23 16:42:42 +00:00
}
2018-03-21 20:32:32 +00:00
if ( parent . internalId ( ) = = 0 ) { // methods/fields
2018-08-17 11:27:07 +00:00
const BinClassDescription * cls = & classes . at ( parent . row ( ) ) ;
return cls - > baseClasses . length ( ) + cls - > methods . length ( ) + cls - > fields . length ( ) ;
2017-12-23 16:42:42 +00:00
}
return 0 ; // below methods/fields
}
2018-08-17 11:27:07 +00:00
int BinClassesModel : : columnCount ( const QModelIndex & ) const
2017-12-23 16:42:42 +00:00
{
return Columns : : COUNT ;
}
2018-08-17 11:27:07 +00:00
QVariant BinClassesModel : : data ( const QModelIndex & index , int role ) const
2017-12-23 16:42:42 +00:00
{
2018-08-17 11:27:07 +00:00
const BinClassDescription * cls ;
2019-01-31 16:45:58 +00:00
const BinClassMethodDescription * meth = nullptr ;
const BinClassFieldDescription * field = nullptr ;
const BinClassBaseClassDescription * base = nullptr ;
2018-03-21 20:32:32 +00:00
if ( index . internalId ( ) = = 0 ) { // class row
2018-08-17 11:27:07 +00:00
if ( index . row ( ) > = classes . count ( ) ) {
2017-12-23 16:42:42 +00:00
return QVariant ( ) ;
}
2018-08-17 11:27:07 +00:00
cls = & classes . at ( index . row ( ) ) ;
} else { // method/field/base row
cls = & classes . at ( static_cast < int > ( index . internalId ( ) - 1 ) ) ;
2017-12-23 16:42:42 +00:00
2018-08-17 11:27:07 +00:00
if ( index . row ( ) > = cls - > baseClasses . length ( ) + cls - > methods . length ( ) + cls - > fields . length ( ) ) {
2017-12-23 16:42:42 +00:00
return QVariant ( ) ;
}
2018-08-17 11:27:07 +00:00
if ( index . row ( ) < cls - > baseClasses . length ( ) ) {
base = & cls - > baseClasses [ index . row ( ) ] ;
} else if ( index . row ( ) - cls - > baseClasses . length ( ) < cls - > methods . length ( ) ) {
meth = & cls - > methods [ index . row ( ) - cls - > baseClasses . length ( ) ] ;
2018-03-21 20:32:32 +00:00
} else {
2018-08-17 11:27:07 +00:00
field = & cls - > fields [ index . row ( ) - cls - > baseClasses . length ( ) - cls - > methods . length ( ) ] ;
2017-12-23 16:42:42 +00:00
}
}
2018-03-21 20:32:32 +00:00
if ( meth ) {
switch ( role ) {
case Qt : : DisplayRole :
switch ( index . column ( ) ) {
case NAME :
2017-12-23 16:42:42 +00:00
return meth - > name ;
2018-03-21 20:32:32 +00:00
case TYPE :
return tr ( " method " ) ;
case OFFSET :
2018-08-17 11:27:07 +00:00
return meth - > addr = = RVA_INVALID ? QString ( ) : RAddressString ( meth - > addr ) ;
case VTABLE :
return meth - > vtableOffset < 0 ? QString ( ) : QString ( " +%1 " ) . arg ( meth - > vtableOffset ) ;
2017-12-23 16:42:42 +00:00
default :
return QVariant ( ) ;
2018-03-21 20:32:32 +00:00
}
case OffsetRole :
return QVariant : : fromValue ( meth - > addr ) ;
case NameRole :
return meth - > name ;
case TypeRole :
2019-02-01 14:51:29 +00:00
return QVariant : : fromValue ( RowType : : Method ) ;
2018-03-21 20:32:32 +00:00
default :
return QVariant ( ) ;
2017-12-23 16:42:42 +00:00
}
2018-03-21 20:32:32 +00:00
} else if ( field ) {
switch ( role ) {
case Qt : : DisplayRole :
switch ( index . column ( ) ) {
case NAME :
2017-12-23 16:42:42 +00:00
return field - > name ;
2018-03-21 20:32:32 +00:00
case TYPE :
return tr ( " field " ) ;
case OFFSET :
2018-08-17 11:27:07 +00:00
return field - > addr = = RVA_INVALID ? QString ( ) : RAddressString ( field - > addr ) ;
2017-12-23 16:42:42 +00:00
default :
return QVariant ( ) ;
2018-03-21 20:32:32 +00:00
}
case OffsetRole :
return QVariant : : fromValue ( field - > addr ) ;
case NameRole :
return field - > name ;
case TypeRole :
2019-02-01 14:51:29 +00:00
return QVariant : : fromValue ( RowType : : Field ) ;
2018-03-21 20:32:32 +00:00
default :
return QVariant ( ) ;
2017-12-23 16:42:42 +00:00
}
2018-08-17 11:27:07 +00:00
} else if ( base ) {
switch ( role ) {
case Qt : : DisplayRole :
switch ( index . column ( ) ) {
case NAME :
return base - > name ;
case TYPE :
return tr ( " base class " ) ;
case OFFSET :
return QString ( " +%1 " ) . arg ( base - > offset ) ;
default :
return QVariant ( ) ;
}
case NameRole :
return base - > name ;
case TypeRole :
2019-02-01 14:51:29 +00:00
return QVariant : : fromValue ( RowType : : Base ) ;
2018-08-17 11:27:07 +00:00
default :
return QVariant ( ) ;
}
}
else {
2018-03-21 20:32:32 +00:00
switch ( role ) {
case Qt : : DisplayRole :
switch ( index . column ( ) ) {
case NAME :
2017-12-23 16:42:42 +00:00
return cls - > name ;
2018-03-21 20:32:32 +00:00
case TYPE :
return tr ( " class " ) ;
case OFFSET :
2018-08-17 11:27:07 +00:00
return cls - > addr = = RVA_INVALID ? QString ( ) : RAddressString ( cls - > addr ) ;
case VTABLE :
return cls - > vtableAddr = = RVA_INVALID ? QString ( ) : RAddressString ( cls - > vtableAddr ) ;
2017-12-23 16:42:42 +00:00
default :
return QVariant ( ) ;
2018-03-21 20:32:32 +00:00
}
case OffsetRole :
return QVariant : : fromValue ( cls - > addr ) ;
case NameRole :
return cls - > name ;
case TypeRole :
2019-02-01 14:51:29 +00:00
return QVariant : : fromValue ( RowType : : Class ) ;
2018-03-21 20:32:32 +00:00
default :
return QVariant ( ) ;
2017-12-23 16:42:42 +00:00
}
}
}
2018-08-17 11:27:07 +00:00
2019-02-07 10:42:53 +00:00
AnalClassesModel : : AnalClassesModel ( CutterDockWidget * parent )
2019-02-01 14:51:29 +00:00
: ClassesModel ( parent ) , attrs ( new QMap < QString , QVector < Attribute > > )
2017-12-23 16:42:42 +00:00
{
2019-02-07 10:42:53 +00:00
// Just use a simple refresh deferrer. If an event was triggered in the background, simply refresh everything later.
refreshDeferrer = parent - > createRefreshDeferrer ( [ this ] ( ) {
this - > refreshAll ( ) ;
} ) ;
connect ( Core ( ) , & CutterCore : : refreshAll , this , & AnalClassesModel : : refreshAll ) ;
connect ( Core ( ) , & CutterCore : : classNew , this , & AnalClassesModel : : classNew ) ;
connect ( Core ( ) , & CutterCore : : classDeleted , this , & AnalClassesModel : : classDeleted ) ;
connect ( Core ( ) , & CutterCore : : classRenamed , this , & AnalClassesModel : : classRenamed ) ;
connect ( Core ( ) , & CutterCore : : classAttrsChanged , this , & AnalClassesModel : : classAttrsChanged ) ;
refreshAll ( ) ;
2018-08-17 11:27:07 +00:00
}
2019-02-07 10:42:53 +00:00
void AnalClassesModel : : refreshAll ( )
2018-08-17 11:27:07 +00:00
{
2019-02-07 10:42:53 +00:00
if ( ! refreshDeferrer - > attemptRefresh ( nullptr ) ) {
return ;
}
2018-08-17 11:27:07 +00:00
beginResetModel ( ) ;
2019-02-02 10:59:58 +00:00
attrs - > clear ( ) ;
2019-02-07 10:42:53 +00:00
classes = Core ( ) - > getAllAnalClasses ( true ) ; // must be sorted
2018-08-17 11:27:07 +00:00
endResetModel ( ) ;
}
2019-02-07 10:42:53 +00:00
void AnalClassesModel : : classNew ( const QString & cls )
{
if ( ! refreshDeferrer - > attemptRefresh ( nullptr ) ) {
return ;
}
// find the destination position using binary search and add the row
auto it = std : : lower_bound ( classes . begin ( ) , classes . end ( ) , cls ) ;
int index = it - classes . begin ( ) ;
beginInsertRows ( QModelIndex ( ) , index , index ) ;
classes . insert ( it , cls ) ;
endInsertRows ( ) ;
}
void AnalClassesModel : : classDeleted ( const QString & cls )
{
if ( ! refreshDeferrer - > attemptRefresh ( nullptr ) ) {
return ;
}
// find the position using binary search and remove the row
auto it = std : : lower_bound ( classes . begin ( ) , classes . end ( ) , cls ) ;
if ( it = = classes . end ( ) | | * it ! = cls ) {
return ;
}
int index = it - classes . begin ( ) ;
beginRemoveRows ( QModelIndex ( ) , index , index ) ;
classes . erase ( it ) ;
endRemoveRows ( ) ;
}
void AnalClassesModel : : classRenamed ( const QString & oldName , const QString & newName )
{
if ( ! refreshDeferrer - > attemptRefresh ( nullptr ) ) {
return ;
}
auto oldIt = std : : lower_bound ( classes . begin ( ) , classes . end ( ) , oldName ) ;
if ( oldIt = = classes . end ( ) | | * oldIt ! = oldName ) {
return ;
}
auto newIt = std : : lower_bound ( classes . begin ( ) , classes . end ( ) , newName ) ;
int oldRow = oldIt - classes . begin ( ) ;
int newRow = newIt - classes . begin ( ) ;
// oldRow == newRow means the name stayed the same.
// oldRow == newRow - 1 means the name changed, but the row stays the same.
if ( oldRow ! = newRow & & oldRow ! = newRow - 1 ) {
beginMoveRows ( QModelIndex ( ) , oldRow , oldRow , QModelIndex ( ) , newRow ) ;
classes . erase ( oldIt ) ;
// iterators are invalid now, so we calculate the new position from the rows.
if ( oldRow < newRow ) {
// if we move down, we need to account for the removed old element above.
newRow - - ;
}
classes . insert ( newRow , newName ) ;
endMoveRows ( ) ;
} else if ( oldRow = = newRow - 1 ) { // class name changed, but not the row
newRow - - ;
classes [ newRow ] = newName ;
}
emit dataChanged ( index ( newRow , 0 ) , index ( newRow , 0 ) ) ;
}
void AnalClassesModel : : classAttrsChanged ( const QString & cls )
{
if ( ! refreshDeferrer - > attemptRefresh ( nullptr ) ) {
return ;
}
auto it = std : : lower_bound ( classes . begin ( ) , classes . end ( ) , cls ) ;
if ( it = = classes . end ( ) | | * it ! = cls ) {
return ;
}
QPersistentModelIndex persistentIndex = QPersistentModelIndex ( index ( it - classes . begin ( ) , 0 ) ) ;
layoutAboutToBeChanged ( { persistentIndex } ) ;
attrs - > remove ( cls ) ;
layoutChanged ( { persistentIndex } ) ;
}
2019-02-01 14:51:29 +00:00
const QVector < AnalClassesModel : : Attribute > & AnalClassesModel : : getAttrs ( const QString & cls ) const
{
auto it = attrs - > find ( cls ) ;
if ( it ! = attrs - > end ( ) ) {
return it . value ( ) ;
}
QVector < AnalClassesModel : : Attribute > clsAttrs ;
QList < AnalBaseClassDescription > bases = Core ( ) - > getAnalClassBaseClasses ( cls ) ;
QList < AnalMethodDescription > meths = Core ( ) - > getAnalClassMethods ( cls ) ;
QList < AnalVTableDescription > vtables = Core ( ) - > getAnalClassVTables ( cls ) ;
clsAttrs . reserve ( bases . size ( ) + meths . size ( ) + vtables . size ( ) ) ;
for ( const AnalBaseClassDescription & base : bases ) {
clsAttrs . push_back ( Attribute ( Attribute : : Type : : Base , QVariant : : fromValue ( base ) ) ) ;
}
for ( const AnalVTableDescription & vtable : vtables ) {
clsAttrs . push_back ( Attribute ( Attribute : : Type : : VTable , QVariant : : fromValue ( vtable ) ) ) ;
}
for ( const AnalMethodDescription & meth : meths ) {
clsAttrs . push_back ( Attribute ( Attribute : : Type : : Method , QVariant : : fromValue ( meth ) ) ) ;
}
return attrs - > insert ( cls , clsAttrs ) . value ( ) ;
}
2018-08-17 11:27:07 +00:00
QModelIndex AnalClassesModel : : index ( int row , int column , const QModelIndex & parent ) const
{
2019-02-01 14:51:29 +00:00
if ( ! parent . isValid ( ) ) {
2018-08-17 11:27:07 +00:00
return createIndex ( row , column , ( quintptr ) 0 ) ; // root function nodes have id = 0
2019-02-01 14:51:29 +00:00
}
2018-08-17 11:27:07 +00:00
return createIndex ( row , column , ( quintptr ) parent . row ( ) + 1 ) ; // sub-nodes have id = class index + 1
}
QModelIndex AnalClassesModel : : parent ( const QModelIndex & index ) const
{
2019-02-01 14:51:29 +00:00
if ( ! index . isValid ( ) ) {
return { } ;
}
2018-08-17 11:27:07 +00:00
2019-02-01 14:51:29 +00:00
if ( index . internalId ( ) = = 0 ) { // root function node
return { } ;
} else { // sub-node
2018-08-17 11:27:07 +00:00
return this - > index ( ( int ) ( index . internalId ( ) - 1 ) , 0 ) ;
2019-02-01 14:51:29 +00:00
}
2018-08-17 11:27:07 +00:00
}
int AnalClassesModel : : rowCount ( const QModelIndex & parent ) const
{
if ( ! parent . isValid ( ) ) { // root
return classes . count ( ) ;
}
if ( parent . internalId ( ) = = 0 ) { // methods/fields
2019-02-01 14:51:29 +00:00
return getAttrs ( classes [ parent . row ( ) ] ) . size ( ) ;
2018-08-17 11:27:07 +00:00
}
return 0 ; // below methods/fields
}
bool AnalClassesModel : : hasChildren ( const QModelIndex & parent ) const
{
2019-02-01 14:51:29 +00:00
return ! parent . isValid ( ) | | ! parent . parent ( ) . isValid ( ) ;
2018-08-17 11:27:07 +00:00
}
int AnalClassesModel : : columnCount ( const QModelIndex & ) const
{
return Columns : : COUNT ;
}
QVariant AnalClassesModel : : data ( const QModelIndex & index , int role ) const
{
if ( index . internalId ( ) = = 0 ) { // class row
if ( index . row ( ) > = classes . count ( ) ) {
return QVariant ( ) ;
}
2019-02-01 14:51:29 +00:00
QString cls = classes . at ( index . row ( ) ) ;
2018-08-17 11:27:07 +00:00
switch ( role ) {
case Qt : : DisplayRole :
switch ( index . column ( ) ) {
case NAME :
2019-02-01 14:51:29 +00:00
return cls ;
2018-08-17 11:27:07 +00:00
case TYPE :
2019-02-01 14:51:29 +00:00
return tr ( " class " ) ;
2018-08-17 11:27:07 +00:00
default :
return QVariant ( ) ;
}
case TypeRole :
2019-02-01 14:51:29 +00:00
return QVariant : : fromValue ( RowType : : Class ) ;
2019-02-01 20:40:34 +00:00
case NameRole :
return cls ;
2018-08-17 11:27:07 +00:00
default :
return QVariant ( ) ;
}
2019-02-01 14:51:29 +00:00
} else { // method/field/base row
QString cls = classes . at ( static_cast < int > ( index . internalId ( ) - 1 ) ) ;
const Attribute & attr = getAttrs ( cls ) [ index . row ( ) ] ;
switch ( attr . type ) {
case Attribute : : Type : : Base : {
AnalBaseClassDescription base = attr . data . value < AnalBaseClassDescription > ( ) ;
switch ( role ) {
case Qt : : DisplayRole :
switch ( index . column ( ) ) {
case NAME :
return base . className ;
case TYPE :
return tr ( " base " ) ;
case OFFSET :
return QString ( " +%1 " ) . arg ( base . offset ) ;
default :
return QVariant ( ) ;
}
2019-02-01 15:14:11 +00:00
case Qt : : DecorationRole :
if ( index . column ( ) = = NAME ) {
return QIcon ( new SvgIconEngine ( QString ( " :/img/icons/home.svg " ) , QPalette : : WindowText ) ) ;
}
return QVariant ( ) ;
2019-02-01 14:51:29 +00:00
case NameRole :
return base . className ;
case TypeRole :
return QVariant : : fromValue ( RowType : : Base ) ;
2018-08-17 11:27:07 +00:00
default :
return QVariant ( ) ;
}
2019-02-01 14:51:29 +00:00
break ;
2018-08-17 11:27:07 +00:00
}
2019-02-01 14:51:29 +00:00
case Attribute : : Type : : Method : {
AnalMethodDescription meth = attr . data . value < AnalMethodDescription > ( ) ;
switch ( role ) {
case Qt : : DisplayRole :
switch ( index . column ( ) ) {
case NAME :
return meth . name ;
case TYPE :
return tr ( " method " ) ;
case OFFSET :
return meth . addr = = RVA_INVALID ? QString ( ) : RAddressString ( meth . addr ) ;
case VTABLE :
return meth . vtableOffset < 0 ? QString ( ) : QString ( " +%1 " ) . arg ( meth . vtableOffset ) ;
default :
return QVariant ( ) ;
}
2019-02-01 15:14:11 +00:00
case Qt : : DecorationRole :
if ( index . column ( ) = = NAME ) {
return QIcon ( new SvgIconEngine ( QString ( " :/img/icons/fork.svg " ) , QPalette : : WindowText ) ) ;
}
return QVariant ( ) ;
2019-02-01 14:51:29 +00:00
case OffsetRole :
return QVariant : : fromValue ( meth . addr ) ;
case NameRole :
return meth . name ;
case TypeRole :
return QVariant : : fromValue ( RowType : : Method ) ;
2018-08-17 11:27:07 +00:00
default :
return QVariant ( ) ;
}
2019-02-01 14:51:29 +00:00
break ;
2018-08-17 11:27:07 +00:00
}
2019-02-01 14:51:29 +00:00
case Attribute : : Type : : VTable : {
AnalVTableDescription vtable = attr . data . value < AnalVTableDescription > ( ) ;
switch ( role ) {
case Qt : : DisplayRole :
switch ( index . column ( ) ) {
case NAME :
return " vtable " ;
case TYPE :
return tr ( " vtable " ) ;
case OFFSET :
return RAddressString ( vtable . addr ) ;
default :
return QVariant ( ) ;
}
2019-02-01 15:14:11 +00:00
case Qt : : DecorationRole :
if ( index . column ( ) = = NAME ) {
return QIcon ( new SvgIconEngine ( QString ( " :/img/icons/list.svg " ) , QPalette : : WindowText ) ) ;
}
return QVariant ( ) ;
2019-02-01 14:51:29 +00:00
case OffsetRole :
return QVariant : : fromValue ( vtable . addr ) ;
case TypeRole :
return QVariant : : fromValue ( RowType : : VTable ) ;
2018-08-17 11:27:07 +00:00
default :
return QVariant ( ) ;
}
2019-02-01 14:51:29 +00:00
break ;
}
2017-12-23 16:42:42 +00:00
}
}
2019-02-01 14:51:29 +00:00
return QVariant ( ) ;
2017-12-23 16:42:42 +00:00
}
2018-08-17 11:27:07 +00:00
ClassesSortFilterProxyModel : : ClassesSortFilterProxyModel ( QObject * parent )
2017-12-23 16:42:42 +00:00
: QSortFilterProxyModel ( parent )
{
}
bool ClassesSortFilterProxyModel : : filterAcceptsRow ( int row , const QModelIndex & parent ) const
{
QModelIndex index = sourceModel ( ) - > index ( row , 0 , parent ) ;
return index . data ( ClassesModel : : NameRole ) . toString ( ) . contains ( filterRegExp ( ) ) ;
}
bool ClassesSortFilterProxyModel : : lessThan ( const QModelIndex & left , const QModelIndex & right ) const
{
2018-03-21 20:32:32 +00:00
switch ( left . column ( ) ) {
case ClassesModel : : OFFSET : {
2017-12-23 16:42:42 +00:00
RVA left_offset = left . data ( ClassesModel : : OffsetRole ) . toULongLong ( ) ;
RVA right_offset = right . data ( ClassesModel : : OffsetRole ) . toULongLong ( ) ;
2019-02-07 10:42:53 +00:00
if ( left_offset ! = right_offset ) {
2017-12-23 16:42:42 +00:00
return left_offset < right_offset ;
2019-02-07 10:42:53 +00:00
}
2017-12-23 16:42:42 +00:00
}
// fallthrough
2018-03-21 20:32:32 +00:00
case ClassesModel : : TYPE : {
2017-12-23 16:42:42 +00:00
auto left_type = left . data ( ClassesModel : : TypeRole ) . value < ClassesModel : : RowType > ( ) ;
auto right_type = right . data ( ClassesModel : : TypeRole ) . value < ClassesModel : : RowType > ( ) ;
2019-02-07 10:42:53 +00:00
if ( left_type ! = right_type ) {
2017-12-23 16:42:42 +00:00
return left_type < right_type ;
2019-02-07 10:42:53 +00:00
}
2017-12-23 16:42:42 +00:00
}
// fallthrough
case ClassesModel : : NAME :
default :
QString left_name = left . data ( ClassesModel : : NameRole ) . toString ( ) ;
QString right_name = right . data ( ClassesModel : : NameRole ) . toString ( ) ;
2019-02-07 10:42:53 +00:00
return QString : : compare ( left_name , right_name , Qt : : CaseInsensitive ) < 0 ;
2017-12-23 16:42:42 +00:00
}
}
2019-02-01 16:10:05 +00:00
bool ClassesSortFilterProxyModel : : hasChildren ( const QModelIndex & parent ) const
{
return ! parent . isValid ( ) | | ! parent . parent ( ) . isValid ( ) ;
}
2017-12-23 16:42:42 +00:00
2018-03-16 21:46:57 +00:00
ClassesWidget : : ClassesWidget ( MainWindow * main , QAction * action ) :
CutterDockWidget ( main , action ) ,
2018-02-04 14:32:18 +00:00
ui ( new Ui : : ClassesWidget )
2017-12-23 16:42:42 +00:00
{
ui - > setupUi ( this ) ;
2019-02-01 16:00:09 +00:00
ui - > classesTreeView - > setIconSize ( QSize ( 10 , 10 ) ) ;
2018-08-17 11:27:07 +00:00
proxy_model = new ClassesSortFilterProxyModel ( this ) ;
2019-02-01 16:10:05 +00:00
ui - > classesTreeView - > setModel ( proxy_model ) ;
2017-12-23 16:42:42 +00:00
ui - > classesTreeView - > sortByColumn ( ClassesModel : : TYPE , Qt : : AscendingOrder ) ;
2018-08-17 11:27:07 +00:00
ui - > classesTreeView - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
ui - > classSourceCombo - > setCurrentIndex ( 1 ) ;
2017-12-23 16:42:42 +00:00
2018-03-11 15:57:38 +00:00
connect ( ui - > classSourceCombo , SIGNAL ( currentIndexChanged ( int ) ) , this , SLOT ( refreshClasses ( ) ) ) ;
2018-08-17 11:27:07 +00:00
connect ( ui - > classesTreeView , & QTreeView : : customContextMenuRequested , this , & ClassesWidget : : showContextMenu ) ;
2019-02-07 10:42:53 +00:00
refreshClasses ( ) ;
2017-12-23 16:42:42 +00:00
}
ClassesWidget : : ~ ClassesWidget ( ) { }
2018-03-11 15:57:38 +00:00
ClassesWidget : : Source ClassesWidget : : getSource ( )
{
2018-08-17 11:27:07 +00:00
switch ( ui - > classSourceCombo - > currentIndex ( ) ) {
case 0 :
2018-03-11 15:57:38 +00:00
return Source : : BIN ;
2018-08-17 11:27:07 +00:00
default :
return Source : : ANAL ;
2018-03-11 15:57:38 +00:00
}
}
2017-12-23 16:42:42 +00:00
void ClassesWidget : : refreshClasses ( )
{
2018-08-17 11:27:07 +00:00
switch ( getSource ( ) ) {
case Source : : BIN :
if ( ! bin_model ) {
proxy_model - > setSourceModel ( nullptr ) ;
delete anal_model ;
anal_model = nullptr ;
bin_model = new BinClassesModel ( this ) ;
2019-02-01 16:10:05 +00:00
proxy_model - > setSourceModel ( bin_model ) ;
2018-08-17 11:27:07 +00:00
}
bin_model - > setClasses ( Core ( ) - > getAllClassesFromBin ( ) ) ;
break ;
case Source : : ANAL :
if ( ! anal_model ) {
proxy_model - > setSourceModel ( nullptr ) ;
delete bin_model ;
bin_model = nullptr ;
anal_model = new AnalClassesModel ( this ) ;
2019-02-01 16:10:05 +00:00
proxy_model - > setSourceModel ( anal_model ) ;
2018-08-17 11:27:07 +00:00
}
break ;
}
2017-12-23 16:42:42 +00:00
2018-04-01 08:25:31 +00:00
qhelpers : : adjustColumns ( ui - > classesTreeView , 3 , 0 ) ;
2017-12-23 16:42:42 +00:00
ui - > classesTreeView - > setColumnWidth ( 0 , 200 ) ;
}
void ClassesWidget : : on_classesTreeView_doubleClicked ( const QModelIndex & index )
{
2018-04-23 07:54:06 +00:00
if ( ! index . isValid ( ) )
return ;
2019-02-01 16:00:09 +00:00
QVariant offsetData = index . data ( ClassesModel : : OffsetRole ) ;
if ( ! offsetData . isValid ( ) ) {
return ;
}
RVA offset = offsetData . value < RVA > ( ) ;
2018-04-12 06:33:30 +00:00
Core ( ) - > seek ( offset ) ;
2017-12-23 16:42:42 +00:00
}
2018-08-17 11:27:07 +00:00
void ClassesWidget : : showContextMenu ( const QPoint & pt )
{
2019-02-02 13:14:39 +00:00
if ( ! anal_model ) {
// no context menu for bin classes
return ;
}
2018-08-17 11:27:07 +00:00
QModelIndex index = ui - > classesTreeView - > selectionModel ( ) - > currentIndex ( ) ;
if ( ! index . isValid ( ) ) {
return ;
}
2019-02-07 12:40:41 +00:00
auto type = static_cast < ClassesModel : : RowType > ( index . data ( ClassesModel : : TypeRole ) . toInt ( ) ) ;
2018-08-17 11:27:07 +00:00
QMenu menu ( ui - > classesTreeView ) ;
2019-02-07 12:40:41 +00:00
menu . addAction ( ui - > newClassAction ) ;
if ( type = = ClassesModel : : RowType : : Class ) {
menu . addAction ( ui - > renameClassAction ) ;
menu . addAction ( ui - > deleteClassAction ) ;
}
menu . addSeparator ( ) ;
2018-08-17 11:27:07 +00:00
menu . addAction ( ui - > addMethodAction ) ;
2019-02-07 12:40:41 +00:00
if ( type = = ClassesModel : : RowType : : Method ) {
2018-08-17 11:27:07 +00:00
menu . addAction ( ui - > editMethodAction ) ;
2019-02-02 13:14:39 +00:00
QString className = index . parent ( ) . data ( ClassesModel : : NameRole ) . toString ( ) ;
QString methodName = index . data ( ClassesModel : : NameRole ) . toString ( ) ;
AnalMethodDescription desc ;
if ( Core ( ) - > getAnalMethod ( className , methodName , & desc ) ) {
if ( desc . vtableOffset > = 0 ) {
menu . addAction ( ui - > seekToVTableAction ) ;
}
}
2018-08-17 11:27:07 +00:00
}
menu . exec ( ui - > classesTreeView - > mapToGlobal ( pt ) ) ;
}
void ClassesWidget : : on_seekToVTableAction_triggered ( )
{
2019-02-02 13:14:39 +00:00
QModelIndex index = ui - > classesTreeView - > selectionModel ( ) - > currentIndex ( ) ;
QString className = index . parent ( ) . data ( ClassesModel : : NameRole ) . toString ( ) ;
QList < AnalVTableDescription > vtables = Core ( ) - > getAnalClassVTables ( className ) ;
if ( vtables . isEmpty ( ) ) {
QMessageBox : : warning ( this , tr ( " Missing VTable in class " ) , tr ( " The class %1 does not have any VTable! " ) . arg ( className ) ) ;
return ;
2018-08-17 11:27:07 +00:00
}
2019-02-02 13:14:39 +00:00
QString methodName = index . data ( ClassesModel : : NameRole ) . toString ( ) ;
AnalMethodDescription desc ;
if ( ! Core ( ) - > getAnalMethod ( className , methodName , & desc ) | | desc . vtableOffset < 0 ) {
return ;
}
Core ( ) - > seek ( vtables [ 0 ] . addr + desc . vtableOffset ) ;
2018-08-17 11:27:07 +00:00
}
void ClassesWidget : : on_addMethodAction_triggered ( )
{
QModelIndex index = ui - > classesTreeView - > selectionModel ( ) - > currentIndex ( ) ;
if ( ! index . isValid ( ) ) {
return ;
}
QString className ;
2019-02-01 14:51:29 +00:00
if ( index . data ( ClassesModel : : TypeRole ) . toInt ( ) = = static_cast < int > ( ClassesModel : : RowType : : Class ) ) {
2018-08-17 11:27:07 +00:00
className = index . data ( ClassesModel : : NameRole ) . toString ( ) ;
} else {
className = index . parent ( ) . data ( ClassesModel : : NameRole ) . toString ( ) ;
}
2019-02-01 20:40:34 +00:00
EditMethodDialog : : newMethod ( className , QString ( ) , this ) ;
2018-08-17 11:27:07 +00:00
}
void ClassesWidget : : on_editMethodAction_triggered ( )
{
QModelIndex index = ui - > classesTreeView - > selectionModel ( ) - > currentIndex ( ) ;
2019-02-01 14:51:29 +00:00
if ( ! index . isValid ( ) | | index . data ( ClassesModel : : TypeRole ) . toInt ( ) ! = static_cast < int > ( ClassesModel : : RowType : : Method ) ) {
2018-08-17 11:27:07 +00:00
return ;
}
QString className = index . parent ( ) . data ( ClassesModel : : NameRole ) . toString ( ) ;
2019-02-01 20:40:34 +00:00
QString methName = index . data ( ClassesModel : : NameRole ) . toString ( ) ;
EditMethodDialog : : editMethod ( className , methName , this ) ;
2018-08-17 11:27:07 +00:00
}
2019-02-07 12:40:41 +00:00
void ClassesWidget : : on_newClassAction_triggered ( )
{
QString name ;
if ( ! RenameDialog : : showDialog ( tr ( " Create new Class " ) , & name , tr ( " Class Name " ) , this ) | | name . isEmpty ( ) ) {
return ;
}
Core ( ) - > createNewClass ( name ) ;
}
void ClassesWidget : : on_deleteClassAction_triggered ( )
{
QModelIndex index = ui - > classesTreeView - > selectionModel ( ) - > currentIndex ( ) ;
if ( ! index . isValid ( ) | | index . data ( ClassesModel : : TypeRole ) . toInt ( ) ! = static_cast < int > ( ClassesModel : : RowType : : Class ) ) {
return ;
}
QString className = index . data ( ClassesModel : : NameRole ) . toString ( ) ;
if ( QMessageBox : : question ( this , tr ( " Delete Class " ) , tr ( " Are you sure you want to delete the class %1? " ) . arg ( className ) ) ! = QMessageBox : : StandardButton : : Yes ) {
return ;
}
Core ( ) - > deleteClass ( className ) ;
}
void ClassesWidget : : on_renameClassAction_triggered ( )
{
QModelIndex index = ui - > classesTreeView - > selectionModel ( ) - > currentIndex ( ) ;
if ( ! index . isValid ( ) | | index . data ( ClassesModel : : TypeRole ) . toInt ( ) ! = static_cast < int > ( ClassesModel : : RowType : : Class ) ) {
return ;
}
QString oldName = index . data ( ClassesModel : : NameRole ) . toString ( ) ;
QString newName = oldName ;
if ( ! RenameDialog : : showDialog ( tr ( " Rename Class %1 " ) . arg ( oldName ) , & newName , tr ( " Class Name " ) , this ) | | newName . isEmpty ( ) ) {
return ;
}
Core ( ) - > renameClass ( oldName , newName ) ;
}