import idc
import ida_idaapi
import ida_kernwin
import ida_ua
import ida_bytes
import ida_xref
import ida_funcs
import ida_nalt
import ida_name
import ida_lines

import traceback
import os
import sys
#import re
#import threading

import get_func_relation
import cache_data
import jump
import tinfo
import config_base
import debug_print
import comments
import utils

ida_idaapi.require("get_func_relation")
ida_idaapi.require("cache_data")
ida_idaapi.require("jump")
ida_idaapi.require("tinfo")
ida_idaapi.require("config_base")
ida_idaapi.require("debug_print")
ida_idaapi.require("comments")
ida_idaapi.require("utils")

FT_UNK = get_func_relation.FT_UNK
FT_GEN = get_func_relation.FT_GEN
FT_LIB = get_func_relation.FT_LIB
FT_API = get_func_relation.FT_API
FT_MEM = get_func_relation.FT_MEM
FT_VAR = get_func_relation.FT_VAR
FT_STR = get_func_relation.FT_STR
FT_VTB = get_func_relation.FT_VTB

class cto_base(debug_print.debug):
    
    def __init__(self, cto_data=None, debug=False):
        #super(cto_base, self).__init__(debug)
        debug_print.debug.__init__(self, debug)
        
        self.cmt_obj = comments.comment_t()

        self.decomp_avail = False
        try:
            import ida_hexrays
            self.decomp_avail = ida_hexrays.init_hexrays_plugin()
        except ImportError:
            pass
        
        # get current available widget
        self.curr_view = None
        w, wt = self.get_widget()
        self.curr_view = w
        
        # it should be the first instance
        if cto_data is None:
            self.config = self._config()
            if debug:
                self.config.debug = debug
            if self.config.debug: self.dbg_print("newly created and cto_data is None")
            self.cto_data = {'cto_data':{}, 'refcnt':1, 'master':id(self), 'config': self.config, 'insts':set([self])}
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self.config, self.config.debug)
            result = self.cd.dh.load_data(self.config)
            if result:
                self.cto_data['cto_data'] = result
                self.copy_cache_data()
                if debug:
                    self.config.debug = debug
            else:
                self.update_data()
                #self.cache_update()
                if debug:
                    self.config.debug = debug
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config, self.cto_data['cto_data']))
        # it has an external data
        else:
            self.cto_data = cto_data
            self.copy_cache_data()
            self.cto_data['insts'].add(self)
            self.config = self.cto_data["config"]
            if debug:
                self.config.debug = debug
            if self.config.debug: self.dbg_print("cto_data is NOT None")
            if self.config.debug: self.dbg_print(id(self), id(self.cto_data))
            # take master if ref count is less than 1. Note that, at this moment,
            # ref count is not increased yet. Do not include 1.
            if self.cto_data['refcnt'] < 1:
                if self.config.debug: self.dbg_print("took master")
                self.cto_data['master'] = id(self)
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self.config, self.config.debug)
            # if this instance is a slave, 
            if self.cto_data['master'] != id(self):
                self.cto_data["refcnt"] += 1
        
    # for potable config values for taking over subgraphs
    class _config(config_base._config_base):
        def __init__(self):
            self.get_default_config()
        
        def get_default_config(self):
            self.debug = False
            self.skip_caller = False
            self.center_node = True
            self.display_children_in_parent_funcs = False
            self.dark_mode = False
            self.show_strings_nodes = False
            self.show_gvars_nodes = False
            self.show_stroff_nodes = False
            self.show_comment_nodes = False
            self.show_indirect_calls = True
    
    def dbg_print(self, *msg):
        if self.config.debug:
            self._dbg_print(*msg)

    def copy_cache_data(self):
        self.func_relations = self.cto_data["cto_data"]["func_relations"]
        self.import_eas = self.cto_data["cto_data"]["import_eas"]
        self.string_eas = self.cto_data["cto_data"]["string_eas"]
        self.paths_cache = self.cto_data["cto_data"]["paths_cache"]
        self.dyn_apicalls = self.cto_data["cto_data"]["dyn_apicalls"]
        self.opnums = self.cto_data["cto_data"]["opnums"]
        self.eps = self.cto_data["cto_data"]["eps"]
        self.vtbl_refs = self.cto_data["cto_data"]["vtbl_refs"]

    def cache_cmt_update(self):
        # clear cache
        for func_ea in self.cto_data["cto_data"]["func_relations"]:
            self.cto_data["cto_data"]["func_relations"][func_ea]['cmt'] = {}
            self.cto_data["cto_data"]["func_relations"][func_ea]['rcmt'] = {}
            
        # get comments and merge them
        cmts = self.cmt_obj.collect_cmts_as_dict()
        utils.deep_update(self.cto_data["cto_data"]["func_relations"], cmts)
        
        # recursive for instance
        for inst in self.cto_data["insts"]:
            inst.copy_cache_data()
            
        
    def cache_update(self):
        self.cto_data["cto_data"] = {}
        self.cto_data["cto_data"]["func_relations"], self.cto_data["cto_data"]["import_eas"], self.cto_data["cto_data"]["string_eas"] = get_func_relation.get_func_relations()
        self.cto_data["cto_data"]["paths_cache"] = {}
        self.cto_data["cto_data"]["dyn_apicalls"] = get_func_relation.get_dyn_apicalls(self.cto_data["cto_data"]["func_relations"])
        self.cto_data["cto_data"]["opnums"] = get_func_relation.get_opnums(self.cto_data["cto_data"]["func_relations"], "children")
        self.cto_data["cto_data"]["eps"] = list(get_func_relation.get_entry_points())
        self.cto_data["cto_data"]["vtbl_refs"] = get_func_relation.get_vtbl_refs(self.cto_data["cto_data"]["func_relations"])
        
        self.cache_cmt_update()
        
        # recursive for instance
        for inst in self.cto_data["insts"]:
            inst.copy_cache_data()
    
    def partial_cache_update(self, ea):
        # updating a function that the ea belongs to.
        f, bbs = get_func_relation.get_func_bbs(ea)
        if f:
            ea = f.start_ea
        parents, children, apicalls, gvars, strings, stroff, vtbl = get_func_relation.get_family_members(ea, bbs, self.import_eas, self.string_eas)
        func_type = get_func_relation.get_func_type(ea, self.import_eas)
        self.func_relations[ea] = {"parents":parents, "children":children, "func_type":func_type, "gvars":gvars, "strings":strings, "struct_offsets":stroff, "vftables":vtbl , "cmt":{}, "rcmt":{}}
        cmts = self.cmt_obj.collect_cmts_as_dict(ea)
        utils.deep_update(self.func_relations, cmts)
        
        # updating apicalls that are called in the function
        for api_ea in apicalls:
            if api_ea == ida_idaapi.BADADDR:
                continue
            func_type = apicalls[api_ea]
            parents = get_func_relation.get_xrefs(api_ea)
            self.func_relations[api_ea] = {"parents":parents, "children":{}, "func_type":func_type, "gvars":{}, "strings":{}, "struct_offsets":{}, "vftables":{}, "cmt":{}, "rcmt":{}}
            
        # updating vftables that are called in the function
        for vtbl_ea in vtbl:
            if vtbl_ea == ida_idaapi.BADADDR:
                continue
            func_type = FT_VTB
            parents = get_func_relation.get_xrefs(vtbl_ea)
            self.func_relations[vtbl_ea] = {"parents":parents, "children":vtbl[vtbl_ea], "func_type":func_type, "gvars":{}, "strings":{}, "struct_offsets":{}, "vftables":{}, "cmt":{}, "rcmt":{}}
            
        # updating dyn_apicalls table
        if ea in self.dyn_apicalls:
            self.dyn_apicalls.pop(ea)
        for caller, func_name in get_func_relation.get_dyn_apicalls_partial(self.func_relations, ea):
            self.dyn_apicalls[caller] = func_name
            
        # updating paths cache (just removing paths related to the ea)
        to_be_removed = set([])
        for k in self.paths_cache:
            if ea in [k[0], k[1]]:
                to_be_removed.add(k)
            else:
                for path in self.paths_cache[k]:
                    for p in path:
                        if ea in [p[0], p[1]]:
                            to_be_removed.add(k)
                            break
        for k in to_be_removed:
            self.paths_cache.pop(k)
        #self.cd.dh.save_data(self.cd.dh.convert_data(self.config, self.cto_data['cto_data']))
            
    # update data online
    def update_data(self):
        # check the refcount
        if self in self.cto_data['insts']:
            if self.cto_data["refcnt"] <= 1 or self.cto_data["master"] == id(self):
                # raise to the master if recount == 1
                if self.config.debug: self.dbg_print("took over the master to the %d" % self.cto_data["master"])
                self.cto_data["master"] = id(self)
                
            self.cache_update()
            #self.cd.dh.save_data(self.cd.dh.convert_data(self.config, self.cto_data['cto_data']))
    
    def close_data(self):
        if self in self.cto_data['insts']:
            # decrease the refcount
            self.cto_data['refcnt'] -= 1
            if self.config.debug: self.dbg_print(id(self), id(self.cto_data), self.cto_data['refcnt'])
            # remove instance from the cached data
            self.cto_data['insts'].remove(self)
            if self.cto_data["refcnt"] <= 1 or self.cto_data["master"] == id(self) or len(self.cto_data['insts']) == 0:
                # save the pickle
                if self.config.debug: self.dbg_print("save pickle")
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config, self.cto_data['cto_data']))
            if self.cto_data["master"] == id(self) and len(self.cto_data['insts']) > 0:
                self.cto_data["master"] = id(next(iter(self.cto_data['insts'])))
                if self.config.debug: self.dbg_print("took over the master to the %d" % self.cto_data["master"])
                
    def __del__(self):
        if self.config.debug: self.dbg_print("close_data was called")
        self.close_data()
            
    #def refresh(self, ea=ida_idaapi.BADADDR):
    #    self.refresh_all(ea)
    
    def refresh(self, ea=ida_idaapi.BADADDR, center=False):
        pass
    
    def refresh_all(self, ea=ida_idaapi.BADADDR, center=False):
        for inst in self.cto_data['insts']:
            inst.refresh(ea, center)
            
    def _jumpto(self, ea, use_opn=False):
        w, wt = self.get_widget()
        flag = False
        ida_kernwin.jumpto(ea)
        if ea in self.func_relations and not use_opn:
            flag = jump.jumpto_name(ea, w)
        elif ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                flag = jump.jumpto_opn(ea, opn, w)
            else:
                # find offset line for vftable funcs
                flag = jump.jumpto_offset(ea, w)
                
        # for others such as referenced strings in an instruction
        else:
            flags = ida_bytes.get_flags(ea)
            if idc.is_code(flags):
                # for strings in funcs and other variables in funcs
                insn = ida_ua.insn_t()
                inslen = ida_ua.decode_insn(insn, ea)
                for opn in range(inslen):
                    if ida_ua.o_void == insn.ops[opn].type:
                        opn = -1
                        break
                    flag = jump.jumpto_opn(ea, opn, w)
                    if flag:
                        break
            else:
                flag = jump.jumpto_name(ea, w)
        return flag

    def jumpto(self, ea, use_opn=False):
        r = False
        try:
            r = self._jumpto(ea, use_opn)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg("Got a unexpected error (%s: %s) (%d)%s" % (str(type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r
    
    def push_lochist_jump(self, w=None):
        r = False
        try:
            r = jump.push_lochist_jump(w)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg("Got a unexpected error (%s: %s) (%d)%s" % (str(type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r
    
    def check_and_rename_var(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_var_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            flag = self.check_and_rename_var_decomp(w)
        return flag
    
    def check_and_rename_var_decomp(self, w):
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        if line is None:
            return False
        self.dbg_print(line)
        
        self.exec_ida_ui_action("hx:Rename")
        
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            curr_line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        self.dbg_print(curr_line)
        if line != curr_line:
            ea = ida_kernwin.get_screen_ea()
            self.dbg_print("Refreshing")
            self.refresh_all(ea)
        else:
            return False
        return True
        
    def check_and_rename_var_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        self.dbg_print("%x" % ea)
        name = self.get_highlighted_name(ea, w=w)
        self.dbg_print(name)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        #line = ida_kernwin.get_curline()
        op = None
        if ea in self.opnums:
            op = idc.print_operand(ea, self.opnums[ea])
        self.exec_ida_ui_action("MakeName")
        curr_name = self.get_highlighted_name(ea, w=w)
        #ida_kernwin.refresh_idaview_anyway()
        #curr_line = ida_kernwin.get_curline()
        curr_op = None
        if ea in self.opnums:
            curr_op = idc.print_operand(ea, self.opnums[ea])
        self.dbg_print(curr_name)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.dbg_print("Start refreshing")
            self.refresh_all(ea)
        # for renaming a struct member
        #elif line != curr_line or curr_op != op:
        elif curr_op != op:
            self.dbg_print("Start refreshing", curr_op, op)
            callee, func_type, opn, _func_name = self.get_callee_info(ea)
            if callee is not None:
                optype = idc.get_operand_type(ea, opn)
                if optype in [ida_ua.o_displ, ida_ua.o_phrase]:
                    self.update_caller_tif(ea)
                      #self.exec_ui_action("EmptyStack")
                    self.refresh_all(ea)
        else:
            self.dbg_print("Not changed")
            return False
        return True

    def check_and_rename_func_info(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_func_info_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    
    def check_and_rename_func_info_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        name = self.get_highlighted_name(ea, w=w)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        self.exec_ida_ui_action("EditFunction")
        #ida_kernwin.refresh_idaview_anyway()
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.refresh_all(ea)
        return True
            
    def check_and_add_rcmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_rcmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    
    def check_and_add_rcmt_ida(self):
        # repeatable comment
        ea = ida_kernwin.get_screen_ea()
        rcmt = ida_bytes.get_cmt(ea, 1)
        self.exec_ida_ui_action("MakeRptCmt")
        #ida_kernwin.refresh_idaview_anyway()
        curr_rcmt = ida_bytes.get_cmt(ea, 1)
        if rcmt != curr_rcmt:
            self.partial_cache_update(ea)
            #self.exec_ui_action("EmptyStack")
            self.refresh_all(ea)
        return True
            
    def check_and_add_cmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_cmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    
    def check_and_add_cmt_ida(self):
        # comment
        ea = ida_kernwin.get_screen_ea()
        cmt = ida_bytes.get_cmt(ea, 0)
        self.exec_ida_ui_action("MakeComment")
        #ida_kernwin.refresh_idaview_anyway()
        curr_cmt = ida_bytes.get_cmt(ea, 0)
        if cmt != curr_cmt:
            self.update_caller_tif(ea, curr_cmt)
            #self.exec_ui_action("EmptyStack")
            #self.refresh()
            self.partial_cache_update(ea)
            self.refresh_all(ea, center=True)
        return True
            
    def check_and_apply_strunct(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_apply_strunct_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    
    def check_and_apply_strunct_ida(self):
        # apply structure
        ea = ida_kernwin.get_screen_ea()
        line = ida_kernwin.get_curline()
        self.exec_ida_ui_action("OpStructOffset")
        #ida_kernwin.refresh_idaview_anyway()
        curr_line = ida_kernwin.get_curline()
        #f = ida_funcs.get_func(ea)
        if line != curr_line:
            self.update_caller_tif(ea)
            #self.exec_ui_action("EmptyStack")
            #self.refresh_with_center_node()
            self.refresh_all(ea, center=True)
            #self.refresh()
        return True
            
    def check_xrefs(self):
        # show xrefs to
        self.exec_ida_ui_action("JumpOpXref")
        
    def add_cref(self, ea, func_chooser_t):
        fc = func_chooser_t("Choose the destination", self)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, func_name = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            func_type = get_func_relation.get_func_type(callee_ea, self.import_eas)
            cref_type = 0
            optype = idc.get_operand_type(ea, 0)
            if optype in [ida_ua.o_reg, ida_ua.o_displ, ida_ua.o_phrase] and get_func_relation.is_call_insn(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_CN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_CF
            elif (get_func_relation.is_indirect_jump_insn(ea) and not get_func_relation.get_switch_info(ea)):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_JN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_JF
            else:
                ida_kernwin.msg("Do it on a indirecet calls or jumps" + os.linesep)
            if cref_type > 0:
                r = ida_xref.add_cref(ea, callee_ea, cref_type|ida_xref.XREF_USER)
                self.apply_tif(ea, func_name)
                return True
        return False
        
    def del_cref(self, ea, cref_chooser_t):
        fc = cref_chooser_t("Choose the destination", self, ea)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, _ = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            ida_xref.del_cref(ea, callee_ea, 0)
            f = ida_funcs.get_func(ea)
            if f:
                self.del_op_tinfo(f.start_ea, ea)
            return True
        return False
        
    def del_op_tinfo(self, func_ea, ea):
        if func_ea in self.func_relations:
            if ea in self.func_relations[func_ea]['children']:
                callee, func_type, opn, func_name = self.func_relations[func_ea]['children'][ea]
                if callee == ida_idaapi.BADADDR and func_name:
                    ida_nalt.del_op_tinfo(ea, opn)
        
    def get_callee_info(self, caller_ea):
        f = ida_funcs.get_func(caller_ea)
        if f and f.start_ea in self.func_relations:
            if caller_ea in self.func_relations[f.start_ea]['children']:
                callee, func_type, op, func_name =  self.func_relations[f.start_ea]['children'][caller_ea]
                return callee, func_type, op, func_name
        return None, None, None, None
        
    def apply_tif(self, caller, func_name):
        callee, func_type, opn, _func_name = self.get_callee_info(caller)
        if callee is not None:
            tif = tinfo.get_tinfo_by_name(func_name)
            if tif:
                tinfo.apply_tinfo_to_ea(tif, caller, opn)
                
    def update_caller_tif(self, ea, func_name=None):
        callee_ea, func_type, opn, _func_name = self.get_callee_info(ea)
        if callee_ea is not None:
            if func_name is None:
                #if callee_ea == ida_idaapi.BADADDR and _func_name:
                if _func_name:
                    func_name = _func_name
                else:
                    func_name, func_type, v = get_func_relation.get_func_info_by_opstr(ea, opn)
            ida_nalt.del_op_tinfo(ea, opn)
            #if ea in self.dyn_apicalls:
            #    self.dyn_apicalls.pop(ea)
            self.partial_cache_update(ea)
            if func_name:
                self.apply_tif(ea, func_name)
            return True
        return False
    
    def get_func_name(self, ea):
        f = ida_funcs.get_func(ea)
        func_name = ""
        if ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                v = idc.get_operand_value(ea, opn)
                func_name = ida_funcs.get_func_name(v)
                if not func_name:
                    func_name = ida_name.get_name(v)
        elif f and f.start_ea == ea:
            func_name = ida_funcs.get_func_name(ea)
        if not func_name:
            func_name = ida_name.get_name(ea)
        func_name = ida_name.validate_name(func_name, ida_name.VNT_VISIBLE)
        return func_name
    
    def _get_highlighted_name(self, ea, w):
        if w is None:
            return ""
        hl = ida_kernwin.get_highlight(w)
        self.dbg_print(hl)
        name = ""
        if hl is not None:
            for opn in range(2):
                v = idc.get_operand_value(ea, opn)
                fn = ida_name.get_name(v)
                if fn:
                    fn = ida_lines.tag_remove(fn)
                    fn = ida_name.validate_name(fn, ida_name.VNT_VISIBLE)
                    if fn == hl[0]:
                        name = fn
                        break
            if not name:
                fn = self.get_func_name(ea)
                #fn = ida_name.validate_name(fn, ida_name.VNT_VISIBLE)
                self.dbg_print("fn: %s" % fn)
                if fn == hl[0]:
                    name = fn
        return name
    
    def get_highlighted_name(self, ea, w=None):
        if w is None:
            return ""
        return self._get_highlighted_name(ea, w)
    
    def get_widget(self):
        w, wt = utils.get_widget(curr_view=self.curr_view)
        return w, wt
    
    def get_focus(self, w=None):
        if w is None:
            w = self.GetWidget()
        if w is None:
            return False
        return ida_kernwin.activate_widget(w, True)
    
    def _exec_ui_action(self, action, w):
        if w is None:
            return False
        ida_kernwin.activate_widget(w, True)
        ida_kernwin.process_ui_action(action)
        return True
            
    def exec_ui_action(self, action, w=None):
        return self._exec_ui_action(action, w)

    def does_use_opn(self):
        return False
    
    def exec_ida_ui_action(self, action):
        ea = ida_kernwin.get_screen_ea()
        self.jumpto(ea, self.does_use_opn())
        #w = ida_kernwin.find_widget(self.ida_view)
        w, wt = self.get_widget()
        return self.exec_ui_action(action, w=w)
        
    def print_config_info(self):
        for k in self.config:
            ida_kernwin.msg("%s: %s%s" % (k, str(self.config[k]), os.linesep))
        ida_kernwin.msg("max_depth: %d%s" % (self.max_depth, os.linesep))
        ida_kernwin.msg("start_ea: %x%s" %  (self.start_ea, os.linesep))
    

9f4c130a02b33cd3922ea935ee926f2d9a901471
<mask token>


class cto_base(debug_print.debug):
    <mask token>


    class _config(config_base._config_base):

        def __init__(self):
            self.get_default_config()

        def get_default_config(self):
            self.debug = False
            self.skip_caller = False
            self.center_node = True
            self.display_children_in_parent_funcs = False
            self.dark_mode = False
            self.show_strings_nodes = False
            self.show_gvars_nodes = False
            self.show_stroff_nodes = False
            self.show_comment_nodes = False
            self.show_indirect_calls = True
    <mask token>
    <mask token>
    <mask token>
    <mask token>

    def partial_cache_update(self, ea):
        f, bbs = get_func_relation.get_func_bbs(ea)
        if f:
            ea = f.start_ea
        parents, children, apicalls, gvars, strings, stroff, vtbl = (
            get_func_relation.get_family_members(ea, bbs, self.import_eas,
            self.string_eas))
        func_type = get_func_relation.get_func_type(ea, self.import_eas)
        self.func_relations[ea] = {'parents': parents, 'children': children,
            'func_type': func_type, 'gvars': gvars, 'strings': strings,
            'struct_offsets': stroff, 'vftables': vtbl, 'cmt': {}, 'rcmt': {}}
        cmts = self.cmt_obj.collect_cmts_as_dict(ea)
        utils.deep_update(self.func_relations, cmts)
        for api_ea in apicalls:
            if api_ea == ida_idaapi.BADADDR:
                continue
            func_type = apicalls[api_ea]
            parents = get_func_relation.get_xrefs(api_ea)
            self.func_relations[api_ea] = {'parents': parents, 'children':
                {}, 'func_type': func_type, 'gvars': {}, 'strings': {},
                'struct_offsets': {}, 'vftables': {}, 'cmt': {}, 'rcmt': {}}
        for vtbl_ea in vtbl:
            if vtbl_ea == ida_idaapi.BADADDR:
                continue
            func_type = FT_VTB
            parents = get_func_relation.get_xrefs(vtbl_ea)
            self.func_relations[vtbl_ea] = {'parents': parents, 'children':
                vtbl[vtbl_ea], 'func_type': func_type, 'gvars': {},
                'strings': {}, 'struct_offsets': {}, 'vftables': {}, 'cmt':
                {}, 'rcmt': {}}
        if ea in self.dyn_apicalls:
            self.dyn_apicalls.pop(ea)
        for caller, func_name in get_func_relation.get_dyn_apicalls_partial(
            self.func_relations, ea):
            self.dyn_apicalls[caller] = func_name
        to_be_removed = set([])
        for k in self.paths_cache:
            if ea in [k[0], k[1]]:
                to_be_removed.add(k)
            else:
                for path in self.paths_cache[k]:
                    for p in path:
                        if ea in [p[0], p[1]]:
                            to_be_removed.add(k)
                            break
        for k in to_be_removed:
            self.paths_cache.pop(k)

    def update_data(self):
        if self in self.cto_data['insts']:
            if self.cto_data['refcnt'] <= 1 or self.cto_data['master'] == id(
                self):
                if self.config.debug:
                    self.dbg_print('took over the master to the %d' % self.
                        cto_data['master'])
                self.cto_data['master'] = id(self)
            self.cache_update()
    <mask token>

    def __del__(self):
        if self.config.debug:
            self.dbg_print('close_data was called')
        self.close_data()

    def refresh(self, ea=ida_idaapi.BADADDR, center=False):
        pass

    def refresh_all(self, ea=ida_idaapi.BADADDR, center=False):
        for inst in self.cto_data['insts']:
            inst.refresh(ea, center)

    def _jumpto(self, ea, use_opn=False):
        w, wt = self.get_widget()
        flag = False
        ida_kernwin.jumpto(ea)
        if ea in self.func_relations and not use_opn:
            flag = jump.jumpto_name(ea, w)
        elif ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                flag = jump.jumpto_opn(ea, opn, w)
            else:
                flag = jump.jumpto_offset(ea, w)
        else:
            flags = ida_bytes.get_flags(ea)
            if idc.is_code(flags):
                insn = ida_ua.insn_t()
                inslen = ida_ua.decode_insn(insn, ea)
                for opn in range(inslen):
                    if ida_ua.o_void == insn.ops[opn].type:
                        opn = -1
                        break
                    flag = jump.jumpto_opn(ea, opn, w)
                    if flag:
                        break
            else:
                flag = jump.jumpto_name(ea, w)
        return flag
    <mask token>
    <mask token>

    def check_and_rename_var(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_var_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            flag = self.check_and_rename_var_decomp(w)
        return flag
    <mask token>

    def check_and_rename_var_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        self.dbg_print('%x' % ea)
        name = self.get_highlighted_name(ea, w=w)
        self.dbg_print(name)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        op = None
        if ea in self.opnums:
            op = idc.print_operand(ea, self.opnums[ea])
        self.exec_ida_ui_action('MakeName')
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_op = None
        if ea in self.opnums:
            curr_op = idc.print_operand(ea, self.opnums[ea])
        self.dbg_print(curr_name)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.dbg_print('Start refreshing')
            self.refresh_all(ea)
        elif curr_op != op:
            self.dbg_print('Start refreshing', curr_op, op)
            callee, func_type, opn, _func_name = self.get_callee_info(ea)
            if callee is not None:
                optype = idc.get_operand_type(ea, opn)
                if optype in [ida_ua.o_displ, ida_ua.o_phrase]:
                    self.update_caller_tif(ea)
                    self.refresh_all(ea)
        else:
            self.dbg_print('Not changed')
            return False
        return True

    def check_and_rename_func_info(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_func_info_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    <mask token>

    def check_and_add_rcmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_rcmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_add_rcmt_ida(self):
        ea = ida_kernwin.get_screen_ea()
        rcmt = ida_bytes.get_cmt(ea, 1)
        self.exec_ida_ui_action('MakeRptCmt')
        curr_rcmt = ida_bytes.get_cmt(ea, 1)
        if rcmt != curr_rcmt:
            self.partial_cache_update(ea)
            self.refresh_all(ea)
        return True
    <mask token>
    <mask token>
    <mask token>
    <mask token>

    def check_xrefs(self):
        self.exec_ida_ui_action('JumpOpXref')

    def add_cref(self, ea, func_chooser_t):
        fc = func_chooser_t('Choose the destination', self)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, func_name = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            func_type = get_func_relation.get_func_type(callee_ea, self.
                import_eas)
            cref_type = 0
            optype = idc.get_operand_type(ea, 0)
            if optype in [ida_ua.o_reg, ida_ua.o_displ, ida_ua.o_phrase
                ] and get_func_relation.is_call_insn(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_CN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_CF
            elif get_func_relation.is_indirect_jump_insn(ea
                ) and not get_func_relation.get_switch_info(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_JN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_JF
            else:
                ida_kernwin.msg('Do it on a indirecet calls or jumps' + os.
                    linesep)
            if cref_type > 0:
                r = ida_xref.add_cref(ea, callee_ea, cref_type | ida_xref.
                    XREF_USER)
                self.apply_tif(ea, func_name)
                return True
        return False
    <mask token>

    def del_op_tinfo(self, func_ea, ea):
        if func_ea in self.func_relations:
            if ea in self.func_relations[func_ea]['children']:
                callee, func_type, opn, func_name = self.func_relations[func_ea
                    ]['children'][ea]
                if callee == ida_idaapi.BADADDR and func_name:
                    ida_nalt.del_op_tinfo(ea, opn)

    def get_callee_info(self, caller_ea):
        f = ida_funcs.get_func(caller_ea)
        if f and f.start_ea in self.func_relations:
            if caller_ea in self.func_relations[f.start_ea]['children']:
                callee, func_type, op, func_name = self.func_relations[f.
                    start_ea]['children'][caller_ea]
                return callee, func_type, op, func_name
        return None, None, None, None

    def apply_tif(self, caller, func_name):
        callee, func_type, opn, _func_name = self.get_callee_info(caller)
        if callee is not None:
            tif = tinfo.get_tinfo_by_name(func_name)
            if tif:
                tinfo.apply_tinfo_to_ea(tif, caller, opn)

    def update_caller_tif(self, ea, func_name=None):
        callee_ea, func_type, opn, _func_name = self.get_callee_info(ea)
        if callee_ea is not None:
            if func_name is None:
                if _func_name:
                    func_name = _func_name
                else:
                    func_name, func_type, v = (get_func_relation.
                        get_func_info_by_opstr(ea, opn))
            ida_nalt.del_op_tinfo(ea, opn)
            self.partial_cache_update(ea)
            if func_name:
                self.apply_tif(ea, func_name)
            return True
        return False

    def get_func_name(self, ea):
        f = ida_funcs.get_func(ea)
        func_name = ''
        if ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                v = idc.get_operand_value(ea, opn)
                func_name = ida_funcs.get_func_name(v)
                if not func_name:
                    func_name = ida_name.get_name(v)
        elif f and f.start_ea == ea:
            func_name = ida_funcs.get_func_name(ea)
        if not func_name:
            func_name = ida_name.get_name(ea)
        func_name = ida_name.validate_name(func_name, ida_name.VNT_VISIBLE)
        return func_name

    def _get_highlighted_name(self, ea, w):
        if w is None:
            return ''
        hl = ida_kernwin.get_highlight(w)
        self.dbg_print(hl)
        name = ''
        if hl is not None:
            for opn in range(2):
                v = idc.get_operand_value(ea, opn)
                fn = ida_name.get_name(v)
                if fn:
                    fn = ida_lines.tag_remove(fn)
                    fn = ida_name.validate_name(fn, ida_name.VNT_VISIBLE)
                    if fn == hl[0]:
                        name = fn
                        break
            if not name:
                fn = self.get_func_name(ea)
                self.dbg_print('fn: %s' % fn)
                if fn == hl[0]:
                    name = fn
        return name

    def get_highlighted_name(self, ea, w=None):
        if w is None:
            return ''
        return self._get_highlighted_name(ea, w)
    <mask token>

    def get_focus(self, w=None):
        if w is None:
            w = self.GetWidget()
        if w is None:
            return False
        return ida_kernwin.activate_widget(w, True)

    def _exec_ui_action(self, action, w):
        if w is None:
            return False
        ida_kernwin.activate_widget(w, True)
        ida_kernwin.process_ui_action(action)
        return True

    def exec_ui_action(self, action, w=None):
        return self._exec_ui_action(action, w)

    def does_use_opn(self):
        return False
    <mask token>
    <mask token>

<mask token>


class cto_base(debug_print.debug):

    def __init__(self, cto_data=None, debug=False):
        debug_print.debug.__init__(self, debug)
        self.cmt_obj = comments.comment_t()
        self.decomp_avail = False
        try:
            import ida_hexrays
            self.decomp_avail = ida_hexrays.init_hexrays_plugin()
        except ImportError:
            pass
        self.curr_view = None
        w, wt = self.get_widget()
        self.curr_view = w
        if cto_data is None:
            self.config = self._config()
            if debug:
                self.config.debug = debug
            if self.config.debug:
                self.dbg_print('newly created and cto_data is None')
            self.cto_data = {'cto_data': {}, 'refcnt': 1, 'master': id(self
                ), 'config': self.config, 'insts': set([self])}
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self
                .config, self.config.debug)
            result = self.cd.dh.load_data(self.config)
            if result:
                self.cto_data['cto_data'] = result
                self.copy_cache_data()
                if debug:
                    self.config.debug = debug
            else:
                self.update_data()
                if debug:
                    self.config.debug = debug
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config,
                    self.cto_data['cto_data']))
        else:
            self.cto_data = cto_data
            self.copy_cache_data()
            self.cto_data['insts'].add(self)
            self.config = self.cto_data['config']
            if debug:
                self.config.debug = debug
            if self.config.debug:
                self.dbg_print('cto_data is NOT None')
            if self.config.debug:
                self.dbg_print(id(self), id(self.cto_data))
            if self.cto_data['refcnt'] < 1:
                if self.config.debug:
                    self.dbg_print('took master')
                self.cto_data['master'] = id(self)
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self
                .config, self.config.debug)
            if self.cto_data['master'] != id(self):
                self.cto_data['refcnt'] += 1


    class _config(config_base._config_base):

        def __init__(self):
            self.get_default_config()

        def get_default_config(self):
            self.debug = False
            self.skip_caller = False
            self.center_node = True
            self.display_children_in_parent_funcs = False
            self.dark_mode = False
            self.show_strings_nodes = False
            self.show_gvars_nodes = False
            self.show_stroff_nodes = False
            self.show_comment_nodes = False
            self.show_indirect_calls = True

    def dbg_print(self, *msg):
        if self.config.debug:
            self._dbg_print(*msg)
    <mask token>

    def cache_cmt_update(self):
        for func_ea in self.cto_data['cto_data']['func_relations']:
            self.cto_data['cto_data']['func_relations'][func_ea]['cmt'] = {}
            self.cto_data['cto_data']['func_relations'][func_ea]['rcmt'] = {}
        cmts = self.cmt_obj.collect_cmts_as_dict()
        utils.deep_update(self.cto_data['cto_data']['func_relations'], cmts)
        for inst in self.cto_data['insts']:
            inst.copy_cache_data()

    def cache_update(self):
        self.cto_data['cto_data'] = {}
        self.cto_data['cto_data']['func_relations'], self.cto_data['cto_data'][
            'import_eas'], self.cto_data['cto_data']['string_eas'
            ] = get_func_relation.get_func_relations()
        self.cto_data['cto_data']['paths_cache'] = {}
        self.cto_data['cto_data']['dyn_apicalls'
            ] = get_func_relation.get_dyn_apicalls(self.cto_data['cto_data'
            ]['func_relations'])
        self.cto_data['cto_data']['opnums'] = get_func_relation.get_opnums(self
            .cto_data['cto_data']['func_relations'], 'children')
        self.cto_data['cto_data']['eps'] = list(get_func_relation.
            get_entry_points())
        self.cto_data['cto_data']['vtbl_refs'
            ] = get_func_relation.get_vtbl_refs(self.cto_data['cto_data'][
            'func_relations'])
        self.cache_cmt_update()
        for inst in self.cto_data['insts']:
            inst.copy_cache_data()

    def partial_cache_update(self, ea):
        f, bbs = get_func_relation.get_func_bbs(ea)
        if f:
            ea = f.start_ea
        parents, children, apicalls, gvars, strings, stroff, vtbl = (
            get_func_relation.get_family_members(ea, bbs, self.import_eas,
            self.string_eas))
        func_type = get_func_relation.get_func_type(ea, self.import_eas)
        self.func_relations[ea] = {'parents': parents, 'children': children,
            'func_type': func_type, 'gvars': gvars, 'strings': strings,
            'struct_offsets': stroff, 'vftables': vtbl, 'cmt': {}, 'rcmt': {}}
        cmts = self.cmt_obj.collect_cmts_as_dict(ea)
        utils.deep_update(self.func_relations, cmts)
        for api_ea in apicalls:
            if api_ea == ida_idaapi.BADADDR:
                continue
            func_type = apicalls[api_ea]
            parents = get_func_relation.get_xrefs(api_ea)
            self.func_relations[api_ea] = {'parents': parents, 'children':
                {}, 'func_type': func_type, 'gvars': {}, 'strings': {},
                'struct_offsets': {}, 'vftables': {}, 'cmt': {}, 'rcmt': {}}
        for vtbl_ea in vtbl:
            if vtbl_ea == ida_idaapi.BADADDR:
                continue
            func_type = FT_VTB
            parents = get_func_relation.get_xrefs(vtbl_ea)
            self.func_relations[vtbl_ea] = {'parents': parents, 'children':
                vtbl[vtbl_ea], 'func_type': func_type, 'gvars': {},
                'strings': {}, 'struct_offsets': {}, 'vftables': {}, 'cmt':
                {}, 'rcmt': {}}
        if ea in self.dyn_apicalls:
            self.dyn_apicalls.pop(ea)
        for caller, func_name in get_func_relation.get_dyn_apicalls_partial(
            self.func_relations, ea):
            self.dyn_apicalls[caller] = func_name
        to_be_removed = set([])
        for k in self.paths_cache:
            if ea in [k[0], k[1]]:
                to_be_removed.add(k)
            else:
                for path in self.paths_cache[k]:
                    for p in path:
                        if ea in [p[0], p[1]]:
                            to_be_removed.add(k)
                            break
        for k in to_be_removed:
            self.paths_cache.pop(k)

    def update_data(self):
        if self in self.cto_data['insts']:
            if self.cto_data['refcnt'] <= 1 or self.cto_data['master'] == id(
                self):
                if self.config.debug:
                    self.dbg_print('took over the master to the %d' % self.
                        cto_data['master'])
                self.cto_data['master'] = id(self)
            self.cache_update()
    <mask token>

    def __del__(self):
        if self.config.debug:
            self.dbg_print('close_data was called')
        self.close_data()

    def refresh(self, ea=ida_idaapi.BADADDR, center=False):
        pass

    def refresh_all(self, ea=ida_idaapi.BADADDR, center=False):
        for inst in self.cto_data['insts']:
            inst.refresh(ea, center)

    def _jumpto(self, ea, use_opn=False):
        w, wt = self.get_widget()
        flag = False
        ida_kernwin.jumpto(ea)
        if ea in self.func_relations and not use_opn:
            flag = jump.jumpto_name(ea, w)
        elif ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                flag = jump.jumpto_opn(ea, opn, w)
            else:
                flag = jump.jumpto_offset(ea, w)
        else:
            flags = ida_bytes.get_flags(ea)
            if idc.is_code(flags):
                insn = ida_ua.insn_t()
                inslen = ida_ua.decode_insn(insn, ea)
                for opn in range(inslen):
                    if ida_ua.o_void == insn.ops[opn].type:
                        opn = -1
                        break
                    flag = jump.jumpto_opn(ea, opn, w)
                    if flag:
                        break
            else:
                flag = jump.jumpto_name(ea, w)
        return flag

    def jumpto(self, ea, use_opn=False):
        r = False
        try:
            r = self._jumpto(ea, use_opn)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg('Got a unexpected error (%s: %s) (%d)%s' % (str
                (type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r

    def push_lochist_jump(self, w=None):
        r = False
        try:
            r = jump.push_lochist_jump(w)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg('Got a unexpected error (%s: %s) (%d)%s' % (str
                (type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r

    def check_and_rename_var(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_var_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            flag = self.check_and_rename_var_decomp(w)
        return flag

    def check_and_rename_var_decomp(self, w):
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        if line is None:
            return False
        self.dbg_print(line)
        self.exec_ida_ui_action('hx:Rename')
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            curr_line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        self.dbg_print(curr_line)
        if line != curr_line:
            ea = ida_kernwin.get_screen_ea()
            self.dbg_print('Refreshing')
            self.refresh_all(ea)
        else:
            return False
        return True

    def check_and_rename_var_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        self.dbg_print('%x' % ea)
        name = self.get_highlighted_name(ea, w=w)
        self.dbg_print(name)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        op = None
        if ea in self.opnums:
            op = idc.print_operand(ea, self.opnums[ea])
        self.exec_ida_ui_action('MakeName')
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_op = None
        if ea in self.opnums:
            curr_op = idc.print_operand(ea, self.opnums[ea])
        self.dbg_print(curr_name)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.dbg_print('Start refreshing')
            self.refresh_all(ea)
        elif curr_op != op:
            self.dbg_print('Start refreshing', curr_op, op)
            callee, func_type, opn, _func_name = self.get_callee_info(ea)
            if callee is not None:
                optype = idc.get_operand_type(ea, opn)
                if optype in [ida_ua.o_displ, ida_ua.o_phrase]:
                    self.update_caller_tif(ea)
                    self.refresh_all(ea)
        else:
            self.dbg_print('Not changed')
            return False
        return True

    def check_and_rename_func_info(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_func_info_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_rename_func_info_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        name = self.get_highlighted_name(ea, w=w)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        self.exec_ida_ui_action('EditFunction')
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.refresh_all(ea)
        return True

    def check_and_add_rcmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_rcmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_add_rcmt_ida(self):
        ea = ida_kernwin.get_screen_ea()
        rcmt = ida_bytes.get_cmt(ea, 1)
        self.exec_ida_ui_action('MakeRptCmt')
        curr_rcmt = ida_bytes.get_cmt(ea, 1)
        if rcmt != curr_rcmt:
            self.partial_cache_update(ea)
            self.refresh_all(ea)
        return True
    <mask token>

    def check_and_add_cmt_ida(self):
        ea = ida_kernwin.get_screen_ea()
        cmt = ida_bytes.get_cmt(ea, 0)
        self.exec_ida_ui_action('MakeComment')
        curr_cmt = ida_bytes.get_cmt(ea, 0)
        if cmt != curr_cmt:
            self.update_caller_tif(ea, curr_cmt)
            self.partial_cache_update(ea)
            self.refresh_all(ea, center=True)
        return True

    def check_and_apply_strunct(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_apply_strunct_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_apply_strunct_ida(self):
        ea = ida_kernwin.get_screen_ea()
        line = ida_kernwin.get_curline()
        self.exec_ida_ui_action('OpStructOffset')
        curr_line = ida_kernwin.get_curline()
        if line != curr_line:
            self.update_caller_tif(ea)
            self.refresh_all(ea, center=True)
        return True

    def check_xrefs(self):
        self.exec_ida_ui_action('JumpOpXref')

    def add_cref(self, ea, func_chooser_t):
        fc = func_chooser_t('Choose the destination', self)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, func_name = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            func_type = get_func_relation.get_func_type(callee_ea, self.
                import_eas)
            cref_type = 0
            optype = idc.get_operand_type(ea, 0)
            if optype in [ida_ua.o_reg, ida_ua.o_displ, ida_ua.o_phrase
                ] and get_func_relation.is_call_insn(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_CN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_CF
            elif get_func_relation.is_indirect_jump_insn(ea
                ) and not get_func_relation.get_switch_info(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_JN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_JF
            else:
                ida_kernwin.msg('Do it on a indirecet calls or jumps' + os.
                    linesep)
            if cref_type > 0:
                r = ida_xref.add_cref(ea, callee_ea, cref_type | ida_xref.
                    XREF_USER)
                self.apply_tif(ea, func_name)
                return True
        return False

    def del_cref(self, ea, cref_chooser_t):
        fc = cref_chooser_t('Choose the destination', self, ea)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, _ = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            ida_xref.del_cref(ea, callee_ea, 0)
            f = ida_funcs.get_func(ea)
            if f:
                self.del_op_tinfo(f.start_ea, ea)
            return True
        return False

    def del_op_tinfo(self, func_ea, ea):
        if func_ea in self.func_relations:
            if ea in self.func_relations[func_ea]['children']:
                callee, func_type, opn, func_name = self.func_relations[func_ea
                    ]['children'][ea]
                if callee == ida_idaapi.BADADDR and func_name:
                    ida_nalt.del_op_tinfo(ea, opn)

    def get_callee_info(self, caller_ea):
        f = ida_funcs.get_func(caller_ea)
        if f and f.start_ea in self.func_relations:
            if caller_ea in self.func_relations[f.start_ea]['children']:
                callee, func_type, op, func_name = self.func_relations[f.
                    start_ea]['children'][caller_ea]
                return callee, func_type, op, func_name
        return None, None, None, None

    def apply_tif(self, caller, func_name):
        callee, func_type, opn, _func_name = self.get_callee_info(caller)
        if callee is not None:
            tif = tinfo.get_tinfo_by_name(func_name)
            if tif:
                tinfo.apply_tinfo_to_ea(tif, caller, opn)

    def update_caller_tif(self, ea, func_name=None):
        callee_ea, func_type, opn, _func_name = self.get_callee_info(ea)
        if callee_ea is not None:
            if func_name is None:
                if _func_name:
                    func_name = _func_name
                else:
                    func_name, func_type, v = (get_func_relation.
                        get_func_info_by_opstr(ea, opn))
            ida_nalt.del_op_tinfo(ea, opn)
            self.partial_cache_update(ea)
            if func_name:
                self.apply_tif(ea, func_name)
            return True
        return False

    def get_func_name(self, ea):
        f = ida_funcs.get_func(ea)
        func_name = ''
        if ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                v = idc.get_operand_value(ea, opn)
                func_name = ida_funcs.get_func_name(v)
                if not func_name:
                    func_name = ida_name.get_name(v)
        elif f and f.start_ea == ea:
            func_name = ida_funcs.get_func_name(ea)
        if not func_name:
            func_name = ida_name.get_name(ea)
        func_name = ida_name.validate_name(func_name, ida_name.VNT_VISIBLE)
        return func_name

    def _get_highlighted_name(self, ea, w):
        if w is None:
            return ''
        hl = ida_kernwin.get_highlight(w)
        self.dbg_print(hl)
        name = ''
        if hl is not None:
            for opn in range(2):
                v = idc.get_operand_value(ea, opn)
                fn = ida_name.get_name(v)
                if fn:
                    fn = ida_lines.tag_remove(fn)
                    fn = ida_name.validate_name(fn, ida_name.VNT_VISIBLE)
                    if fn == hl[0]:
                        name = fn
                        break
            if not name:
                fn = self.get_func_name(ea)
                self.dbg_print('fn: %s' % fn)
                if fn == hl[0]:
                    name = fn
        return name

    def get_highlighted_name(self, ea, w=None):
        if w is None:
            return ''
        return self._get_highlighted_name(ea, w)

    def get_widget(self):
        w, wt = utils.get_widget(curr_view=self.curr_view)
        return w, wt

    def get_focus(self, w=None):
        if w is None:
            w = self.GetWidget()
        if w is None:
            return False
        return ida_kernwin.activate_widget(w, True)

    def _exec_ui_action(self, action, w):
        if w is None:
            return False
        ida_kernwin.activate_widget(w, True)
        ida_kernwin.process_ui_action(action)
        return True

    def exec_ui_action(self, action, w=None):
        return self._exec_ui_action(action, w)

    def does_use_opn(self):
        return False

    def exec_ida_ui_action(self, action):
        ea = ida_kernwin.get_screen_ea()
        self.jumpto(ea, self.does_use_opn())
        w, wt = self.get_widget()
        return self.exec_ui_action(action, w=w)

    def print_config_info(self):
        for k in self.config:
            ida_kernwin.msg('%s: %s%s' % (k, str(self.config[k]), os.linesep))
        ida_kernwin.msg('max_depth: %d%s' % (self.max_depth, os.linesep))
        ida_kernwin.msg('start_ea: %x%s' % (self.start_ea, os.linesep))

<mask token>
ida_idaapi.require('get_func_relation')
ida_idaapi.require('cache_data')
ida_idaapi.require('jump')
ida_idaapi.require('tinfo')
ida_idaapi.require('config_base')
ida_idaapi.require('debug_print')
ida_idaapi.require('comments')
ida_idaapi.require('utils')
FT_UNK = get_func_relation.FT_UNK
FT_GEN = get_func_relation.FT_GEN
FT_LIB = get_func_relation.FT_LIB
FT_API = get_func_relation.FT_API
FT_MEM = get_func_relation.FT_MEM
FT_VAR = get_func_relation.FT_VAR
FT_STR = get_func_relation.FT_STR
FT_VTB = get_func_relation.FT_VTB


class cto_base(debug_print.debug):

    def __init__(self, cto_data=None, debug=False):
        debug_print.debug.__init__(self, debug)
        self.cmt_obj = comments.comment_t()
        self.decomp_avail = False
        try:
            import ida_hexrays
            self.decomp_avail = ida_hexrays.init_hexrays_plugin()
        except ImportError:
            pass
        self.curr_view = None
        w, wt = self.get_widget()
        self.curr_view = w
        if cto_data is None:
            self.config = self._config()
            if debug:
                self.config.debug = debug
            if self.config.debug:
                self.dbg_print('newly created and cto_data is None')
            self.cto_data = {'cto_data': {}, 'refcnt': 1, 'master': id(self
                ), 'config': self.config, 'insts': set([self])}
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self
                .config, self.config.debug)
            result = self.cd.dh.load_data(self.config)
            if result:
                self.cto_data['cto_data'] = result
                self.copy_cache_data()
                if debug:
                    self.config.debug = debug
            else:
                self.update_data()
                if debug:
                    self.config.debug = debug
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config,
                    self.cto_data['cto_data']))
        else:
            self.cto_data = cto_data
            self.copy_cache_data()
            self.cto_data['insts'].add(self)
            self.config = self.cto_data['config']
            if debug:
                self.config.debug = debug
            if self.config.debug:
                self.dbg_print('cto_data is NOT None')
            if self.config.debug:
                self.dbg_print(id(self), id(self.cto_data))
            if self.cto_data['refcnt'] < 1:
                if self.config.debug:
                    self.dbg_print('took master')
                self.cto_data['master'] = id(self)
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self
                .config, self.config.debug)
            if self.cto_data['master'] != id(self):
                self.cto_data['refcnt'] += 1


    class _config(config_base._config_base):

        def __init__(self):
            self.get_default_config()

        def get_default_config(self):
            self.debug = False
            self.skip_caller = False
            self.center_node = True
            self.display_children_in_parent_funcs = False
            self.dark_mode = False
            self.show_strings_nodes = False
            self.show_gvars_nodes = False
            self.show_stroff_nodes = False
            self.show_comment_nodes = False
            self.show_indirect_calls = True

    def dbg_print(self, *msg):
        if self.config.debug:
            self._dbg_print(*msg)

    def copy_cache_data(self):
        self.func_relations = self.cto_data['cto_data']['func_relations']
        self.import_eas = self.cto_data['cto_data']['import_eas']
        self.string_eas = self.cto_data['cto_data']['string_eas']
        self.paths_cache = self.cto_data['cto_data']['paths_cache']
        self.dyn_apicalls = self.cto_data['cto_data']['dyn_apicalls']
        self.opnums = self.cto_data['cto_data']['opnums']
        self.eps = self.cto_data['cto_data']['eps']
        self.vtbl_refs = self.cto_data['cto_data']['vtbl_refs']

    def cache_cmt_update(self):
        for func_ea in self.cto_data['cto_data']['func_relations']:
            self.cto_data['cto_data']['func_relations'][func_ea]['cmt'] = {}
            self.cto_data['cto_data']['func_relations'][func_ea]['rcmt'] = {}
        cmts = self.cmt_obj.collect_cmts_as_dict()
        utils.deep_update(self.cto_data['cto_data']['func_relations'], cmts)
        for inst in self.cto_data['insts']:
            inst.copy_cache_data()

    def cache_update(self):
        self.cto_data['cto_data'] = {}
        self.cto_data['cto_data']['func_relations'], self.cto_data['cto_data'][
            'import_eas'], self.cto_data['cto_data']['string_eas'
            ] = get_func_relation.get_func_relations()
        self.cto_data['cto_data']['paths_cache'] = {}
        self.cto_data['cto_data']['dyn_apicalls'
            ] = get_func_relation.get_dyn_apicalls(self.cto_data['cto_data'
            ]['func_relations'])
        self.cto_data['cto_data']['opnums'] = get_func_relation.get_opnums(self
            .cto_data['cto_data']['func_relations'], 'children')
        self.cto_data['cto_data']['eps'] = list(get_func_relation.
            get_entry_points())
        self.cto_data['cto_data']['vtbl_refs'
            ] = get_func_relation.get_vtbl_refs(self.cto_data['cto_data'][
            'func_relations'])
        self.cache_cmt_update()
        for inst in self.cto_data['insts']:
            inst.copy_cache_data()

    def partial_cache_update(self, ea):
        f, bbs = get_func_relation.get_func_bbs(ea)
        if f:
            ea = f.start_ea
        parents, children, apicalls, gvars, strings, stroff, vtbl = (
            get_func_relation.get_family_members(ea, bbs, self.import_eas,
            self.string_eas))
        func_type = get_func_relation.get_func_type(ea, self.import_eas)
        self.func_relations[ea] = {'parents': parents, 'children': children,
            'func_type': func_type, 'gvars': gvars, 'strings': strings,
            'struct_offsets': stroff, 'vftables': vtbl, 'cmt': {}, 'rcmt': {}}
        cmts = self.cmt_obj.collect_cmts_as_dict(ea)
        utils.deep_update(self.func_relations, cmts)
        for api_ea in apicalls:
            if api_ea == ida_idaapi.BADADDR:
                continue
            func_type = apicalls[api_ea]
            parents = get_func_relation.get_xrefs(api_ea)
            self.func_relations[api_ea] = {'parents': parents, 'children':
                {}, 'func_type': func_type, 'gvars': {}, 'strings': {},
                'struct_offsets': {}, 'vftables': {}, 'cmt': {}, 'rcmt': {}}
        for vtbl_ea in vtbl:
            if vtbl_ea == ida_idaapi.BADADDR:
                continue
            func_type = FT_VTB
            parents = get_func_relation.get_xrefs(vtbl_ea)
            self.func_relations[vtbl_ea] = {'parents': parents, 'children':
                vtbl[vtbl_ea], 'func_type': func_type, 'gvars': {},
                'strings': {}, 'struct_offsets': {}, 'vftables': {}, 'cmt':
                {}, 'rcmt': {}}
        if ea in self.dyn_apicalls:
            self.dyn_apicalls.pop(ea)
        for caller, func_name in get_func_relation.get_dyn_apicalls_partial(
            self.func_relations, ea):
            self.dyn_apicalls[caller] = func_name
        to_be_removed = set([])
        for k in self.paths_cache:
            if ea in [k[0], k[1]]:
                to_be_removed.add(k)
            else:
                for path in self.paths_cache[k]:
                    for p in path:
                        if ea in [p[0], p[1]]:
                            to_be_removed.add(k)
                            break
        for k in to_be_removed:
            self.paths_cache.pop(k)

    def update_data(self):
        if self in self.cto_data['insts']:
            if self.cto_data['refcnt'] <= 1 or self.cto_data['master'] == id(
                self):
                if self.config.debug:
                    self.dbg_print('took over the master to the %d' % self.
                        cto_data['master'])
                self.cto_data['master'] = id(self)
            self.cache_update()

    def close_data(self):
        if self in self.cto_data['insts']:
            self.cto_data['refcnt'] -= 1
            if self.config.debug:
                self.dbg_print(id(self), id(self.cto_data), self.cto_data[
                    'refcnt'])
            self.cto_data['insts'].remove(self)
            if self.cto_data['refcnt'] <= 1 or self.cto_data['master'] == id(
                self) or len(self.cto_data['insts']) == 0:
                if self.config.debug:
                    self.dbg_print('save pickle')
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config,
                    self.cto_data['cto_data']))
            if self.cto_data['master'] == id(self) and len(self.cto_data[
                'insts']) > 0:
                self.cto_data['master'] = id(next(iter(self.cto_data['insts']))
                    )
                if self.config.debug:
                    self.dbg_print('took over the master to the %d' % self.
                        cto_data['master'])

    def __del__(self):
        if self.config.debug:
            self.dbg_print('close_data was called')
        self.close_data()

    def refresh(self, ea=ida_idaapi.BADADDR, center=False):
        pass

    def refresh_all(self, ea=ida_idaapi.BADADDR, center=False):
        for inst in self.cto_data['insts']:
            inst.refresh(ea, center)

    def _jumpto(self, ea, use_opn=False):
        w, wt = self.get_widget()
        flag = False
        ida_kernwin.jumpto(ea)
        if ea in self.func_relations and not use_opn:
            flag = jump.jumpto_name(ea, w)
        elif ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                flag = jump.jumpto_opn(ea, opn, w)
            else:
                flag = jump.jumpto_offset(ea, w)
        else:
            flags = ida_bytes.get_flags(ea)
            if idc.is_code(flags):
                insn = ida_ua.insn_t()
                inslen = ida_ua.decode_insn(insn, ea)
                for opn in range(inslen):
                    if ida_ua.o_void == insn.ops[opn].type:
                        opn = -1
                        break
                    flag = jump.jumpto_opn(ea, opn, w)
                    if flag:
                        break
            else:
                flag = jump.jumpto_name(ea, w)
        return flag

    def jumpto(self, ea, use_opn=False):
        r = False
        try:
            r = self._jumpto(ea, use_opn)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg('Got a unexpected error (%s: %s) (%d)%s' % (str
                (type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r

    def push_lochist_jump(self, w=None):
        r = False
        try:
            r = jump.push_lochist_jump(w)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg('Got a unexpected error (%s: %s) (%d)%s' % (str
                (type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r

    def check_and_rename_var(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_var_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            flag = self.check_and_rename_var_decomp(w)
        return flag

    def check_and_rename_var_decomp(self, w):
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        if line is None:
            return False
        self.dbg_print(line)
        self.exec_ida_ui_action('hx:Rename')
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            curr_line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        self.dbg_print(curr_line)
        if line != curr_line:
            ea = ida_kernwin.get_screen_ea()
            self.dbg_print('Refreshing')
            self.refresh_all(ea)
        else:
            return False
        return True

    def check_and_rename_var_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        self.dbg_print('%x' % ea)
        name = self.get_highlighted_name(ea, w=w)
        self.dbg_print(name)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        op = None
        if ea in self.opnums:
            op = idc.print_operand(ea, self.opnums[ea])
        self.exec_ida_ui_action('MakeName')
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_op = None
        if ea in self.opnums:
            curr_op = idc.print_operand(ea, self.opnums[ea])
        self.dbg_print(curr_name)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.dbg_print('Start refreshing')
            self.refresh_all(ea)
        elif curr_op != op:
            self.dbg_print('Start refreshing', curr_op, op)
            callee, func_type, opn, _func_name = self.get_callee_info(ea)
            if callee is not None:
                optype = idc.get_operand_type(ea, opn)
                if optype in [ida_ua.o_displ, ida_ua.o_phrase]:
                    self.update_caller_tif(ea)
                    self.refresh_all(ea)
        else:
            self.dbg_print('Not changed')
            return False
        return True

    def check_and_rename_func_info(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_func_info_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_rename_func_info_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        name = self.get_highlighted_name(ea, w=w)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        self.exec_ida_ui_action('EditFunction')
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.refresh_all(ea)
        return True

    def check_and_add_rcmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_rcmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_add_rcmt_ida(self):
        ea = ida_kernwin.get_screen_ea()
        rcmt = ida_bytes.get_cmt(ea, 1)
        self.exec_ida_ui_action('MakeRptCmt')
        curr_rcmt = ida_bytes.get_cmt(ea, 1)
        if rcmt != curr_rcmt:
            self.partial_cache_update(ea)
            self.refresh_all(ea)
        return True

    def check_and_add_cmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_cmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_add_cmt_ida(self):
        ea = ida_kernwin.get_screen_ea()
        cmt = ida_bytes.get_cmt(ea, 0)
        self.exec_ida_ui_action('MakeComment')
        curr_cmt = ida_bytes.get_cmt(ea, 0)
        if cmt != curr_cmt:
            self.update_caller_tif(ea, curr_cmt)
            self.partial_cache_update(ea)
            self.refresh_all(ea, center=True)
        return True

    def check_and_apply_strunct(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_apply_strunct_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_apply_strunct_ida(self):
        ea = ida_kernwin.get_screen_ea()
        line = ida_kernwin.get_curline()
        self.exec_ida_ui_action('OpStructOffset')
        curr_line = ida_kernwin.get_curline()
        if line != curr_line:
            self.update_caller_tif(ea)
            self.refresh_all(ea, center=True)
        return True

    def check_xrefs(self):
        self.exec_ida_ui_action('JumpOpXref')

    def add_cref(self, ea, func_chooser_t):
        fc = func_chooser_t('Choose the destination', self)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, func_name = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            func_type = get_func_relation.get_func_type(callee_ea, self.
                import_eas)
            cref_type = 0
            optype = idc.get_operand_type(ea, 0)
            if optype in [ida_ua.o_reg, ida_ua.o_displ, ida_ua.o_phrase
                ] and get_func_relation.is_call_insn(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_CN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_CF
            elif get_func_relation.is_indirect_jump_insn(ea
                ) and not get_func_relation.get_switch_info(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_JN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_JF
            else:
                ida_kernwin.msg('Do it on a indirecet calls or jumps' + os.
                    linesep)
            if cref_type > 0:
                r = ida_xref.add_cref(ea, callee_ea, cref_type | ida_xref.
                    XREF_USER)
                self.apply_tif(ea, func_name)
                return True
        return False

    def del_cref(self, ea, cref_chooser_t):
        fc = cref_chooser_t('Choose the destination', self, ea)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, _ = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            ida_xref.del_cref(ea, callee_ea, 0)
            f = ida_funcs.get_func(ea)
            if f:
                self.del_op_tinfo(f.start_ea, ea)
            return True
        return False

    def del_op_tinfo(self, func_ea, ea):
        if func_ea in self.func_relations:
            if ea in self.func_relations[func_ea]['children']:
                callee, func_type, opn, func_name = self.func_relations[func_ea
                    ]['children'][ea]
                if callee == ida_idaapi.BADADDR and func_name:
                    ida_nalt.del_op_tinfo(ea, opn)

    def get_callee_info(self, caller_ea):
        f = ida_funcs.get_func(caller_ea)
        if f and f.start_ea in self.func_relations:
            if caller_ea in self.func_relations[f.start_ea]['children']:
                callee, func_type, op, func_name = self.func_relations[f.
                    start_ea]['children'][caller_ea]
                return callee, func_type, op, func_name
        return None, None, None, None

    def apply_tif(self, caller, func_name):
        callee, func_type, opn, _func_name = self.get_callee_info(caller)
        if callee is not None:
            tif = tinfo.get_tinfo_by_name(func_name)
            if tif:
                tinfo.apply_tinfo_to_ea(tif, caller, opn)

    def update_caller_tif(self, ea, func_name=None):
        callee_ea, func_type, opn, _func_name = self.get_callee_info(ea)
        if callee_ea is not None:
            if func_name is None:
                if _func_name:
                    func_name = _func_name
                else:
                    func_name, func_type, v = (get_func_relation.
                        get_func_info_by_opstr(ea, opn))
            ida_nalt.del_op_tinfo(ea, opn)
            self.partial_cache_update(ea)
            if func_name:
                self.apply_tif(ea, func_name)
            return True
        return False

    def get_func_name(self, ea):
        f = ida_funcs.get_func(ea)
        func_name = ''
        if ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                v = idc.get_operand_value(ea, opn)
                func_name = ida_funcs.get_func_name(v)
                if not func_name:
                    func_name = ida_name.get_name(v)
        elif f and f.start_ea == ea:
            func_name = ida_funcs.get_func_name(ea)
        if not func_name:
            func_name = ida_name.get_name(ea)
        func_name = ida_name.validate_name(func_name, ida_name.VNT_VISIBLE)
        return func_name

    def _get_highlighted_name(self, ea, w):
        if w is None:
            return ''
        hl = ida_kernwin.get_highlight(w)
        self.dbg_print(hl)
        name = ''
        if hl is not None:
            for opn in range(2):
                v = idc.get_operand_value(ea, opn)
                fn = ida_name.get_name(v)
                if fn:
                    fn = ida_lines.tag_remove(fn)
                    fn = ida_name.validate_name(fn, ida_name.VNT_VISIBLE)
                    if fn == hl[0]:
                        name = fn
                        break
            if not name:
                fn = self.get_func_name(ea)
                self.dbg_print('fn: %s' % fn)
                if fn == hl[0]:
                    name = fn
        return name

    def get_highlighted_name(self, ea, w=None):
        if w is None:
            return ''
        return self._get_highlighted_name(ea, w)

    def get_widget(self):
        w, wt = utils.get_widget(curr_view=self.curr_view)
        return w, wt

    def get_focus(self, w=None):
        if w is None:
            w = self.GetWidget()
        if w is None:
            return False
        return ida_kernwin.activate_widget(w, True)

    def _exec_ui_action(self, action, w):
        if w is None:
            return False
        ida_kernwin.activate_widget(w, True)
        ida_kernwin.process_ui_action(action)
        return True

    def exec_ui_action(self, action, w=None):
        return self._exec_ui_action(action, w)

    def does_use_opn(self):
        return False

    def exec_ida_ui_action(self, action):
        ea = ida_kernwin.get_screen_ea()
        self.jumpto(ea, self.does_use_opn())
        w, wt = self.get_widget()
        return self.exec_ui_action(action, w=w)

    def print_config_info(self):
        for k in self.config:
            ida_kernwin.msg('%s: %s%s' % (k, str(self.config[k]), os.linesep))
        ida_kernwin.msg('max_depth: %d%s' % (self.max_depth, os.linesep))
        ida_kernwin.msg('start_ea: %x%s' % (self.start_ea, os.linesep))

import idc
import ida_idaapi
import ida_kernwin
import ida_ua
import ida_bytes
import ida_xref
import ida_funcs
import ida_nalt
import ida_name
import ida_lines
import traceback
import os
import sys
import get_func_relation
import cache_data
import jump
import tinfo
import config_base
import debug_print
import comments
import utils
ida_idaapi.require('get_func_relation')
ida_idaapi.require('cache_data')
ida_idaapi.require('jump')
ida_idaapi.require('tinfo')
ida_idaapi.require('config_base')
ida_idaapi.require('debug_print')
ida_idaapi.require('comments')
ida_idaapi.require('utils')
FT_UNK = get_func_relation.FT_UNK
FT_GEN = get_func_relation.FT_GEN
FT_LIB = get_func_relation.FT_LIB
FT_API = get_func_relation.FT_API
FT_MEM = get_func_relation.FT_MEM
FT_VAR = get_func_relation.FT_VAR
FT_STR = get_func_relation.FT_STR
FT_VTB = get_func_relation.FT_VTB


class cto_base(debug_print.debug):

    def __init__(self, cto_data=None, debug=False):
        debug_print.debug.__init__(self, debug)
        self.cmt_obj = comments.comment_t()
        self.decomp_avail = False
        try:
            import ida_hexrays
            self.decomp_avail = ida_hexrays.init_hexrays_plugin()
        except ImportError:
            pass
        self.curr_view = None
        w, wt = self.get_widget()
        self.curr_view = w
        if cto_data is None:
            self.config = self._config()
            if debug:
                self.config.debug = debug
            if self.config.debug:
                self.dbg_print('newly created and cto_data is None')
            self.cto_data = {'cto_data': {}, 'refcnt': 1, 'master': id(self
                ), 'config': self.config, 'insts': set([self])}
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self
                .config, self.config.debug)
            result = self.cd.dh.load_data(self.config)
            if result:
                self.cto_data['cto_data'] = result
                self.copy_cache_data()
                if debug:
                    self.config.debug = debug
            else:
                self.update_data()
                if debug:
                    self.config.debug = debug
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config,
                    self.cto_data['cto_data']))
        else:
            self.cto_data = cto_data
            self.copy_cache_data()
            self.cto_data['insts'].add(self)
            self.config = self.cto_data['config']
            if debug:
                self.config.debug = debug
            if self.config.debug:
                self.dbg_print('cto_data is NOT None')
            if self.config.debug:
                self.dbg_print(id(self), id(self.cto_data))
            if self.cto_data['refcnt'] < 1:
                if self.config.debug:
                    self.dbg_print('took master')
                self.cto_data['master'] = id(self)
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self
                .config, self.config.debug)
            if self.cto_data['master'] != id(self):
                self.cto_data['refcnt'] += 1


    class _config(config_base._config_base):

        def __init__(self):
            self.get_default_config()

        def get_default_config(self):
            self.debug = False
            self.skip_caller = False
            self.center_node = True
            self.display_children_in_parent_funcs = False
            self.dark_mode = False
            self.show_strings_nodes = False
            self.show_gvars_nodes = False
            self.show_stroff_nodes = False
            self.show_comment_nodes = False
            self.show_indirect_calls = True

    def dbg_print(self, *msg):
        if self.config.debug:
            self._dbg_print(*msg)

    def copy_cache_data(self):
        self.func_relations = self.cto_data['cto_data']['func_relations']
        self.import_eas = self.cto_data['cto_data']['import_eas']
        self.string_eas = self.cto_data['cto_data']['string_eas']
        self.paths_cache = self.cto_data['cto_data']['paths_cache']
        self.dyn_apicalls = self.cto_data['cto_data']['dyn_apicalls']
        self.opnums = self.cto_data['cto_data']['opnums']
        self.eps = self.cto_data['cto_data']['eps']
        self.vtbl_refs = self.cto_data['cto_data']['vtbl_refs']

    def cache_cmt_update(self):
        for func_ea in self.cto_data['cto_data']['func_relations']:
            self.cto_data['cto_data']['func_relations'][func_ea]['cmt'] = {}
            self.cto_data['cto_data']['func_relations'][func_ea]['rcmt'] = {}
        cmts = self.cmt_obj.collect_cmts_as_dict()
        utils.deep_update(self.cto_data['cto_data']['func_relations'], cmts)
        for inst in self.cto_data['insts']:
            inst.copy_cache_data()

    def cache_update(self):
        self.cto_data['cto_data'] = {}
        self.cto_data['cto_data']['func_relations'], self.cto_data['cto_data'][
            'import_eas'], self.cto_data['cto_data']['string_eas'
            ] = get_func_relation.get_func_relations()
        self.cto_data['cto_data']['paths_cache'] = {}
        self.cto_data['cto_data']['dyn_apicalls'
            ] = get_func_relation.get_dyn_apicalls(self.cto_data['cto_data'
            ]['func_relations'])
        self.cto_data['cto_data']['opnums'] = get_func_relation.get_opnums(self
            .cto_data['cto_data']['func_relations'], 'children')
        self.cto_data['cto_data']['eps'] = list(get_func_relation.
            get_entry_points())
        self.cto_data['cto_data']['vtbl_refs'
            ] = get_func_relation.get_vtbl_refs(self.cto_data['cto_data'][
            'func_relations'])
        self.cache_cmt_update()
        for inst in self.cto_data['insts']:
            inst.copy_cache_data()

    def partial_cache_update(self, ea):
        f, bbs = get_func_relation.get_func_bbs(ea)
        if f:
            ea = f.start_ea
        parents, children, apicalls, gvars, strings, stroff, vtbl = (
            get_func_relation.get_family_members(ea, bbs, self.import_eas,
            self.string_eas))
        func_type = get_func_relation.get_func_type(ea, self.import_eas)
        self.func_relations[ea] = {'parents': parents, 'children': children,
            'func_type': func_type, 'gvars': gvars, 'strings': strings,
            'struct_offsets': stroff, 'vftables': vtbl, 'cmt': {}, 'rcmt': {}}
        cmts = self.cmt_obj.collect_cmts_as_dict(ea)
        utils.deep_update(self.func_relations, cmts)
        for api_ea in apicalls:
            if api_ea == ida_idaapi.BADADDR:
                continue
            func_type = apicalls[api_ea]
            parents = get_func_relation.get_xrefs(api_ea)
            self.func_relations[api_ea] = {'parents': parents, 'children':
                {}, 'func_type': func_type, 'gvars': {}, 'strings': {},
                'struct_offsets': {}, 'vftables': {}, 'cmt': {}, 'rcmt': {}}
        for vtbl_ea in vtbl:
            if vtbl_ea == ida_idaapi.BADADDR:
                continue
            func_type = FT_VTB
            parents = get_func_relation.get_xrefs(vtbl_ea)
            self.func_relations[vtbl_ea] = {'parents': parents, 'children':
                vtbl[vtbl_ea], 'func_type': func_type, 'gvars': {},
                'strings': {}, 'struct_offsets': {}, 'vftables': {}, 'cmt':
                {}, 'rcmt': {}}
        if ea in self.dyn_apicalls:
            self.dyn_apicalls.pop(ea)
        for caller, func_name in get_func_relation.get_dyn_apicalls_partial(
            self.func_relations, ea):
            self.dyn_apicalls[caller] = func_name
        to_be_removed = set([])
        for k in self.paths_cache:
            if ea in [k[0], k[1]]:
                to_be_removed.add(k)
            else:
                for path in self.paths_cache[k]:
                    for p in path:
                        if ea in [p[0], p[1]]:
                            to_be_removed.add(k)
                            break
        for k in to_be_removed:
            self.paths_cache.pop(k)

    def update_data(self):
        if self in self.cto_data['insts']:
            if self.cto_data['refcnt'] <= 1 or self.cto_data['master'] == id(
                self):
                if self.config.debug:
                    self.dbg_print('took over the master to the %d' % self.
                        cto_data['master'])
                self.cto_data['master'] = id(self)
            self.cache_update()

    def close_data(self):
        if self in self.cto_data['insts']:
            self.cto_data['refcnt'] -= 1
            if self.config.debug:
                self.dbg_print(id(self), id(self.cto_data), self.cto_data[
                    'refcnt'])
            self.cto_data['insts'].remove(self)
            if self.cto_data['refcnt'] <= 1 or self.cto_data['master'] == id(
                self) or len(self.cto_data['insts']) == 0:
                if self.config.debug:
                    self.dbg_print('save pickle')
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config,
                    self.cto_data['cto_data']))
            if self.cto_data['master'] == id(self) and len(self.cto_data[
                'insts']) > 0:
                self.cto_data['master'] = id(next(iter(self.cto_data['insts']))
                    )
                if self.config.debug:
                    self.dbg_print('took over the master to the %d' % self.
                        cto_data['master'])

    def __del__(self):
        if self.config.debug:
            self.dbg_print('close_data was called')
        self.close_data()

    def refresh(self, ea=ida_idaapi.BADADDR, center=False):
        pass

    def refresh_all(self, ea=ida_idaapi.BADADDR, center=False):
        for inst in self.cto_data['insts']:
            inst.refresh(ea, center)

    def _jumpto(self, ea, use_opn=False):
        w, wt = self.get_widget()
        flag = False
        ida_kernwin.jumpto(ea)
        if ea in self.func_relations and not use_opn:
            flag = jump.jumpto_name(ea, w)
        elif ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                flag = jump.jumpto_opn(ea, opn, w)
            else:
                flag = jump.jumpto_offset(ea, w)
        else:
            flags = ida_bytes.get_flags(ea)
            if idc.is_code(flags):
                insn = ida_ua.insn_t()
                inslen = ida_ua.decode_insn(insn, ea)
                for opn in range(inslen):
                    if ida_ua.o_void == insn.ops[opn].type:
                        opn = -1
                        break
                    flag = jump.jumpto_opn(ea, opn, w)
                    if flag:
                        break
            else:
                flag = jump.jumpto_name(ea, w)
        return flag

    def jumpto(self, ea, use_opn=False):
        r = False
        try:
            r = self._jumpto(ea, use_opn)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg('Got a unexpected error (%s: %s) (%d)%s' % (str
                (type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r

    def push_lochist_jump(self, w=None):
        r = False
        try:
            r = jump.push_lochist_jump(w)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg('Got a unexpected error (%s: %s) (%d)%s' % (str
                (type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r

    def check_and_rename_var(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_var_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            flag = self.check_and_rename_var_decomp(w)
        return flag

    def check_and_rename_var_decomp(self, w):
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        if line is None:
            return False
        self.dbg_print(line)
        self.exec_ida_ui_action('hx:Rename')
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            curr_line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        self.dbg_print(curr_line)
        if line != curr_line:
            ea = ida_kernwin.get_screen_ea()
            self.dbg_print('Refreshing')
            self.refresh_all(ea)
        else:
            return False
        return True

    def check_and_rename_var_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        self.dbg_print('%x' % ea)
        name = self.get_highlighted_name(ea, w=w)
        self.dbg_print(name)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        op = None
        if ea in self.opnums:
            op = idc.print_operand(ea, self.opnums[ea])
        self.exec_ida_ui_action('MakeName')
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_op = None
        if ea in self.opnums:
            curr_op = idc.print_operand(ea, self.opnums[ea])
        self.dbg_print(curr_name)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.dbg_print('Start refreshing')
            self.refresh_all(ea)
        elif curr_op != op:
            self.dbg_print('Start refreshing', curr_op, op)
            callee, func_type, opn, _func_name = self.get_callee_info(ea)
            if callee is not None:
                optype = idc.get_operand_type(ea, opn)
                if optype in [ida_ua.o_displ, ida_ua.o_phrase]:
                    self.update_caller_tif(ea)
                    self.refresh_all(ea)
        else:
            self.dbg_print('Not changed')
            return False
        return True

    def check_and_rename_func_info(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_func_info_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_rename_func_info_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        name = self.get_highlighted_name(ea, w=w)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        self.exec_ida_ui_action('EditFunction')
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.refresh_all(ea)
        return True

    def check_and_add_rcmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_rcmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_add_rcmt_ida(self):
        ea = ida_kernwin.get_screen_ea()
        rcmt = ida_bytes.get_cmt(ea, 1)
        self.exec_ida_ui_action('MakeRptCmt')
        curr_rcmt = ida_bytes.get_cmt(ea, 1)
        if rcmt != curr_rcmt:
            self.partial_cache_update(ea)
            self.refresh_all(ea)
        return True

    def check_and_add_cmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_cmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_add_cmt_ida(self):
        ea = ida_kernwin.get_screen_ea()
        cmt = ida_bytes.get_cmt(ea, 0)
        self.exec_ida_ui_action('MakeComment')
        curr_cmt = ida_bytes.get_cmt(ea, 0)
        if cmt != curr_cmt:
            self.update_caller_tif(ea, curr_cmt)
            self.partial_cache_update(ea)
            self.refresh_all(ea, center=True)
        return True

    def check_and_apply_strunct(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_apply_strunct_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag

    def check_and_apply_strunct_ida(self):
        ea = ida_kernwin.get_screen_ea()
        line = ida_kernwin.get_curline()
        self.exec_ida_ui_action('OpStructOffset')
        curr_line = ida_kernwin.get_curline()
        if line != curr_line:
            self.update_caller_tif(ea)
            self.refresh_all(ea, center=True)
        return True

    def check_xrefs(self):
        self.exec_ida_ui_action('JumpOpXref')

    def add_cref(self, ea, func_chooser_t):
        fc = func_chooser_t('Choose the destination', self)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, func_name = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            func_type = get_func_relation.get_func_type(callee_ea, self.
                import_eas)
            cref_type = 0
            optype = idc.get_operand_type(ea, 0)
            if optype in [ida_ua.o_reg, ida_ua.o_displ, ida_ua.o_phrase
                ] and get_func_relation.is_call_insn(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_CN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_CF
            elif get_func_relation.is_indirect_jump_insn(ea
                ) and not get_func_relation.get_switch_info(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_JN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_JF
            else:
                ida_kernwin.msg('Do it on a indirecet calls or jumps' + os.
                    linesep)
            if cref_type > 0:
                r = ida_xref.add_cref(ea, callee_ea, cref_type | ida_xref.
                    XREF_USER)
                self.apply_tif(ea, func_name)
                return True
        return False

    def del_cref(self, ea, cref_chooser_t):
        fc = cref_chooser_t('Choose the destination', self, ea)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, _ = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            ida_xref.del_cref(ea, callee_ea, 0)
            f = ida_funcs.get_func(ea)
            if f:
                self.del_op_tinfo(f.start_ea, ea)
            return True
        return False

    def del_op_tinfo(self, func_ea, ea):
        if func_ea in self.func_relations:
            if ea in self.func_relations[func_ea]['children']:
                callee, func_type, opn, func_name = self.func_relations[func_ea
                    ]['children'][ea]
                if callee == ida_idaapi.BADADDR and func_name:
                    ida_nalt.del_op_tinfo(ea, opn)

    def get_callee_info(self, caller_ea):
        f = ida_funcs.get_func(caller_ea)
        if f and f.start_ea in self.func_relations:
            if caller_ea in self.func_relations[f.start_ea]['children']:
                callee, func_type, op, func_name = self.func_relations[f.
                    start_ea]['children'][caller_ea]
                return callee, func_type, op, func_name
        return None, None, None, None

    def apply_tif(self, caller, func_name):
        callee, func_type, opn, _func_name = self.get_callee_info(caller)
        if callee is not None:
            tif = tinfo.get_tinfo_by_name(func_name)
            if tif:
                tinfo.apply_tinfo_to_ea(tif, caller, opn)

    def update_caller_tif(self, ea, func_name=None):
        callee_ea, func_type, opn, _func_name = self.get_callee_info(ea)
        if callee_ea is not None:
            if func_name is None:
                if _func_name:
                    func_name = _func_name
                else:
                    func_name, func_type, v = (get_func_relation.
                        get_func_info_by_opstr(ea, opn))
            ida_nalt.del_op_tinfo(ea, opn)
            self.partial_cache_update(ea)
            if func_name:
                self.apply_tif(ea, func_name)
            return True
        return False

    def get_func_name(self, ea):
        f = ida_funcs.get_func(ea)
        func_name = ''
        if ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                v = idc.get_operand_value(ea, opn)
                func_name = ida_funcs.get_func_name(v)
                if not func_name:
                    func_name = ida_name.get_name(v)
        elif f and f.start_ea == ea:
            func_name = ida_funcs.get_func_name(ea)
        if not func_name:
            func_name = ida_name.get_name(ea)
        func_name = ida_name.validate_name(func_name, ida_name.VNT_VISIBLE)
        return func_name

    def _get_highlighted_name(self, ea, w):
        if w is None:
            return ''
        hl = ida_kernwin.get_highlight(w)
        self.dbg_print(hl)
        name = ''
        if hl is not None:
            for opn in range(2):
                v = idc.get_operand_value(ea, opn)
                fn = ida_name.get_name(v)
                if fn:
                    fn = ida_lines.tag_remove(fn)
                    fn = ida_name.validate_name(fn, ida_name.VNT_VISIBLE)
                    if fn == hl[0]:
                        name = fn
                        break
            if not name:
                fn = self.get_func_name(ea)
                self.dbg_print('fn: %s' % fn)
                if fn == hl[0]:
                    name = fn
        return name

    def get_highlighted_name(self, ea, w=None):
        if w is None:
            return ''
        return self._get_highlighted_name(ea, w)

    def get_widget(self):
        w, wt = utils.get_widget(curr_view=self.curr_view)
        return w, wt

    def get_focus(self, w=None):
        if w is None:
            w = self.GetWidget()
        if w is None:
            return False
        return ida_kernwin.activate_widget(w, True)

    def _exec_ui_action(self, action, w):
        if w is None:
            return False
        ida_kernwin.activate_widget(w, True)
        ida_kernwin.process_ui_action(action)
        return True

    def exec_ui_action(self, action, w=None):
        return self._exec_ui_action(action, w)

    def does_use_opn(self):
        return False

    def exec_ida_ui_action(self, action):
        ea = ida_kernwin.get_screen_ea()
        self.jumpto(ea, self.does_use_opn())
        w, wt = self.get_widget()
        return self.exec_ui_action(action, w=w)

    def print_config_info(self):
        for k in self.config:
            ida_kernwin.msg('%s: %s%s' % (k, str(self.config[k]), os.linesep))
        ida_kernwin.msg('max_depth: %d%s' % (self.max_depth, os.linesep))
        ida_kernwin.msg('start_ea: %x%s' % (self.start_ea, os.linesep))

import idc
import ida_idaapi
import ida_kernwin
import ida_ua
import ida_bytes
import ida_xref
import ida_funcs
import ida_nalt
import ida_name
import ida_lines

import traceback
import os
import sys
#import re
#import threading

import get_func_relation
import cache_data
import jump
import tinfo
import config_base
import debug_print
import comments
import utils

ida_idaapi.require("get_func_relation")
ida_idaapi.require("cache_data")
ida_idaapi.require("jump")
ida_idaapi.require("tinfo")
ida_idaapi.require("config_base")
ida_idaapi.require("debug_print")
ida_idaapi.require("comments")
ida_idaapi.require("utils")

FT_UNK = get_func_relation.FT_UNK
FT_GEN = get_func_relation.FT_GEN
FT_LIB = get_func_relation.FT_LIB
FT_API = get_func_relation.FT_API
FT_MEM = get_func_relation.FT_MEM
FT_VAR = get_func_relation.FT_VAR
FT_STR = get_func_relation.FT_STR
FT_VTB = get_func_relation.FT_VTB

class cto_base(debug_print.debug):
    
    def __init__(self, cto_data=None, debug=False):
        #super(cto_base, self).__init__(debug)
        debug_print.debug.__init__(self, debug)
        
        self.cmt_obj = comments.comment_t()

        self.decomp_avail = False
        try:
            import ida_hexrays
            self.decomp_avail = ida_hexrays.init_hexrays_plugin()
        except ImportError:
            pass
        
        # get current available widget
        self.curr_view = None
        w, wt = self.get_widget()
        self.curr_view = w
        
        # it should be the first instance
        if cto_data is None:
            self.config = self._config()
            if debug:
                self.config.debug = debug
            if self.config.debug: self.dbg_print("newly created and cto_data is None")
            self.cto_data = {'cto_data':{}, 'refcnt':1, 'master':id(self), 'config': self.config, 'insts':set([self])}
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self.config, self.config.debug)
            result = self.cd.dh.load_data(self.config)
            if result:
                self.cto_data['cto_data'] = result
                self.copy_cache_data()
                if debug:
                    self.config.debug = debug
            else:
                self.update_data()
                #self.cache_update()
                if debug:
                    self.config.debug = debug
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config, self.cto_data['cto_data']))
        # it has an external data
        else:
            self.cto_data = cto_data
            self.copy_cache_data()
            self.cto_data['insts'].add(self)
            self.config = self.cto_data["config"]
            if debug:
                self.config.debug = debug
            if self.config.debug: self.dbg_print("cto_data is NOT None")
            if self.config.debug: self.dbg_print(id(self), id(self.cto_data))
            # take master if ref count is less than 1. Note that, at this moment,
            # ref count is not increased yet. Do not include 1.
            if self.cto_data['refcnt'] < 1:
                if self.config.debug: self.dbg_print("took master")
                self.cto_data['master'] = id(self)
            self.cd = cache_data.cache_data(self.cto_data['cto_data'], self.config, self.config.debug)
            # if this instance is a slave, 
            if self.cto_data['master'] != id(self):
                self.cto_data["refcnt"] += 1
        
    # for potable config values for taking over subgraphs
    class _config(config_base._config_base):
        def __init__(self):
            self.get_default_config()
        
        def get_default_config(self):
            self.debug = False
            self.skip_caller = False
            self.center_node = True
            self.display_children_in_parent_funcs = False
            self.dark_mode = False
            self.show_strings_nodes = False
            self.show_gvars_nodes = False
            self.show_stroff_nodes = False
            self.show_comment_nodes = False
            self.show_indirect_calls = True
    
    def dbg_print(self, *msg):
        if self.config.debug:
            self._dbg_print(*msg)

    def copy_cache_data(self):
        self.func_relations = self.cto_data["cto_data"]["func_relations"]
        self.import_eas = self.cto_data["cto_data"]["import_eas"]
        self.string_eas = self.cto_data["cto_data"]["string_eas"]
        self.paths_cache = self.cto_data["cto_data"]["paths_cache"]
        self.dyn_apicalls = self.cto_data["cto_data"]["dyn_apicalls"]
        self.opnums = self.cto_data["cto_data"]["opnums"]
        self.eps = self.cto_data["cto_data"]["eps"]
        self.vtbl_refs = self.cto_data["cto_data"]["vtbl_refs"]

    def cache_cmt_update(self):
        # clear cache
        for func_ea in self.cto_data["cto_data"]["func_relations"]:
            self.cto_data["cto_data"]["func_relations"][func_ea]['cmt'] = {}
            self.cto_data["cto_data"]["func_relations"][func_ea]['rcmt'] = {}
            
        # get comments and merge them
        cmts = self.cmt_obj.collect_cmts_as_dict()
        utils.deep_update(self.cto_data["cto_data"]["func_relations"], cmts)
        
        # recursive for instance
        for inst in self.cto_data["insts"]:
            inst.copy_cache_data()
            
        
    def cache_update(self):
        self.cto_data["cto_data"] = {}
        self.cto_data["cto_data"]["func_relations"], self.cto_data["cto_data"]["import_eas"], self.cto_data["cto_data"]["string_eas"] = get_func_relation.get_func_relations()
        self.cto_data["cto_data"]["paths_cache"] = {}
        self.cto_data["cto_data"]["dyn_apicalls"] = get_func_relation.get_dyn_apicalls(self.cto_data["cto_data"]["func_relations"])
        self.cto_data["cto_data"]["opnums"] = get_func_relation.get_opnums(self.cto_data["cto_data"]["func_relations"], "children")
        self.cto_data["cto_data"]["eps"] = list(get_func_relation.get_entry_points())
        self.cto_data["cto_data"]["vtbl_refs"] = get_func_relation.get_vtbl_refs(self.cto_data["cto_data"]["func_relations"])
        
        self.cache_cmt_update()
        
        # recursive for instance
        for inst in self.cto_data["insts"]:
            inst.copy_cache_data()
    
    def partial_cache_update(self, ea):
        # updating a function that the ea belongs to.
        f, bbs = get_func_relation.get_func_bbs(ea)
        if f:
            ea = f.start_ea
        parents, children, apicalls, gvars, strings, stroff, vtbl = get_func_relation.get_family_members(ea, bbs, self.import_eas, self.string_eas)
        func_type = get_func_relation.get_func_type(ea, self.import_eas)
        self.func_relations[ea] = {"parents":parents, "children":children, "func_type":func_type, "gvars":gvars, "strings":strings, "struct_offsets":stroff, "vftables":vtbl , "cmt":{}, "rcmt":{}}
        cmts = self.cmt_obj.collect_cmts_as_dict(ea)
        utils.deep_update(self.func_relations, cmts)
        
        # updating apicalls that are called in the function
        for api_ea in apicalls:
            if api_ea == ida_idaapi.BADADDR:
                continue
            func_type = apicalls[api_ea]
            parents = get_func_relation.get_xrefs(api_ea)
            self.func_relations[api_ea] = {"parents":parents, "children":{}, "func_type":func_type, "gvars":{}, "strings":{}, "struct_offsets":{}, "vftables":{}, "cmt":{}, "rcmt":{}}
            
        # updating vftables that are called in the function
        for vtbl_ea in vtbl:
            if vtbl_ea == ida_idaapi.BADADDR:
                continue
            func_type = FT_VTB
            parents = get_func_relation.get_xrefs(vtbl_ea)
            self.func_relations[vtbl_ea] = {"parents":parents, "children":vtbl[vtbl_ea], "func_type":func_type, "gvars":{}, "strings":{}, "struct_offsets":{}, "vftables":{}, "cmt":{}, "rcmt":{}}
            
        # updating dyn_apicalls table
        if ea in self.dyn_apicalls:
            self.dyn_apicalls.pop(ea)
        for caller, func_name in get_func_relation.get_dyn_apicalls_partial(self.func_relations, ea):
            self.dyn_apicalls[caller] = func_name
            
        # updating paths cache (just removing paths related to the ea)
        to_be_removed = set([])
        for k in self.paths_cache:
            if ea in [k[0], k[1]]:
                to_be_removed.add(k)
            else:
                for path in self.paths_cache[k]:
                    for p in path:
                        if ea in [p[0], p[1]]:
                            to_be_removed.add(k)
                            break
        for k in to_be_removed:
            self.paths_cache.pop(k)
        #self.cd.dh.save_data(self.cd.dh.convert_data(self.config, self.cto_data['cto_data']))
            
    # update data online
    def update_data(self):
        # check the refcount
        if self in self.cto_data['insts']:
            if self.cto_data["refcnt"] <= 1 or self.cto_data["master"] == id(self):
                # raise to the master if recount == 1
                if self.config.debug: self.dbg_print("took over the master to the %d" % self.cto_data["master"])
                self.cto_data["master"] = id(self)
                
            self.cache_update()
            #self.cd.dh.save_data(self.cd.dh.convert_data(self.config, self.cto_data['cto_data']))
    
    def close_data(self):
        if self in self.cto_data['insts']:
            # decrease the refcount
            self.cto_data['refcnt'] -= 1
            if self.config.debug: self.dbg_print(id(self), id(self.cto_data), self.cto_data['refcnt'])
            # remove instance from the cached data
            self.cto_data['insts'].remove(self)
            if self.cto_data["refcnt"] <= 1 or self.cto_data["master"] == id(self) or len(self.cto_data['insts']) == 0:
                # save the pickle
                if self.config.debug: self.dbg_print("save pickle")
                self.cd.dh.save_data(self.cd.dh.convert_data(self.config, self.cto_data['cto_data']))
            if self.cto_data["master"] == id(self) and len(self.cto_data['insts']) > 0:
                self.cto_data["master"] = id(next(iter(self.cto_data['insts'])))
                if self.config.debug: self.dbg_print("took over the master to the %d" % self.cto_data["master"])
                
    def __del__(self):
        if self.config.debug: self.dbg_print("close_data was called")
        self.close_data()
            
    #def refresh(self, ea=ida_idaapi.BADADDR):
    #    self.refresh_all(ea)
    
    def refresh(self, ea=ida_idaapi.BADADDR, center=False):
        pass
    
    def refresh_all(self, ea=ida_idaapi.BADADDR, center=False):
        for inst in self.cto_data['insts']:
            inst.refresh(ea, center)
            
    def _jumpto(self, ea, use_opn=False):
        w, wt = self.get_widget()
        flag = False
        ida_kernwin.jumpto(ea)
        if ea in self.func_relations and not use_opn:
            flag = jump.jumpto_name(ea, w)
        elif ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                flag = jump.jumpto_opn(ea, opn, w)
            else:
                # find offset line for vftable funcs
                flag = jump.jumpto_offset(ea, w)
                
        # for others such as referenced strings in an instruction
        else:
            flags = ida_bytes.get_flags(ea)
            if idc.is_code(flags):
                # for strings in funcs and other variables in funcs
                insn = ida_ua.insn_t()
                inslen = ida_ua.decode_insn(insn, ea)
                for opn in range(inslen):
                    if ida_ua.o_void == insn.ops[opn].type:
                        opn = -1
                        break
                    flag = jump.jumpto_opn(ea, opn, w)
                    if flag:
                        break
            else:
                flag = jump.jumpto_name(ea, w)
        return flag

    def jumpto(self, ea, use_opn=False):
        r = False
        try:
            r = self._jumpto(ea, use_opn)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg("Got a unexpected error (%s: %s) (%d)%s" % (str(type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r
    
    def push_lochist_jump(self, w=None):
        r = False
        try:
            r = jump.push_lochist_jump(w)
        except Exception as e:
            exc_type, exc_obj, tb = sys.exc_info()
            lineno = tb.tb_lineno
            ida_kernwin.msg("Got a unexpected error (%s: %s) (%d)%s" % (str(type(e)), e, lineno, os.linesep))
            if self.config.debug:
                self.dbg_print(traceback.format_exc())
            else:
                traceback.print_exc()
        return r
    
    def check_and_rename_var(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_var_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            flag = self.check_and_rename_var_decomp(w)
        return flag
    
    def check_and_rename_var_decomp(self, w):
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        if line is None:
            return False
        self.dbg_print(line)
        
        self.exec_ida_ui_action("hx:Rename")
        
        highlight, vu = jump.get_highlight_decomp(w)
        if highlight:
            curr_line = jump.get_current_line_decomp(highlight, vu)
        else:
            return False
        self.dbg_print(curr_line)
        if line != curr_line:
            ea = ida_kernwin.get_screen_ea()
            self.dbg_print("Refreshing")
            self.refresh_all(ea)
        else:
            return False
        return True
        
    def check_and_rename_var_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        self.dbg_print("%x" % ea)
        name = self.get_highlighted_name(ea, w=w)
        self.dbg_print(name)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        #line = ida_kernwin.get_curline()
        op = None
        if ea in self.opnums:
            op = idc.print_operand(ea, self.opnums[ea])
        self.exec_ida_ui_action("MakeName")
        curr_name = self.get_highlighted_name(ea, w=w)
        #ida_kernwin.refresh_idaview_anyway()
        #curr_line = ida_kernwin.get_curline()
        curr_op = None
        if ea in self.opnums:
            curr_op = idc.print_operand(ea, self.opnums[ea])
        self.dbg_print(curr_name)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.dbg_print("Start refreshing")
            self.refresh_all(ea)
        # for renaming a struct member
        #elif line != curr_line or curr_op != op:
        elif curr_op != op:
            self.dbg_print("Start refreshing", curr_op, op)
            callee, func_type, opn, _func_name = self.get_callee_info(ea)
            if callee is not None:
                optype = idc.get_operand_type(ea, opn)
                if optype in [ida_ua.o_displ, ida_ua.o_phrase]:
                    self.update_caller_tif(ea)
                      #self.exec_ui_action("EmptyStack")
                    self.refresh_all(ea)
        else:
            self.dbg_print("Not changed")
            return False
        return True

    def check_and_rename_func_info(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_rename_func_info_ida(w)
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    
    def check_and_rename_func_info_ida(self, w):
        ea = ida_kernwin.get_screen_ea()
        name = self.get_highlighted_name(ea, w=w)
        line = ida_lines.tag_remove(ida_kernwin.get_curline())
        self.exec_ida_ui_action("EditFunction")
        #ida_kernwin.refresh_idaview_anyway()
        curr_name = self.get_highlighted_name(ea, w=w)
        curr_line = ida_lines.tag_remove(ida_kernwin.get_curline())
        if name != curr_name or line != curr_line:
            self.refresh_all(ea)
        return True
            
    def check_and_add_rcmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_rcmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    
    def check_and_add_rcmt_ida(self):
        # repeatable comment
        ea = ida_kernwin.get_screen_ea()
        rcmt = ida_bytes.get_cmt(ea, 1)
        self.exec_ida_ui_action("MakeRptCmt")
        #ida_kernwin.refresh_idaview_anyway()
        curr_rcmt = ida_bytes.get_cmt(ea, 1)
        if rcmt != curr_rcmt:
            self.partial_cache_update(ea)
            #self.exec_ui_action("EmptyStack")
            self.refresh_all(ea)
        return True
            
    def check_and_add_cmt(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_add_cmt_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    
    def check_and_add_cmt_ida(self):
        # comment
        ea = ida_kernwin.get_screen_ea()
        cmt = ida_bytes.get_cmt(ea, 0)
        self.exec_ida_ui_action("MakeComment")
        #ida_kernwin.refresh_idaview_anyway()
        curr_cmt = ida_bytes.get_cmt(ea, 0)
        if cmt != curr_cmt:
            self.update_caller_tif(ea, curr_cmt)
            #self.exec_ui_action("EmptyStack")
            #self.refresh()
            self.partial_cache_update(ea)
            self.refresh_all(ea, center=True)
        return True
            
    def check_and_apply_strunct(self):
        flag = True
        w, wt = self.get_widget()
        if wt == ida_kernwin.BWN_DISASM:
            flag = self.check_and_apply_strunct_ida()
        elif wt == ida_kernwin.BWN_PSEUDOCODE:
            pass
        return flag
    
    def check_and_apply_strunct_ida(self):
        # apply structure
        ea = ida_kernwin.get_screen_ea()
        line = ida_kernwin.get_curline()
        self.exec_ida_ui_action("OpStructOffset")
        #ida_kernwin.refresh_idaview_anyway()
        curr_line = ida_kernwin.get_curline()
        #f = ida_funcs.get_func(ea)
        if line != curr_line:
            self.update_caller_tif(ea)
            #self.exec_ui_action("EmptyStack")
            #self.refresh_with_center_node()
            self.refresh_all(ea, center=True)
            #self.refresh()
        return True
            
    def check_xrefs(self):
        # show xrefs to
        self.exec_ida_ui_action("JumpOpXref")
        
    def add_cref(self, ea, func_chooser_t):
        fc = func_chooser_t("Choose the destination", self)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, func_name = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            func_type = get_func_relation.get_func_type(callee_ea, self.import_eas)
            cref_type = 0
            optype = idc.get_operand_type(ea, 0)
            if optype in [ida_ua.o_reg, ida_ua.o_displ, ida_ua.o_phrase] and get_func_relation.is_call_insn(ea):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_CN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_CF
            elif (get_func_relation.is_indirect_jump_insn(ea) and not get_func_relation.get_switch_info(ea)):
                if func_type == FT_GEN:
                    cref_type = ida_xref.fl_JN
                elif func_type in [FT_LIB, FT_API]:
                    cref_type = ida_xref.fl_JF
            else:
                ida_kernwin.msg("Do it on a indirecet calls or jumps" + os.linesep)
            if cref_type > 0:
                r = ida_xref.add_cref(ea, callee_ea, cref_type|ida_xref.XREF_USER)
                self.apply_tif(ea, func_name)
                return True
        return False
        
    def del_cref(self, ea, cref_chooser_t):
        fc = cref_chooser_t("Choose the destination", self, ea)
        selected = fc.Show(modal=True)
        if selected >= 0:
            callee_ea, _ = fc.items[selected]
            callee_ea = int(callee_ea, 16)
            ida_xref.del_cref(ea, callee_ea, 0)
            f = ida_funcs.get_func(ea)
            if f:
                self.del_op_tinfo(f.start_ea, ea)
            return True
        return False
        
    def del_op_tinfo(self, func_ea, ea):
        if func_ea in self.func_relations:
            if ea in self.func_relations[func_ea]['children']:
                callee, func_type, opn, func_name = self.func_relations[func_ea]['children'][ea]
                if callee == ida_idaapi.BADADDR and func_name:
                    ida_nalt.del_op_tinfo(ea, opn)
        
    def get_callee_info(self, caller_ea):
        f = ida_funcs.get_func(caller_ea)
        if f and f.start_ea in self.func_relations:
            if caller_ea in self.func_relations[f.start_ea]['children']:
                callee, func_type, op, func_name =  self.func_relations[f.start_ea]['children'][caller_ea]
                return callee, func_type, op, func_name
        return None, None, None, None
        
    def apply_tif(self, caller, func_name):
        callee, func_type, opn, _func_name = self.get_callee_info(caller)
        if callee is not None:
            tif = tinfo.get_tinfo_by_name(func_name)
            if tif:
                tinfo.apply_tinfo_to_ea(tif, caller, opn)
                
    def update_caller_tif(self, ea, func_name=None):
        callee_ea, func_type, opn, _func_name = self.get_callee_info(ea)
        if callee_ea is not None:
            if func_name is None:
                #if callee_ea == ida_idaapi.BADADDR and _func_name:
                if _func_name:
                    func_name = _func_name
                else:
                    func_name, func_type, v = get_func_relation.get_func_info_by_opstr(ea, opn)
            ida_nalt.del_op_tinfo(ea, opn)
            #if ea in self.dyn_apicalls:
            #    self.dyn_apicalls.pop(ea)
            self.partial_cache_update(ea)
            if func_name:
                self.apply_tif(ea, func_name)
            return True
        return False
    
    def get_func_name(self, ea):
        f = ida_funcs.get_func(ea)
        func_name = ""
        if ea in self.opnums:
            opn = self.opnums[ea]
            if opn >= 0:
                v = idc.get_operand_value(ea, opn)
                func_name = ida_funcs.get_func_name(v)
                if not func_name:
                    func_name = ida_name.get_name(v)
        elif f and f.start_ea == ea:
            func_name = ida_funcs.get_func_name(ea)
        if not func_name:
            func_name = ida_name.get_name(ea)
        func_name = ida_name.validate_name(func_name, ida_name.VNT_VISIBLE)
        return func_name
    
    def _get_highlighted_name(self, ea, w):
        if w is None:
            return ""
        hl = ida_kernwin.get_highlight(w)
        self.dbg_print(hl)
        name = ""
        if hl is not None:
            for opn in range(2):
                v = idc.get_operand_value(ea, opn)
                fn = ida_name.get_name(v)
                if fn:
                    fn = ida_lines.tag_remove(fn)
                    fn = ida_name.validate_name(fn, ida_name.VNT_VISIBLE)
                    if fn == hl[0]:
                        name = fn
                        break
            if not name:
                fn = self.get_func_name(ea)
                #fn = ida_name.validate_name(fn, ida_name.VNT_VISIBLE)
                self.dbg_print("fn: %s" % fn)
                if fn == hl[0]:
                    name = fn
        return name
    
    def get_highlighted_name(self, ea, w=None):
        if w is None:
            return ""
        return self._get_highlighted_name(ea, w)
    
    def get_widget(self):
        w, wt = utils.get_widget(curr_view=self.curr_view)
        return w, wt
    
    def get_focus(self, w=None):
        if w is None:
            w = self.GetWidget()
        if w is None:
            return False
        return ida_kernwin.activate_widget(w, True)
    
    def _exec_ui_action(self, action, w):
        if w is None:
            return False
        ida_kernwin.activate_widget(w, True)
        ida_kernwin.process_ui_action(action)
        return True
            
    def exec_ui_action(self, action, w=None):
        return self._exec_ui_action(action, w)

    def does_use_opn(self):
        return False
    
    def exec_ida_ui_action(self, action):
        ea = ida_kernwin.get_screen_ea()
        self.jumpto(ea, self.does_use_opn())
        #w = ida_kernwin.find_widget(self.ida_view)
        w, wt = self.get_widget()
        return self.exec_ui_action(action, w=w)
        
    def print_config_info(self):
        for k in self.config:
            ida_kernwin.msg("%s: %s%s" % (k, str(self.config[k]), os.linesep))
        ida_kernwin.msg("max_depth: %d%s" % (self.max_depth, os.linesep))
        ida_kernwin.msg("start_ea: %x%s" %  (self.start_ea, os.linesep))
    
