mirror of
https://github.com/cemu-project/idapython.git
synced 2024-11-24 01:59:18 +01:00
903 lines
24 KiB
C++
903 lines
24 KiB
C++
#ifndef __PY_CHOOSE2__
|
|
#define __PY_CHOOSE2__
|
|
|
|
//<code(py_kernwin)>
|
|
|
|
//------------------------------------------------------------------------
|
|
// 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<PyObject *, py_choose2_t *> 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)
|
|
{
|
|
// This hook gets called from the kernel. Ensure we hold the GIL.
|
|
PYW_GIL_GET;
|
|
|
|
// 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<POPUP_NAMES_COUNT; i++ )
|
|
qfree((void *)popup_names[i]);
|
|
|
|
delete [] popup_names;
|
|
popup_names = NULL;
|
|
}
|
|
|
|
void install_hooks(bool install)
|
|
{
|
|
if ( install )
|
|
{
|
|
if ( (flags & CH_ATTRS) != 0 )
|
|
{
|
|
if ( !hook_to_notification_point(HT_UI, ui_cb, this) )
|
|
flags &= ~CH_ATTRS;
|
|
else
|
|
ui_cb_hooked = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (flags & CH_ATTRS) != 0 )
|
|
{
|
|
unhook_from_notification_point(HT_UI, ui_cb, this);
|
|
ui_cb_hooked = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void on_get_line(int lineno, char * const *line_arr)
|
|
{
|
|
// Called from s_getl, which itself can be called from the kernel. Ensure GIL
|
|
PYW_GIL_GET;
|
|
|
|
// Get headers?
|
|
if ( lineno == 0 )
|
|
{
|
|
// Copy the pre-parsed columns
|
|
for ( size_t i=0; i < cols.size(); i++ )
|
|
qstrncpy(line_arr[i], cols[i].c_str(), MAXSTR);
|
|
return;
|
|
}
|
|
|
|
// Clear buffer
|
|
int ncols = int(cols.size());
|
|
for ( int i=ncols-1; i>=0; i-- )
|
|
line_arr[i][0] = '\0';
|
|
|
|
// Call Python
|
|
PYW_GIL_CHECK_LOCKED_SCOPE();
|
|
pycall_res_t list(PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1));
|
|
if ( list.result == NULL )
|
|
return;
|
|
|
|
// Go over the List returned by Python and convert to C strings
|
|
for ( int i=ncols-1; i>=0; i-- )
|
|
{
|
|
borref_t item(PyList_GetItem(list.result.o, Py_ssize_t(i)));
|
|
if ( item == NULL )
|
|
continue;
|
|
|
|
const char *str = PyString_AsString(item.o);
|
|
if ( str != NULL )
|
|
qstrncpy(line_arr[i], str, MAXSTR);
|
|
}
|
|
}
|
|
|
|
size_t on_get_size()
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL));
|
|
if ( pyres.result == NULL )
|
|
return 0;
|
|
|
|
return PyInt_AsLong(pyres.result.o);
|
|
}
|
|
|
|
void on_refreshed()
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_REFRESHED, NULL));
|
|
}
|
|
|
|
void on_select(const intvec_t &intvec)
|
|
{
|
|
PYW_GIL_GET;
|
|
ref_t py_list(PyW_IntVecToPyList(intvec));
|
|
pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_SELECTION_CHANGE, "O", py_list.o));
|
|
}
|
|
|
|
void on_close()
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_CLOSE, NULL));
|
|
|
|
// 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_GET;
|
|
pycall_res_t pyres(
|
|
PyObject_CallMethod(
|
|
self,
|
|
(char *)S_ON_DELETE_LINE,
|
|
"i",
|
|
IS_CHOOSER_EVENT(lineno) ? lineno : lineno-1));
|
|
return pyres.result == NULL ? 1 : PyInt_AsLong(pyres.result.o);
|
|
}
|
|
|
|
int on_refresh(int lineno)
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(
|
|
PyObject_CallMethod(
|
|
self,
|
|
(char *)S_ON_REFRESH,
|
|
"i",
|
|
lineno - 1));
|
|
return pyres.result == NULL ? lineno : PyInt_AsLong(pyres.result.o) + 1;
|
|
}
|
|
|
|
void on_insert_line()
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_INSERT_LINE, NULL));
|
|
}
|
|
|
|
void on_enter(int lineno)
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(
|
|
PyObject_CallMethod(
|
|
self,
|
|
(char *)S_ON_SELECT_LINE,
|
|
"i",
|
|
lineno - 1));
|
|
}
|
|
|
|
void on_edit_line(int lineno)
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(
|
|
PyObject_CallMethod(
|
|
self,
|
|
(char *)S_ON_EDIT_LINE,
|
|
"i",
|
|
lineno - 1));
|
|
}
|
|
|
|
int on_command(int cmd_id, int lineno)
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(
|
|
PyObject_CallMethod(
|
|
self,
|
|
(char *)S_ON_COMMAND,
|
|
"ii",
|
|
lineno - 1,
|
|
cmd_id));
|
|
return pyres.result == NULL ? lineno : PyInt_AsLong(pyres.result.o);
|
|
}
|
|
|
|
int on_get_icon(int lineno)
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(
|
|
PyObject_CallMethod(
|
|
self,
|
|
(char *)S_ON_GET_ICON,
|
|
"i",
|
|
lineno - 1));
|
|
return PyInt_AsLong(pyres.result.o);
|
|
}
|
|
|
|
void on_get_line_attr(int lineno, chooser_item_attrs_t *attr)
|
|
{
|
|
PYW_GIL_GET;
|
|
pycall_res_t pyres(PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1));
|
|
if ( pyres.result != NULL )
|
|
{
|
|
if ( PyList_Check(pyres.result.o) )
|
|
{
|
|
PyObject *item;
|
|
if ( (item = PyList_GetItem(pyres.result.o, 0)) != NULL )
|
|
attr->color = PyInt_AsLong(item);
|
|
if ( (item = PyList_GetItem(pyres.result.o, 1)) != NULL )
|
|
attr->flags = PyInt_AsLong(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool split_chooser_caption(qstring *out_title, qstring *out_caption, const char *caption) const
|
|
{
|
|
if ( get_embedded() != NULL )
|
|
{
|
|
// 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;
|
|
|
|
static const char delimiter[] = ":";
|
|
char temp[MAXSTR];
|
|
qstrncpy(temp, caption, sizeof(temp));
|
|
|
|
char *ctx;
|
|
char *p = qstrtok(temp, delimiter, &ctx);
|
|
if ( p == NULL )
|
|
return false;
|
|
|
|
// Copy the title
|
|
char title_str[MAXSTR];
|
|
qstrncpy(title_str, p, sizeof(title_str));
|
|
|
|
// Copy the echooser ID
|
|
p = qstrtok(NULL, delimiter, &ctx);
|
|
if ( p == NULL )
|
|
return false;
|
|
|
|
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
|
|
*out_title = title_buf;
|
|
|
|
// Adjust the caption
|
|
p = qstrtok(NULL, delimiter, &ctx);
|
|
*out_caption = caption + (p - temp);
|
|
}
|
|
else
|
|
{
|
|
*out_title = title;
|
|
*out_caption = caption;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// This must be called at the end of create(), when many dependencies
|
|
// have been computed (title, widths, popup_names, [cb_]flags, ...)
|
|
void fill_chooser_info(
|
|
chooser_info_t *out,
|
|
int deflt,
|
|
int desired_width,
|
|
int desired_height,
|
|
int icon)
|
|
{
|
|
memset(out, 0, sizeof(*out));
|
|
out->obj = this;
|
|
out->cb = sizeof(*out);
|
|
out->title = title.c_str();
|
|
out->columns = widths.size();
|
|
out->deflt = deflt;
|
|
out->flags = flags;
|
|
out->width = desired_width;
|
|
out->height = desired_height;
|
|
out->icon = icon;
|
|
out->popup_names = popup_names;
|
|
out->widths = widths.begin();
|
|
out->destroyer = s_destroy;
|
|
out->getl = s_getl;
|
|
out->sizer = s_sizer;
|
|
out->del = (cb_flags & CHOOSE2_HAVE_DEL) != 0 ? s_del : NULL;
|
|
out->edit = (cb_flags & CHOOSE2_HAVE_EDIT) != 0 ? s_edit : NULL;
|
|
out->enter = (cb_flags & CHOOSE2_HAVE_ENTER) != 0 ? s_enter : NULL;
|
|
out->get_icon = (cb_flags & CHOOSE2_HAVE_GETICON) != 0 ? s_get_icon : NULL;
|
|
out->ins = (cb_flags & CHOOSE2_HAVE_INS) != 0 ? s_ins : NULL;
|
|
out->update = (cb_flags & CHOOSE2_HAVE_UPDATE) != 0 ? s_update : NULL;
|
|
out->get_attrs = NULL;
|
|
out->initializer = NULL;
|
|
// Fill callbacks that are only present in idaq
|
|
if ( is_idaq() )
|
|
{
|
|
out->select = (cb_flags & CHOOSE2_HAVE_SELECT) != 0 ? s_select : NULL;
|
|
out->refresh = (cb_flags & CHOOSE2_HAVE_REFRESHED)!= 0 ? s_refreshed : NULL;
|
|
}
|
|
else
|
|
{
|
|
out->select = NULL;
|
|
out->refresh = NULL;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
qstring title, caption;
|
|
if ( !split_chooser_caption(&title, &caption, _caption)
|
|
|| !add_chooser_command(
|
|
title.c_str(),
|
|
caption.c_str(),
|
|
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)
|
|
{
|
|
PYW_GIL_CHECK_LOCKED_SCOPE();
|
|
|
|
// Get flags
|
|
ref_t flags_attr(PyW_TryGetAttrString(self, S_FLAGS));
|
|
if ( flags_attr == NULL )
|
|
return -1;
|
|
flags = PyInt_Check(flags_attr.o) != 0 ? PyInt_AsLong(flags_attr.o) : 0;
|
|
|
|
// Get the title
|
|
if ( !PyW_GetStringAttr(self, S_TITLE, &title) )
|
|
return -1;
|
|
|
|
// Get columns
|
|
ref_t cols_attr(PyW_TryGetAttrString(self, "cols"));
|
|
if ( cols_attr == NULL )
|
|
return -1;
|
|
|
|
// Get col count
|
|
int ncols = int(PyList_Size(cols_attr.o));
|
|
|
|
// Get cols caption and widthes
|
|
cols.qclear();
|
|
for ( int i=0; i<ncols; i++ )
|
|
{
|
|
// get list item: [name, width]
|
|
borref_t list(PyList_GetItem(cols_attr.o, i));
|
|
borref_t v(PyList_GetItem(list.o, 0));
|
|
|
|
// Extract string
|
|
const char *str = v == NULL ? "" : PyString_AsString(v.o);
|
|
cols.push_back(str);
|
|
|
|
// Extract width
|
|
int width;
|
|
borref_t v2(PyList_GetItem(list.o, 1));
|
|
// No width? Guess width from column title
|
|
if ( v2 == NULL )
|
|
width = strlen(str);
|
|
else
|
|
width = PyInt_AsLong(v2.o);
|
|
widths.push_back(width);
|
|
}
|
|
|
|
// Get *deflt
|
|
int deflt = -1;
|
|
ref_t deflt_attr(PyW_TryGetAttrString(self, "deflt"));
|
|
if ( deflt_attr != NULL )
|
|
deflt = PyInt_AsLong(deflt_attr.o);
|
|
|
|
// Get *icon
|
|
int icon = -1;
|
|
ref_t icon_attr(PyW_TryGetAttrString(self, "icon"));
|
|
if ( icon_attr != NULL )
|
|
icon = PyInt_AsLong(icon_attr.o);
|
|
|
|
// Get *x1,y1,x2,y2
|
|
int pts[4];
|
|
static const char *pt_attrs[qnumber(pts)] = {"x1", "y1", "x2", "y2"};
|
|
for ( size_t i=0; i < qnumber(pts); i++ )
|
|
{
|
|
ref_t pt_attr(PyW_TryGetAttrString(self, pt_attrs[i]));
|
|
if ( pt_attr == NULL )
|
|
pts[i] = -1;
|
|
else
|
|
pts[i] = PyInt_AsLong(pt_attr.o);
|
|
}
|
|
|
|
// Check what callbacks we have
|
|
static const struct
|
|
{
|
|
const char *name;
|
|
unsigned int have; // 0 = mandatory callback
|
|
} callbacks[] =
|
|
{
|
|
{S_ON_GET_SIZE, 0},
|
|
{S_ON_GET_LINE, 0},
|
|
{S_ON_CLOSE, 0},
|
|
{S_ON_EDIT_LINE, CHOOSE2_HAVE_EDIT},
|
|
{S_ON_INSERT_LINE, CHOOSE2_HAVE_INS},
|
|
{S_ON_DELETE_LINE, CHOOSE2_HAVE_DEL},
|
|
{S_ON_REFRESH, CHOOSE2_HAVE_UPDATE}, // update()
|
|
{S_ON_SELECT_LINE, CHOOSE2_HAVE_ENTER}, // enter()
|
|
{S_ON_COMMAND, CHOOSE2_HAVE_COMMAND},
|
|
{S_ON_GET_LINE_ATTR, CHOOSE2_HAVE_GETATTR},
|
|
{S_ON_GET_ICON, CHOOSE2_HAVE_GETICON},
|
|
{S_ON_SELECTION_CHANGE, CHOOSE2_HAVE_SELECT},
|
|
{S_ON_REFRESHED, CHOOSE2_HAVE_REFRESHED},
|
|
};
|
|
cb_flags = 0;
|
|
for ( int i=0; i<qnumber(callbacks); i++ )
|
|
{
|
|
ref_t cb_attr(PyW_TryGetAttrString(self, callbacks[i].name));
|
|
bool have_cb = cb_attr != NULL && PyCallable_Check(cb_attr.o) != 0;
|
|
if ( have_cb )
|
|
{
|
|
cb_flags |= callbacks[i].have;
|
|
}
|
|
else
|
|
{
|
|
// Mandatory field?
|
|
if ( callbacks[i].have == 0 )
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Get *popup names
|
|
// An array of 4 strings: ("Insert", "Delete", "Edit", "Refresh"
|
|
ref_t pn_attr(PyW_TryGetAttrString(self, S_POPUP_NAMES));
|
|
if ( (pn_attr != NULL)
|
|
&& PyList_Check(pn_attr.o)
|
|
&& PyList_Size(pn_attr.o) == POPUP_NAMES_COUNT )
|
|
{
|
|
popup_names = new const char *[POPUP_NAMES_COUNT];
|
|
for ( int i=0; i<POPUP_NAMES_COUNT; i++ )
|
|
{
|
|
const char *str = PyString_AsString(PyList_GetItem(pn_attr.o, i));
|
|
popup_names[i] = qstrdup(str);
|
|
}
|
|
}
|
|
|
|
// Adjust flags (if needed)
|
|
if ( (cb_flags & CHOOSE2_HAVE_GETATTR) != 0 )
|
|
flags |= CH_ATTRS;
|
|
|
|
// Increase object reference
|
|
Py_INCREF(self);
|
|
this->self = self;
|
|
|
|
// Hook to notification point (to handle chooser item attributes)
|
|
install_hooks(true);
|
|
|
|
// Check if *embedded
|
|
ref_t emb_attr(PyW_TryGetAttrString(self, S_EMBEDDED));
|
|
int rc;
|
|
if ( emb_attr != NULL && PyObject_IsTrue(emb_attr.o) == 1 )
|
|
{
|
|
// Create an embedded chooser structure
|
|
embedded = new chooser_info_t();
|
|
fill_chooser_info(embedded, deflt, pts[0], pts[1], icon);
|
|
rc = 1; // success
|
|
}
|
|
else
|
|
{
|
|
chooser_info_t ci;
|
|
fill_chooser_info(&ci, deflt, -1, -1, icon);
|
|
rc = choose3(&ci);
|
|
clear_popup_names();
|
|
if ( is_modal() )
|
|
--rc; // modal chooser return the index of the selected item
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
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)
|
|
{
|
|
PYW_GIL_CHECK_LOCKED_SCOPE();
|
|
|
|
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)--;
|
|
|
|
ref_t ret(PyW_IntVecToPyList(intvec));
|
|
ret.incref();
|
|
return ret.o;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Return the C instances as 64bit numbers
|
|
PyObject *choose2_get_embedded(PyObject *self)
|
|
{
|
|
PYW_GIL_CHECK_LOCKED_SCOPE();
|
|
|
|
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);
|
|
return c2 == NULL ? -2 : c2->add_command(caption, flags, menu_index, icon);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
PyObject *choose2_find(const char *title)
|
|
{
|
|
py_choose2_t *c2 = py_choose2_t::find_chooser(title);
|
|
return c2 == NULL ? NULL : c2->get_self();
|
|
}
|
|
//</code(py_kernwin)>
|
|
|
|
//---------------------------------------------------------------------------
|
|
//<inline(py_kernwin)>
|
|
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);
|
|
//</inline(py_kernwin)>
|
|
|
|
//---------------------------------------------------------------------------
|
|
// 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__
|