From d2784190640fa2d1e691e88f6ddcb293bdb7c06e Mon Sep 17 00:00:00 2001 From: "elias.bachaalany" Date: Thu, 24 Sep 2009 14:20:29 +0000 Subject: [PATCH] added Choose2() support --- build.py | 2 + examples/ex_choose2.py | 84 ++++ examples/ex_func_chooser.py | 38 ++ python/init.py | 10 +- swig/kernwin.i | 740 ++++++++++++++++++++++++++++++++++++ 5 files changed, 869 insertions(+), 5 deletions(-) create mode 100644 examples/ex_choose2.py create mode 100644 examples/ex_func_chooser.py diff --git a/build.py b/build.py index f6ba1ed..c9f31da 100644 --- a/build.py +++ b/build.py @@ -72,6 +72,8 @@ BINDIST_MANIFEST = [ "examples/structure.py", "examples/ex_gdl_qflow_chart.py", "examples/ex_strings.py", + "examples/ex_func_chooser.py", + "examples/ex_choose2.py", "examples/ex_debug_names.py" ] diff --git a/examples/ex_choose2.py b/examples/ex_choose2.py new file mode 100644 index 0000000..69efc4f --- /dev/null +++ b/examples/ex_choose2.py @@ -0,0 +1,84 @@ +import idaapi +from idaapi import Choose2 + +class MyChoose2(Choose2): + + def __init__(self, title, nb = 5): + Choose2.__init__(self, title, [ ["Address", 10], ["Name", 30] ]) + self.n = 0 + self.items = [ self.make_item() for x in xrange(0, nb+1) ] + self.icon = 5 + self.selcount = 0 + self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"] + print "created", str(self) + + def OnClose(self): + print "closed", str(self) + + def OnEditLine(self, n): + self.items[n][1] = self.items[n][1] + "*" + print "editing", str(n) + + def OnInsertLine(self): + self.items.append(self.make_item()) + print "insert line" + + def OnSelectLine(self, n): + self.selcount += 1 + Warning("[%02d] selectline '%s'" % (self.selcount, n)) + + def OnGetLine(self, n): + print "getline", str(n) + return self.items[n] + + def OnGetSize(self): + print "getsize" + return len(self.items) + + def OnDeleteLine(self, n): + print "del ",str(n) + del self.items[n] + return n + + def OnRefresh(self, n): + print "refresh", n + return n + + def OnCommand(self, n, cmd_id): + if cmd_id == self.cmd_a: + print "command A selected @", n + elif cmd_id == self.cmd_b: + print "command B selected @", n + else: + print "Unknown command:", cmd_id, "@", n + return 1 + + def OnGetIcon(self, n): + r = self.items[n] + t = self.icon + r[1].count("*") + print "geticon", n, t + return t + + def show(self): + t = self.Show() + if t < 0: + return False + self.cmd_a = self.AddCommand("command A") + self.cmd_b = self.AddCommand("command B") + return True + + def make_item(self): + r = [str(self.n), "func_%04d" % self.n] + self.n += 1 + return r + + def OnGetLineAttr(self, n): + print "getlineattr", n + if n == 1: + return [0xFF0000, 0] + +for i in xrange(1, 5+1): + c = MyChoose2("choose2 - sample %d" % i, i*2) + r = c.show() + print r + \ No newline at end of file diff --git a/examples/ex_func_chooser.py b/examples/ex_func_chooser.py new file mode 100644 index 0000000..01aafef --- /dev/null +++ b/examples/ex_func_chooser.py @@ -0,0 +1,38 @@ +import idaapi +import idautils +import idc + +class MyChoose2(Choose2): + + def __init__(self, title): + Choose2.__init__(self, title, [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | Choose2.CHCOL_PLAIN] ]) + self.n = 0 + self.icon = 41 + self.PopulateItems() + + def PopulateItems(self): + self.items = [ [hex(x), GetFunctionName(x), x] for x in idautils.Functions() ] + + def OnClose(self): + print "closed ", self.title + + def OnSelectLine(self, n): + idc.Jump(self.items[n][2]) + + def OnGetLine(self, n): + return self.items[n] + + def OnGetSize(self): + return len(self.items) + + def OnDeleteLine(self, n): + ea = self.items[n][2] + idc.DelFunction(ea) + return n + + def OnRefresh(self, n): + self.PopulateItems() + return n + +c = MyChoose2("My functions list") +c.Show() \ No newline at end of file diff --git a/python/init.py b/python/init.py index 7e91b78..89c7773 100644 --- a/python/init.py +++ b/python/init.py @@ -2,7 +2,7 @@ #------------------------------------------------------------ # IDAPython - Python plugin for Interactive Disassembler Pro # -# Copyright (c) 2004-2009 Gergely Erdelyi +# Copyright (c) 2004-2009 Gergely Erdelyi # # All rights reserved. # @@ -37,7 +37,7 @@ def addscriptpath(script): if pathitem == scriptpath: pathfound = 1 break - + if pathfound == 0: sys.path.append(scriptpath) @@ -124,7 +124,7 @@ print_banner() #----------------------------------------------------------- # Import all the required modules #----------------------------------------------------------- -from idaapi import Choose, get_user_idadir, cvar +from idaapi import Choose, get_user_idadir, cvar, Choose2 from idc import * from idautils import * import idaapi @@ -147,7 +147,7 @@ class ScriptBox(Choose): return None n = self.choose() - + if n > 0: return self.list[n-1] else: @@ -166,7 +166,7 @@ scriptbox = ScriptBox() # watchdog.activate(10) # Use 10-second timeout # # Note: The watchdog only works for code running inside -# functions, not in global/module namespace. +# functions, not in global/module namespace. #------------------------------------------------------------- class WatchDog(): """ diff --git a/swig/kernwin.i b/swig/kernwin.i index ce97884..eb2e0e1 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -141,7 +141,635 @@ uint32 choose_choose(PyObject *self, int x1,int y1, int width); +PyObject *choose2_find(const char *title); +int choose2_add_command(PyObject *self, const char *caption, int flags=0, int menu_index=-1, int icon=-1); +void choose2_refresh(PyObject *self); +void choose2_close(PyObject *self); +int choose2_show(PyObject *self); +void choose2_activate(PyObject *self); + %{ + +//------------------------------------------------------------------------- +// Chooser2 wrapper class +//------------------------------------------------------------------------- +#include + +//------------------------------------------------------------------------ +static PyObject *PyObject_TryGetAttrString(PyObject *object, const char *attr) +{ + if (!PyObject_HasAttrString(object, attr)) + return NULL; + return PyObject_GetAttrString(object, attr); +} + +//------------------------------------------------------------------------ +// Some defines +#define POPUP_NAMES_COUNT 4 +#define MAX_CHOOSER_MENU_COMMANDS 10 +#define thisobj ((py_choose2_t *) obj) +#define thisdecl py_choose2_t *_this = thisobj +#define MENU_COMMAND_CB(id) static uint32 idaapi s_menu_command_##id(void *obj, uint32 n) { return thisobj->on_command(id, int(n)); } +#define DECL_MENU_COMMAND_CB(id) s_menu_command_##id +#define S_ON_EDIT_LINE "OnEditLine" +#define S_ON_INSERT_LINE "OnInsertLine" +#define S_ON_GET_LINE "OnGetLine" +#define S_ON_DELETE_LINE "OnDeleteLine" +#define S_ON_REFRESH "OnRefresh" +#define S_ON_SELECT_LINE "OnSelectLine" +#define S_ON_COMMAND "OnCommand" +#define S_ON_GET_ICON "OnGetIcon" +#ifdef CH_ATTRS + #define S_ON_GET_LINE_ATTR "OnGetLineAttr" +#endif +#define S_ON_GET_SIZE "OnGetSize" +#define S_ON_CLOSE "OnClose" +#define CHOOSE2_HAVE_DEL 0x0001 +#define CHOOSE2_HAVE_INS 0x0002 +#define CHOOSE2_HAVE_UPDATE 0x0004 +#define CHOOSE2_HAVE_EDIT 0x0008 +#define CHOOSE2_HAVE_ENTER 0x0010 +#define CHOOSE2_HAVE_GETICON 0x0020 +#define CHOOSE2_HAVE_GETATTR 0x0040 +#define CHOOSE2_HAVE_COMMAND 0x0080 +#define CHOOSE2_HAVE_ONCLOSE 0x0100 + +//------------------------------------------------------------------------ +// Helper functions +class py_choose2_t; +typedef std::map pychoose2_to_choose2_map_t; +static pychoose2_to_choose2_map_t choosers; + +py_choose2_t *choose2_find_instance(PyObject *self) +{ + pychoose2_to_choose2_map_t::iterator it = choosers.find(self); + if (it == choosers.end()) + return NULL; + return it->second; +} + +void choose2_add_instance(PyObject *self, py_choose2_t *c2) +{ + choosers[self] = c2; +} + +void choose2_del_instance(PyObject *self) +{ + pychoose2_to_choose2_map_t::iterator it = choosers.find(self); + if (it != choosers.end()) + choosers.erase(it); +} + +//------------------------------------------------------------------------ +class py_choose2_t +{ +private: + int flags; + int cb_flags; + qstring title; + PyObject *self; + qstrvec_t cols; + // the number of declarations should follow the MAX_CHOOSER_MENU_COMMANDS value + MENU_COMMAND_CB(0) MENU_COMMAND_CB(1) + MENU_COMMAND_CB(2) MENU_COMMAND_CB(3) + MENU_COMMAND_CB(4) MENU_COMMAND_CB(5) + MENU_COMMAND_CB(6) MENU_COMMAND_CB(7) + MENU_COMMAND_CB(8) MENU_COMMAND_CB(9) + static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS]; + int menu_cb_idx; + //------------------------------------------------------------------------ + // Static methods to dispatch to member functions + //------------------------------------------------------------------------ +#ifdef CH_ATTRS + static int idaapi ui_cb(void *obj, int notification_code, va_list va) + { + if ( notification_code != ui_get_chooser_item_attrs ) + return 0; + va_arg(va, void *); + int n = int(va_arg(va, uint32)); + chooser_item_attrs_t *attr = va_arg(va, chooser_item_attrs_t *); + thisobj->on_get_line_attr(n, attr); + return 1; + } +#endif + static uint32 idaapi s_sizer(void *obj) + { + return (uint32)thisobj->on_get_size(); + } + static void idaapi s_getl(void *obj, uint32 n, char * const *arrptr) + { + thisobj->on_get_line(int(n), arrptr); + } + static uint32 idaapi s_del(void *obj, uint32 n) + { + return uint32(thisobj->on_delete_line(int(n))); + } + static void idaapi s_ins(void *obj) + { + thisobj->on_insert_line(); + } + static uint32 idaapi s_update(void *obj, uint32 n) + { + return uint32(thisobj->on_refresh(int(n))); + } + static void idaapi s_edit(void *obj, uint32 n) + { + thisobj->on_edit_line(int(n)); + } + static void idaapi s_enter(void * obj, uint32 n) + { + thisobj->on_select_line(int(n)); + } + static int idaapi s_get_icon(void *obj, uint32 n) + { + return thisobj->on_get_icon(int(n)); + } + static void idaapi s_destroy(void *obj) + { + thisobj->on_close(); + } +private: + //------------------------------------------------------------------------ + // Member functions corresponding to each chooser2() callback + //------------------------------------------------------------------------ + void on_get_line(int lineno, char * const *line_arr) + { + if (lineno == 0) + { + for (size_t i=0;i=0;i--) + line_arr[i][0] = '\0'; + + // Call Python + PyObject *list = PyObject_CallMethod(self, S_ON_GET_LINE, "l", lineno - 1); + if (list == NULL) + return; + for (int i=ncols-1;i>=0;i--) + { + PyObject *item = PyList_GetItem(list, Py_ssize_t(i)); + if (item == NULL) + continue; + const char *str = PyString_AsString(item); + if (str != NULL) + qstrncpy(line_arr[i], str, MAXSTR); + } + Py_DECREF(list); + } + + size_t on_get_size() + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_GET_SIZE, NULL); + if (pyres == NULL) + return 0; + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res; + } + + void on_close() + { +#ifdef CH_ATTRS + if ( (flags & CH_ATTRS) != 0 ) + unhook_from_notification_point(HT_UI, ui_cb, this); +#endif + // Call Python + PyObject *pyres = PyObject_CallMethod(self, S_ON_CLOSE, NULL); + Py_XDECREF(pyres); + Py_XDECREF(self); + + // Remove from list + choose2_del_instance(self); + + // delete this instance if none modal + if ( (flags & CH_MODAL) == 0 ) + delete this; +} + + int on_delete_line(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_DELETE_LINE, "l", lineno - 1); + if (pyres == NULL) + return lineno; + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res + 1; + } + + int on_refresh(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_REFRESH, "l", lineno - 1); + if (pyres == NULL) + return lineno; + size_t res = PyInt_AsLong(pyres); + Py_DECREF(pyres); + return res + 1; + } + + void on_insert_line() + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_INSERT_LINE, NULL); + Py_XDECREF(pyres); + } + + void on_select_line(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_SELECT_LINE, "l", lineno - 1); + Py_XDECREF(pyres); + } + + void on_edit_line(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_EDIT_LINE, "l", lineno - 1); + Py_XDECREF(pyres); + } + + int on_command(int cmd_id, int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_COMMAND, "ll", lineno - 1, cmd_id); + if (pyres==NULL) + return lineno; + size_t res = PyInt_AsLong(pyres); + Py_XDECREF(pyres); + return res; + } + + int on_get_icon(int lineno) + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_GET_ICON, "l", lineno - 1); + size_t res = PyInt_AsLong(pyres); + Py_XDECREF(pyres); + return res; + } +#ifdef CH_ATTRS + void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) + { + PyObject *pyres = PyObject_CallMethod(self, S_ON_GET_LINE_ATTR, "l", lineno - 1); + if (pyres == NULL) + return; + + if (PyList_Check(pyres)) + { + PyObject *item; + if ((item = PyList_GetItem(pyres, 0)) != NULL) + attr->color = PyInt_AsLong(item); + if ((item = PyList_GetItem(pyres, 1)) != NULL) + attr->flags = PyInt_AsLong(item); + } + Py_XDECREF(pyres); + } +#endif +public: + //------------------------------------------------------------------------ + // Public methods + //------------------------------------------------------------------------ + py_choose2_t() + { + flags = 0; + cb_flags = 0; + menu_cb_idx = 0; + self = NULL; + } + + static py_choose2_t *find_chooser(const char *title) + { + return (py_choose2_t *) get_chooser_obj(title); + } + + void close() + { + close_chooser(title.c_str()); + } + bool activate() + { + TForm *frm = find_tform(title.c_str()); + if (frm == NULL) + return false; + switchto_tform(frm, true); + return true; + } + + int choose2( + int fl, + int ncols, + const int *widths, + const char *title, + int deflt = -1, + // An array of 4 strings: ("Insert", "Delete", "Edit", "Refresh" + const char * const *popup_names = NULL, + int icon = -1, + int x1 = -1, int y1 = -1, int x2 = -1, int y2 = -1) + { + flags = fl; +#ifdef CH_ATTRS + if ( (flags & CH_ATTRS) != 0 ) + { + if ( !hook_to_notification_point(HT_UI, ui_cb, this) ) + flags &= ~CH_ATTRS; + } +#endif + this->title = title; + return ::choose2( + flags, + x1, y1, x2, y2, + this, + ncols, widths, + s_sizer, + s_getl, + title, + icon, + deflt, + cb_flags & CHOOSE2_HAVE_DEL ? s_del : NULL, + cb_flags & CHOOSE2_HAVE_INS ? s_ins : NULL, + cb_flags & CHOOSE2_HAVE_UPDATE ? s_update : NULL, + cb_flags & CHOOSE2_HAVE_EDIT ? s_edit : NULL, + cb_flags & CHOOSE2_HAVE_ENTER ? s_enter : NULL, + s_destroy, + popup_names, + cb_flags & CHOOSE2_HAVE_GETICON ? s_get_icon : NULL); + } + + int add_command(const char *caption, int flags=0, int menu_index=-1, int icon=-1) + { + if (menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS) + return -1; + bool ret = add_chooser_command(title.c_str(), caption, menu_cbs[menu_cb_idx], menu_index, icon, flags); + if (!ret) + return -1; + return menu_cb_idx++; + } + + int show(PyObject *self) + { + PyObject *attr; + // get title + if ( (attr = PyObject_TryGetAttrString(self, "title")) == NULL ) + return -1; + qstring title = PyString_AsString(attr); + Py_DECREF(attr); + + // get flags + if ( (attr = PyObject_TryGetAttrString(self, "flags")) == NULL ) + return -1; + int flags = PyInt_AsLong(attr); + Py_DECREF(attr); + + // get columns + if ( (attr = PyObject_TryGetAttrString(self, "cols")) == NULL ) + return -1; + + // get col count + int ncols = PyList_Size(attr); + + // get cols caption and widthes + intvec_t widths; + cols.qclear(); + for (int i=0;iself = self; + + // Create chooser + int r = this->choose2(flags, ncols, &widths[0], title.c_str(), deflt, popup_names, icon, pts[0], pts[1], pts[2], pts[3]); + + // Clear temporary popup_names + if (popup_names != NULL) + { + for (int i=0;iactivate(); + return 1; + } + c2 = new py_choose2_t(); + choose2_add_instance(self, c2); + return c2->show(self); +} + +//------------------------------------------------------------------------ +void choose2_close(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if (c2 != NULL) + c2->close(); +} + +//------------------------------------------------------------------------ +void choose2_refresh(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if (c2 != NULL) + c2->refresh(); +} + +//------------------------------------------------------------------------ +void choose2_activate(PyObject *self) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if (c2 != NULL) + c2->activate(); +} + +//------------------------------------------------------------------------ +int choose2_add_command(PyObject *self, const char *caption, int flags=0, int menu_index=-1, int icon=-1) +{ + py_choose2_t *c2 = choose2_find_instance(self); + if (c2 != NULL) + return c2->add_command(caption, flags, menu_index, icon); + else + return -2; +} + +//------------------------------------------------------------------------ +PyObject *choose2_find(const char *title) +{ + py_choose2_t *c2 = py_choose2_t::find_chooser(title); + if (c2 == NULL) + return NULL; + return c2->get_self(); +} + +//------------------------------------------------------------------------- +// End of Chooser2 wrapper class +//------------------------------------------------------------------------- + uint32 idaapi choose_sizer(void *self) { PyObject *pyres; @@ -232,6 +860,7 @@ uint32 choose_choose(void *self, %} %pythoncode %{ + class Choose: """ Choose - class for choose() with callbacks @@ -294,4 +923,115 @@ class Choose: choose - Display the choose dialogue """ return _idaapi.choose_choose(self, self.flags, self.x0, self.y0, self.x1, self.y1, self.width) + + +class Choose2: + """Choose2 wrapper class""" + + # refer to kernwin.hpp for more information on how to use these constants + CH_MODAL = 0x01 + CH_MULTI = 0x02 + CH_MULTI_EDIT = 0x04 + CH_NOBTNS = 0x08 + CH_ATTRS = 0x10 + CH_BUILTIN_MASK = 0xF80000 + + # column flags (are specified in the widths array) + CHCOL_PLAIN = 0x00000000 + CHCOL_PATH = 0x00010000 + CHCOL_HEX = 0x00020000 + CHCOL_DEC = 0x00030000 + CHCOL_FORMAT = 0x00070000 + + def __init__(self, title, cols, flags=0, popup_names=None, icon=-1, x1=-1, y1=-1, x2=-1, y2=-1): + self.title = title + self.flags = flags + # a list of colums; each list item is a list of two items + # example: [ ["Address", 10 | Choose2.CHCOL_HEX], ["Name", 30 | CHCOL_PLAIN] ] + self.cols = cols + self.deflt = -1 + # list of new captions to replace this list ["Insert", "Delete", "Edit", "Refresh"] + self.popup_names = popup_names + self.icon = icon + self.x1 = x1 + self.y1 = y1 + self.x2 = x2 + self.y2 = y2 + + def Show(self, modal=False): + """Activates or creates a chooser window""" + if modal: + self.flags |= Choose2.CH_MODAL + else: + self.flags &= ~Choose2.CH_MODAL + return _idaapi.choose2_show(self) + + def Activate(): + """Activates a visible chooser""" + return _idaapi.choose2_activate(self) + + def Refresh(): + """Causes the refresh callback to trigger""" + return _idaapi.choose2_refresh(self) + + def Close(): + """Closes the chooser""" + return _idaapi.choose2_close(self) + + def AddCommand(self, caption, flags = _idaapi.CHOOSER_POPUP_MENU, menu_index=-1,icon = -1): + """Adds a new chooser command + Save the returned value and later use it in the OnCommand handler + + @return: Returns a negative value on failure or the command index + """ + return _idaapi.choose2_add_command(self, caption, flags, menu_index, icon) + + # + # Implement these methods in the subclass: + # + +# def OnClose(self): +# # return nothing +# pass + +# def OnEditLine(self, n): +# # return nothing (mandatory callback) +# pass + +# def OnInsertLine(self): +# # return nothing +# pass + +# def OnSelectLine(self, n): +# # return nothing +# pass + +# def OnGetLine(self, n): +# # return a list [col1, col2, col3, ...] describing the n-th line +# return ["col1", "col2", ...] + +# def OnGetSize(self): +# # return the size (mandatory callback) +# return len(self.the_list) + +# def OnDeleteLine(self, n): +# # return new line number +# return self.n + +# def OnRefresh(self, n): +# # return new line number +# return self.n + +# def OnCommand(self, n, cmd_id): +# # return int ; check add_chooser_command() +# return 0 + +# def OnGetIcon(self, n): +# # return icon number (or -1) +# return -1 + +# def OnGetLineAttr(self, n): +# # return list [color, flags] or None; check chooser_item_attrs_t +# pass + %}