#ifndef __PY_CHOOSE2__
#define __PY_CHOOSE2__
//
//------------------------------------------------------------------------
// Some defines
#define POPUP_NAMES_COUNT 4
#define MAX_CHOOSER_MENU_COMMANDS 20
#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)); \
}
//------------------------------------------------------------------------
// 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);
return it == choosers.end() ? NULL : 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:
enum
{
CHOOSE2_HAVE_DEL = 0x0001,
CHOOSE2_HAVE_INS = 0x0002,
CHOOSE2_HAVE_UPDATE = 0x0004,
CHOOSE2_HAVE_EDIT = 0x0008,
CHOOSE2_HAVE_ENTER = 0x0010,
CHOOSE2_HAVE_GETICON = 0x0020,
CHOOSE2_HAVE_GETATTR = 0x0040,
CHOOSE2_HAVE_COMMAND = 0x0080,
CHOOSE2_HAVE_ONCLOSE = 0x0100,
CHOOSE2_HAVE_SELECT = 0x0200,
CHOOSE2_HAVE_REFRESHED = 0x0400,
};
// Chooser flags
int flags;
// Callback flags (to tell which callback exists and which not)
// One of CHOOSE2_HAVE_xxxx
unsigned int cb_flags;
chooser_info_t *embedded;
intvec_t embedded_sel;
// Menu callback index (in the menu_cbs array)
int menu_cb_idx;
// Chooser title
qstring title;
// Column widths
intvec_t widths;
// Python object link
PyObject *self;
// Chooser columns
qstrvec_t cols;
const char **popup_names;
bool ui_cb_hooked;
// 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)
MENU_COMMAND_CB(10) MENU_COMMAND_CB(11)
MENU_COMMAND_CB(12) MENU_COMMAND_CB(13)
MENU_COMMAND_CB(14) MENU_COMMAND_CB(15)
MENU_COMMAND_CB(16) MENU_COMMAND_CB(17)
MENU_COMMAND_CB(18) MENU_COMMAND_CB(19)
static chooser_cb_t *menu_cbs[MAX_CHOOSER_MENU_COMMANDS];
//------------------------------------------------------------------------
// Static methods to dispatch to member functions
//------------------------------------------------------------------------
static int idaapi ui_cb(void *obj, int notification_code, va_list va)
{
// UI callback to handle chooser items with attributes
if ( notification_code != ui_get_chooser_item_attrs )
return 0;
// Pass events that belong to our chooser only
void *chooser_obj = va_arg(va, void *);
if ( obj != chooser_obj )
return 0;
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;
}
static void idaapi s_select(void *obj, const intvec_t &sel)
{
thisobj->on_select(sel);
}
static void idaapi s_refreshed(void *obj)
{
thisobj->on_refreshed();
}
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_enter(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();
}
//------------------------------------------------------------------------
// Member functions corresponding to each chooser2() callback
//------------------------------------------------------------------------
void clear_popup_names()
{
if ( popup_names == NULL )
return;
for ( int i=0; i=0; i-- )
line_arr[i][0] = '\0';
// Call Python
PYW_GIL_ENSURE;
PyObject *list = PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1);
PYW_GIL_RELEASE;
if ( list == NULL )
return;
// Go over the List returned by Python and convert to C strings
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()
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL);
PYW_GIL_RELEASE;
if ( pyres == NULL )
return 0;
size_t res = PyInt_AsLong(pyres);
Py_DECREF(pyres);
return res;
}
void on_refreshed()
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL);
PYW_GIL_RELEASE;
Py_XDECREF(pyres);
}
void on_select(const intvec_t &intvec)
{
PYW_GIL_ENSURE;
PyObject *py_list = PyW_IntVecToPyList(intvec);
PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_SELECT, "O", py_list);
PYW_GIL_RELEASE;
Py_XDECREF(pyres);
Py_XDECREF(py_list);
}
void on_close()
{
// Call Python
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL);
PYW_GIL_RELEASE;
Py_XDECREF(pyres);
// Delete this instance if none modal and not embedded
if ( !is_modal() && get_embedded() == NULL )
delete this;
}
int on_delete_line(int lineno)
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(
self,
(char *)S_ON_DELETE_LINE,
"i",
lineno - 1);
PYW_GIL_RELEASE;
if ( pyres == NULL )
return lineno;
size_t res = PyInt_AsLong(pyres);
Py_DECREF(pyres);
return res + 1;
}
int on_refresh(int lineno)
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(
self,
(char *)S_ON_REFRESH,
"i",
lineno - 1);
PYW_GIL_RELEASE;
if ( pyres == NULL )
return lineno;
size_t res = PyInt_AsLong(pyres);
Py_DECREF(pyres);
return res + 1;
}
void on_insert_line()
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL);
PYW_GIL_RELEASE;
Py_XDECREF(pyres);
}
void on_enter(int lineno)
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(
self,
(char *)S_ON_SELECT_LINE,
"i",
lineno - 1);
PYW_GIL_RELEASE;
Py_XDECREF(pyres);
}
void on_edit_line(int lineno)
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(
self,
(char *)S_ON_EDIT_LINE,
"i",
lineno - 1);
PYW_GIL_RELEASE;
Py_XDECREF(pyres);
}
int on_command(int cmd_id, int lineno)
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(
self,
(char *)S_ON_COMMAND,
"ii",
lineno - 1,
cmd_id);
PYW_GIL_RELEASE;
if ( pyres==NULL )
return lineno;
size_t res = PyInt_AsLong(pyres);
Py_XDECREF(pyres);
return res;
}
int on_get_icon(int lineno)
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(
self,
(char *)S_ON_GET_ICON,
"i",
lineno - 1);
PYW_GIL_RELEASE;
size_t res = PyInt_AsLong(pyres);
Py_XDECREF(pyres);
return res;
}
void on_get_line_attr(int lineno, chooser_item_attrs_t *attr)
{
PYW_GIL_ENSURE;
PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1);
PYW_GIL_RELEASE;
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);
}
public:
//------------------------------------------------------------------------
// Public methods
//------------------------------------------------------------------------
py_choose2_t(): flags(0), cb_flags(0),
embedded(NULL), menu_cb_idx(0),
self(NULL), popup_names(NULL), ui_cb_hooked(false)
{
}
~py_choose2_t()
{
// Remove from list
choose2_del_instance(self);
// Uninstall hooks
install_hooks(false);
delete embedded;
Py_XDECREF(self);
clear_popup_names();
}
static py_choose2_t *find_chooser(const char *title)
{
return (py_choose2_t *) get_chooser_obj(title);
}
void close()
{
// Will trigger on_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 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;
// For embedded chooser, the "caption" will be overloaded to encode
// the AskUsingForm's title, caption and embedded chooser id
// Title:EmbeddedChooserID:Caption
char title_buf[MAXSTR];
const char *ptitle;
// Embedded chooser?
if ( get_embedded() != NULL )
{
static const char delimiter[] = ":";
char temp[MAXSTR];
qstrncpy(temp, caption, sizeof(temp));
char *p = strtok(temp, delimiter);
if ( p == NULL )
return -1;
// Copy the title
char title_str[MAXSTR];
qstrncpy(title_str, p, sizeof(title_str));
// Copy the echooser ID
p = strtok(NULL, delimiter);
if ( p == NULL )
return -1;
char id_str[10];
qstrncpy(id_str, p, sizeof(id_str));
// Form the new title of the form: "AskUsingFormTitle:EchooserId"
qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str);
// Adjust the title
ptitle = title_buf;
// Adjust the caption
p = strtok(NULL, delimiter);
caption += (p - temp);
}
else
{
ptitle = title.c_str();
}
if ( !add_chooser_command(
ptitle,
caption,
menu_cbs[menu_cb_idx],
menu_index,
icon,
flags))
{
return -1;
}
return menu_cb_idx++;
}
// Create a chooser.
// If it detects the "embedded" attribute, then it will create a chooser_info_t structure
// Otherwise the chooser window is created and displayed
int create(PyObject *self)
{
PyObject *attr;
// Get flags
attr = PyW_TryGetAttrString(self, S_FLAGS);
if ( attr == NULL )
return -1;
flags = PyInt_Check(attr) != 0 ? PyInt_AsLong(attr) : 0;
Py_DECREF(attr);
// Get the title
if ( !PyW_GetStringAttr(self, S_TITLE, &title) )
return -1;
// Get columns
attr = PyW_TryGetAttrString(self, "cols");
if ( attr == NULL )
return -1;
// Get col count
int ncols = int(PyList_Size(attr));
// Get cols caption and widthes
cols.qclear();
for ( int i=0; iself = self;
// Hook to notification point (to handle chooser item attributes)
install_hooks(true);
// Check if *embedded
attr = PyW_TryGetAttrString(self, S_EMBEDDED);
if ( attr != NULL && PyObject_IsTrue(attr) == 1 )
{
// Create an embedded chooser structure
embedded = new chooser_info_t();
embedded->obj = this;
embedded->cb = sizeof(chooser_info_t);
embedded->title = title.c_str();
embedded->columns = ncols;
embedded->deflt = deflt;
embedded->flags = flags;
embedded->width = pts[0]; // Take x1
embedded->height = pts[1]; // Take y1
embedded->icon = icon;
embedded->popup_names = popup_names;
embedded->widths = widths.begin();
embedded->destroyer = s_destroy;
embedded->getl = s_getl;
embedded->sizer = s_sizer;
embedded->del = (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL;
embedded->edit = (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL;
embedded->enter = (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL;
embedded->get_icon = (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL;
embedded->ins = (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL;
embedded->update = (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL;
embedded->get_attrs = NULL;
// Fill callbacks that are only present in idaq
if ( is_idaq() )
{
embedded->select = (cb_flags & CHOOSE2_HAVE_SELECT) != 0 ? s_select : NULL;
embedded->refresh = (cb_flags & CHOOSE2_HAVE_REFRESHED)!= 0 ? s_refreshed : NULL;
}
else
{
embedded->select = NULL;
embedded->refresh = NULL;
}
}
Py_XDECREF(attr);
// Create the chooser (if not embedded)
int r;
if ( embedded == NULL )
{
r = ::choose2(
flags,
pts[0], pts[1], pts[2], pts[3],
this,
ncols,
&widths[0],
s_sizer,
s_getl,
title.c_str(),
icon,
deflt,
(cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL,
(cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL,
(cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL,
(cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL,
(cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL,
s_destroy,
popup_names,
(cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL);
clear_popup_names();
// Modal chooser return the index of the selected item
if ( is_modal() )
r--;
}
// Embedded chooser?
else
{
// Return success
r = 1;
}
return r;
}
inline PyObject *get_self()
{
return self;
}
void refresh()
{
refresh_chooser(title.c_str());
}
bool is_modal()
{
return (flags & CH_MODAL) != 0;
}
intvec_t *get_sel_vec()
{
return &embedded_sel;
}
chooser_info_t *get_embedded() const
{
return embedded;
}
};
//------------------------------------------------------------------------
// Initialize the callback pointers
#define DECL_MENU_COMMAND_CB(id) s_menu_command_##id
chooser_cb_t *py_choose2_t::menu_cbs[MAX_CHOOSER_MENU_COMMANDS] =
{
DECL_MENU_COMMAND_CB(0), DECL_MENU_COMMAND_CB(1),
DECL_MENU_COMMAND_CB(2), DECL_MENU_COMMAND_CB(3),
DECL_MENU_COMMAND_CB(4), DECL_MENU_COMMAND_CB(5),
DECL_MENU_COMMAND_CB(6), DECL_MENU_COMMAND_CB(7),
DECL_MENU_COMMAND_CB(8), DECL_MENU_COMMAND_CB(9),
DECL_MENU_COMMAND_CB(10), DECL_MENU_COMMAND_CB(11),
DECL_MENU_COMMAND_CB(12), DECL_MENU_COMMAND_CB(13),
DECL_MENU_COMMAND_CB(14), DECL_MENU_COMMAND_CB(15),
DECL_MENU_COMMAND_CB(16), DECL_MENU_COMMAND_CB(17),
DECL_MENU_COMMAND_CB(18), DECL_MENU_COMMAND_CB(19)
};
#undef DECL_MENU_COMMAND_CB
#undef POPUP_NAMES_COUNT
#undef MAX_CHOOSER_MENU_COMMANDS
#undef thisobj
#undef thisdecl
#undef MENU_COMMAND_CB
//------------------------------------------------------------------------
int choose2_create(PyObject *self, bool embedded)
{
py_choose2_t *c2;
c2 = choose2_find_instance(self);
if ( c2 != NULL )
{
if ( !embedded )
c2->activate();
return 1;
}
c2 = new py_choose2_t();
choose2_add_instance(self, c2);
int r = c2->create(self);
// Non embedded chooser? Return immediately
if ( !embedded )
return r;
// Embedded chooser was not created?
if ( c2->get_embedded() == NULL || r != 1 )
{
delete c2;
r = 0;
}
return r;
}
//------------------------------------------------------------------------
void choose2_close(PyObject *self)
{
py_choose2_t *c2 = choose2_find_instance(self);
if ( c2 == NULL )
return;
// Modal or embedded chooser?
if ( c2->get_embedded() != NULL || c2->is_modal() )
{
// Then simply delete the instance
delete c2;
}
else
{
// Close the chooser.
// In turn this will lead to the deletion of the object
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();
}
//------------------------------------------------------------------------
PyObject *choose2_get_embedded_selection(PyObject *self)
{
py_choose2_t *c2 = choose2_find_instance(self);
chooser_info_t *embedded;
if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL )
Py_RETURN_NONE;
// Returned as 1-based
intvec_t &intvec = *c2->get_sel_vec();
// Make 0-based
for ( intvec_t::iterator it=intvec.begin(); it != intvec.end(); ++it)
(*it)--;
return PyW_IntVecToPyList(intvec);
}
//------------------------------------------------------------------------
// Return the C instances as 64bit numbers
PyObject *choose2_get_embedded(PyObject *self)
{
py_choose2_t *c2 = choose2_find_instance(self);
chooser_info_t *embedded;
if ( c2 == NULL || (embedded = c2->get_embedded()) == NULL )
Py_RETURN_NONE;
else
return Py_BuildValue("(KK)",
PY_ULONG_LONG(embedded),
PY_ULONG_LONG(c2->get_sel_vec()));
}
//------------------------------------------------------------------------
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);
return c2 == NULL ? NULL : c2->get_self();
}
//
//---------------------------------------------------------------------------
//
PyObject *choose2_find(const char *title);
int choose2_add_command(PyObject *self, const char *caption, int flags, int menu_index, int icon);
void choose2_refresh(PyObject *self);
void choose2_close(PyObject *self);
int choose2_create(PyObject *self, bool embedded);
void choose2_activate(PyObject *self);
PyObject *choose2_get_embedded(PyObject *self);
PyObject *choose2_get_embedded_selection(PyObject *self);
//
//---------------------------------------------------------------------------
// Testing functions. They belong to PyWraps and won't be copied to IDAPython
//---------------------------------------------------------------------------
static void NT_CDECL choose2_test_embedded(chooser_info_t *embedded)
{
msg("cb=%d -> looks %valid\n",
embedded->cb,
embedded->cb == sizeof(chooser_info_t) ? "" : "in");
}
static size_t choose2_get_test_embedded()
{
return (size_t)choose2_test_embedded;
}
#endif // __PY_CHOOSE2__