iup-stack/iup/srclua5/ctrl/iuplua_matrix_aux.c

833 lines
20 KiB
C
Raw Normal View History

2023-02-20 16:44:45 +00:00
/** \file
* \brief matrix binding for Lua 5.
*
* See Copyright Notice in "iup.h"
*/
#include <memory.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "iup.h"
#include "iupcontrols.h"
#include "iupcbs.h"
#include <cd.h>
#include <cdlua.h>
#include "iuplua.h"
#include "il.h"
#include "il_controls.h"
#include "iup_attrib.h"
#include "iup_object.h"
#include "iup_assert.h"
#include "iup_predialogs.h"
#include "iup_str.h"
typedef int(*IFnL)(Ihandle*, lua_State *L);
static int math_sum(lua_State *L)
{
int n = lua_gettop(L); /* number of arguments */
lua_Number dsum = luaL_checknumber(L, 1);
int i;
for (i = 2; i <= n; i++)
{
lua_Number d = luaL_checknumber(L, i);
dsum += d;
}
lua_pushnumber(L, dsum);
return 1;
}
static int math_average(lua_State *L)
{
int n = lua_gettop(L); /* number of arguments */
lua_Number dsum = luaL_checknumber(L, 1);
int i;
for (i = 2; i <= n; i++)
{
lua_Number d = luaL_checknumber(L, i);
dsum += d;
}
dsum /= (double)n;
lua_pushnumber(L, dsum);
return 1;
}
static int luamatrix_pushvalue(lua_State *L, const char* value, int only_number)
{
double num;
if (!value || value[0] == 0)
{
if (only_number)
return 0;
lua_pushnil(L);
return 1;
}
if (sscanf(value, "%lf", &num) == 1)
lua_pushnumber(L, num);
else
{
if (only_number)
return 0;
lua_pushstring(L, value);
}
return 1;
}
static char* get_cell_value_safe(lua_State *L, Ihandle* ih, int lin, int col)
{
char* value;
if (iupAttribGetId2(ih, "_IUPMATRIX_GETCELL", lin, col))
luaL_error(L, "recursion detected for cell(%d,%d)", lin, col);
iupAttribSetStrId2(ih, "_IUPMATRIX_GETCELL", lin, col, "1");
value = IupGetAttributeId2(ih, "CELL", lin, col); /* display value */
iupAttribSetId2(ih, "_IUPMATRIX_GETCELL", lin, col, NULL);
return value;
}
static int formula_range(lua_State *L)
{
Ihandle *ih;
int lin1 = luaL_checkinteger(L, 1);
int col1 = luaL_checkinteger(L, 2);
int lin2 = luaL_checkinteger(L, 3);
int col2 = luaL_checkinteger(L, 4);
int only_number = 0;
int lin, col, count;
if (lua_isboolean(L, 5))
only_number = lua_toboolean(L, 5);
lua_getglobal(L, "matrix");
ih = (Ihandle*)lua_touserdata(L, -1);
count = 0;
for (lin = lin1; lin <= lin2; lin++)
{
for (col = col1; col <= col2; col++)
{
char* value = get_cell_value_safe(L, ih, lin, col);
if (luamatrix_pushvalue(L, value, only_number))
count++;
}
}
return count;
}
static int formula_cell(lua_State *L)
{
Ihandle *ih;
char* value;
int lin = luaL_checkinteger(L, 1);
int col = luaL_checkinteger(L, 2);
lua_getglobal(L, "matrix");
ih = (Ihandle*)lua_touserdata(L, -1);
value = get_cell_value_safe(L, ih, lin, col);
return luamatrix_pushvalue(L, value, 0);
}
static int formula_ifelse(lua_State *L)
{
lua_toboolean(L, 1) ? lua_pushvalue(L, 2) : lua_pushvalue(L, 3);
return 1;
}
static void show_formula_error(lua_State *L, Ihandle* ih, const char* str_message)
{
const char* error = lua_tostring(L, -1);
char msg[1024];
sprintf(msg, "%s\n Lua error: %s", str_message, error);
IupMessageError(IupGetDialog(ih), msg);
}
static void register_math_global(lua_State *L)
{
const char* register_global =
"function openpackage(ns)\n"
" for n, v in pairs(ns) do _G[n] = v end\n"
"end\n"
"openpackage(math)\n";
luaL_dostring(L, register_global);
}
#define iup_isupper(_c) (_c>='A' && _c<='Z')
static int matrix_get_global_name(const char* key, size_t len, int *lin, int *col)
{
int i;
if (len < 2)
return 0;
if (key[0] == 'L' && iup_isdigit(key[1]) && len > 3)
{
i = 2;
while (iup_isdigit(key[i]) && i < len)
i++;
if (key[i] == 'C' && iup_isdigit(key[i+1]))
{
int start_col = i+1;
i += 2;
while (iup_isdigit(key[i]) && i < len)
i++;
if (i == len) /* found L123C123 notation */
{
char str_lin[50];
int str_lin_len = start_col-2;
if (str_lin_len > 50)
return 0;
memcpy(str_lin, key + 1, str_lin_len);
str_lin[str_lin_len] = 0;
iupStrToInt(str_lin, lin);
iupStrToInt(key + start_col, col);
return 1;
}
}
}
return 0;
}
static int excel_get_global_name(const char* key, size_t len, int *lin, int *col)
{
int i;
if (len < 2)
return 0;
if (iup_isupper(key[0]) && len > 1)
{
i = 1;
while (iup_isupper(key[i]) && i < len)
i++;
if (iup_isdigit(key[i]))
{
int start_lin = i;
i++;
while (iup_isdigit(key[i]) && i < len)
i++;
if (i == len) /* found ABC123 (col|lin) notation (same as Excel) */
{
*col = 0;
i = 0;
while (i < start_lin)
{
(*col) = 26 * (*col) + (key[i] - 'A' + 1);
i++;
}
iupStrToInt(key + start_lin, lin);
return 1;
}
}
}
return 0;
}
static int matrix_global_index(lua_State *L)
{
size_t len;
const char* key = lua_tolstring(L, 2, &len);
int lin, col;
if (matrix_get_global_name(key, len, &lin, &col))
{
Ihandle *ih;
char* value;
lua_getglobal(L, "matrix");
ih = (Ihandle*)lua_touserdata(L, -1);
value = get_cell_value_safe(L, ih, lin, col);
luamatrix_pushvalue(L, value, 0);
}
else
{
/* get raw method */
lua_getmetatable(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
}
return 1;
}
static int excel_global_index(lua_State *L)
{
size_t len;
const char* key = lua_tolstring(L, 2, &len);
int lin, col;
if (excel_get_global_name(key, len, &lin, &col))
{
Ihandle *ih;
char* value;
lua_getglobal(L, "matrix");
ih = (Ihandle*)lua_touserdata(L, -1);
value = get_cell_value_safe(L, ih, lin, col);
luamatrix_pushvalue(L, value, 0);
}
else
{
/* get raw method */
lua_getmetatable(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
}
return 1;
}
static lua_State* iMatrixInitFormula(Ihandle* ih, const char* init)
{
lua_State *L;
IFnL init_cb;
char* cell_names;
iupASSERT(iupObjectCheck(ih));
if (!iupObjectCheck(ih))
return NULL;
/* must be an IupMatrix */
if (ih->iclass->nativetype != IUP_TYPECANVAS ||
!IupClassMatch(ih, "matrix"))
return NULL;
L = (lua_State*)iupAttribGet(ih, "_IUPMATRIX_LUASTATE"); /* Used only by SetDynamic */
if (L)
lua_close(L);
L = luaL_newstate();
luaL_openlibs(L);
register_math_global(L);
cell_names = iupAttribGet(ih, "CELLNAMES");
if (iupStrEqualNoCase(cell_names, "EXCEL"))
{
lua_register(L, "global_index", excel_global_index);
luaL_dostring(L, "setmetatable(_G, {__index=global_index})");
}
else if (iupStrEqualNoCase(cell_names, "MATRIX"))
{
lua_register(L, "global_index", matrix_global_index);
luaL_dostring(L, "setmetatable(_G, {__index=global_index})");
}
lua_register(L, "sum", math_sum);
lua_register(L, "average", math_average);
lua_register(L, "range", formula_range);
lua_register(L, "cell", formula_cell);
lua_register(L, "ifelse", formula_ifelse);
if (init)
luaL_dostring(L, init);
init_cb = (IFnL)IupGetCallback(ih, "FORMULAINIT_CB");
if (init_cb)
init_cb(ih, L);
lua_pushlightuserdata(L, ih);
lua_setglobal(L, "matrix");
return L;
}
static int iMatrixLoadFormula(lua_State *L, const char* formula)
{
char formula_func[1024];
sprintf(formula_func, "function matrix_formula(lin, col)\n"
" return %s\n"
"end\n", formula);
if (luaL_dostring(L, formula_func) != 0)
return 0;
return 1;
}
static int iMatrixExecFormula(lua_State *L, int lin, int col)
{
lua_getglobal(L, "matrix_formula");
lua_pushinteger(L, lin);
lua_pushinteger(L, col);
if (lua_pcall(L, 2, 1, 0) != 0)
return 0;
return 1;
}
void IupMatrixSetFormula(Ihandle* ih, int col, const char* formula, const char* init)
{
int lin, numlin;
lua_State *L = iMatrixInitFormula(ih, init);
if (!L)
return;
if (!iMatrixLoadFormula(L, formula))
{
const char* str_message = IupGetLanguageString("IUP_ERRORINVALIDFORMULA");
show_formula_error(L, ih, str_message);
lua_close(L);
return;
}
numlin = IupGetInt(ih, "NUMLIN");
for (lin = 1; lin <= numlin; lin++)
{
if (!iMatrixExecFormula(L, lin, col))
{
const char* str_message = IupGetLanguageString("IUP_ERRORINVALIDFORMULA");
show_formula_error(L, ih, str_message);
lua_close(L);
return;
}
/* get the result */
if (lua_isnumber(L, -1))
{
double num = lua_tonumber(L, -1);
IupSetDoubleId2(ih, "", lin, col, num);
}
else if (lua_isnil(L, -1))
IupSetAttributeId2(ih, "", lin, col, NULL);
else if (lua_isboolean(L, -1))
{
int num = lua_toboolean(L, -1);
IupSetIntId2(ih, "", lin, col, num);
}
else
{
const char* value = lua_tostring(L, -1);
IupSetStrAttributeId2(ih, "", lin, col, value);
}
lua_pop(L, 1); /* remove the result from the stack */
}
lua_close(L);
}
static char* iMatrixDynamicTranslateValue_CB(Ihandle* ih, int lin, int col, char* value)
{
if (value && value[0] == '=' && !iupAttribGet(ih, "EDITVALUE"))
{
lua_State* L = (lua_State*)iupAttribGet(ih, "_IUPMATRIX_LUASTATE");
if (!iMatrixLoadFormula(L, value + 1))
{
const char* str_message = IupGetLanguageString("IUP_ERRORINVALIDFORMULA");
show_formula_error(L, ih, str_message);
return (char*)str_message;
}
if (!iMatrixExecFormula(L, lin, col))
{
const char* str_message = IupGetLanguageString("IUP_ERRORINVALIDFORMULA");
show_formula_error(L, ih, str_message);
return (char*)str_message;
}
/* get the result */
if (lua_isnumber(L, -1))
{
double num = lua_tonumber(L, -1);
iupAttribSetDouble(ih, "FORMULA_RETURN", num);
}
else
{
const char* str = lua_tostring(L, -1);
iupAttribSetStr(ih, "FORMULA_RETURN", str);
}
lua_pop(L, 1); /* remove the result from the stack */
return iupAttribGet(ih, "FORMULA_RETURN");
}
return value;
}
/* Samples:
A = 1
Z = 26
BC = 55
AD = 30
*/
static void iMatrixDynamicColName(char* col_name, int col)
{
int n = 1;
int cc = col;
while (cc > 26)
{
cc /= 26;
n++;
}
col_name[n] = 0;
while (n)
{
int rem = col % 26;
int c = rem + 'A' - 1;
col_name[n-1] = (char)c;
col /= 26;
n--;
}
}
static void iMatrixDynamicInsertCellReference(Ihandle* ih, int lin, int col)
{
char* old_value = iupAttribGet(ih, "_IUPMATRIX_EDITVALUE");
if (old_value)
{
char* cell_names;
char* old_caret = iupAttribGet(ih, "_IUPMATRIX_EDITCARET");
IupSetStrAttribute(ih, "VALUE", old_value);
IupSetStrAttribute(ih, "CARET", old_caret);
cell_names = iupAttribGet(ih, "CELLNAMES");
if (iupStrEqualNoCase(cell_names, "EXCEL"))
{
char col_name[30];
iMatrixDynamicColName(col_name, col);
IupSetfAttribute(ih, "INSERT", "%s%d", col_name, lin);
}
else if (iupStrEqualNoCase(cell_names, "MATRIX"))
IupSetfAttribute(ih, "INSERT", "L%dC%d", lin, col);
else
IupSetfAttribute(ih, "INSERT", "cell(%d,%d)", lin, col);
}
}
static void iMatrixDynamicInsertRange(Ihandle* ih, int start_lin, int start_col, int lin, int col)
{
char* old_value = iupAttribGet(ih, "_IUPMATRIX_EDITVALUE");
if (old_value)
{
char* old_caret = iupAttribGet(ih, "_IUPMATRIX_EDITCARET");
IupSetStrAttribute(ih, "VALUE", old_value);
IupSetStrAttribute(ih, "CARET", old_caret);
IupSetfAttribute(ih, "INSERT", "range(%d,%d,%d,%d)", start_lin, start_col, lin, col);
}
}
static int iMatrixDynamicEditKillFocus_CB(Ihandle* ih)
{
if (IupGetInt(ih, "EDITTEXT"))
{
char* value = IupGetAttribute(ih, "VALUE"); /* edited value */
char* caret = IupGetAttribute(ih, "CARET");
iupAttribSetStr(ih, "_IUPMATRIX_EDITVALUE", value);
iupAttribSetStr(ih, "_IUPMATRIX_EDITCARET", caret);
}
return IUP_DEFAULT;
}
static int iMatrixDynamicEditClick_CB(Ihandle* ih, int lin, int col, char* status)
{
if (iup_isbutton1(status) && IupGetInt(ih, "EDITTEXT"))
{
char* value = IupGetAttribute(ih, "VALUE"); /* edited value */
if (value && value[0] == '=')
{
iMatrixDynamicInsertCellReference(ih, lin, col);
iupAttribSetStrf(ih, "_IUPMATRIX_EDITINSERT", "%d:%d", lin, col);
}
}
return IUP_DEFAULT;
}
static int iMatrixDynamicEditRelease_CB(Ihandle* ih, int lin, int col, char* status)
{
(void)lin;
(void)col;
(void)status;
if (iupAttribGet(ih, "_IUPMATRIX_EDITINSERT"))
iupAttribSet(ih, "_IUPMATRIX_EDITINSERT", NULL);
return IUP_DEFAULT;
}
static int iMatrixDynamicEditMouseMove_CB(Ihandle* ih, int lin, int col)
{
char* value = iupAttribGet(ih, "_IUPMATRIX_EDITINSERT");
if (value)
{
int start_lin, start_col;
iupStrToIntInt(value, &start_lin, &start_col, ':');
iMatrixDynamicInsertRange(ih, start_lin, start_col, lin, col);
}
return IUP_DEFAULT;
}
static int iMatrixDynamicLDestroy_CB(Ihandle* ih)
{
Icallback cb = IupGetCallback(ih, "OLD_LDESTROY_CB");
lua_State* L = (lua_State*)iupAttribGet(ih, "_IUPMATRIX_LUASTATE");
iupAttribSet(ih, "_IUPMATRIX_LUASTATE", NULL);
IupSetCallback(ih, "TRANSLATEVALUE_CB", NULL);
lua_close(L);
if (cb)
cb(ih);
return IUP_DEFAULT;
}
void IupMatrixSetDynamic(Ihandle* ih, const char* init)
{
Icallback cb;
lua_State *L = iMatrixInitFormula(ih, init);
if (!L)
return;
iupAttribSet(ih, "_IUPMATRIX_LUASTATE", (char*)L);
cb = IupGetCallback(ih, "LDESTROY_CB");
if (cb)
IupSetCallback(ih, "OLD_LDESTROY_CB", cb);
IupSetCallback(ih, "LDESTROY_CB", iMatrixDynamicLDestroy_CB);
IupSetCallback(ih, "TRANSLATEVALUE_CB", (Icallback)iMatrixDynamicTranslateValue_CB);
IupSetCallback(ih, "EDITCLICK_CB", (Icallback)iMatrixDynamicEditClick_CB);
IupSetCallback(ih, "EDITRELEASE_CB", (Icallback)iMatrixDynamicEditRelease_CB);
IupSetCallback(ih, "EDITMOUSEMOVE_CB", (Icallback)iMatrixDynamicEditMouseMove_CB);
IupSetCallback(ih, "EDITKILLFOCUS_CB", (Icallback)iMatrixDynamicEditKillFocus_CB);
}
static int MatrixSetFormula(lua_State *L)
{
Ihandle *ih = iuplua_checkihandle(L, 1);
IupMatrixSetFormula(ih, (int)luaL_checkinteger(L, 2), luaL_checkstring(L, 3), luaL_optstring(L, 4, NULL));
return 0;
}
static int MatrixSetDynamic(lua_State *L)
{
Ihandle *ih = iuplua_checkihandle(L, 1);
IupMatrixSetDynamic(ih, luaL_optstring(L, 2, NULL));
return 0;
}
static int matrix_draw_cb(Ihandle *self, int p0, int p1, int p2, int p3, int p4, int p5, cdCanvas* cnv)
{
lua_State *L = iuplua_call_start(self, "draw_cb");
lua_pushinteger(L, p0);
lua_pushinteger(L, p1);
lua_pushinteger(L, p2);
lua_pushinteger(L, p3);
lua_pushinteger(L, p4);
lua_pushinteger(L, p5);
cdlua_pushcanvas(L, cnv);
return iuplua_call(L, 7);
}
static int matrix_color_cb(Ihandle *self, int p0, int p1, int *p2, int *p3, int *p4, const char* name)
{
int status;
lua_State *L = iuplua_call_start(self, name);
int top = lua_gettop(L) - 3; /* 3 is the number of pushed values in iuplua_call_start */
/* don't have control over the number of returned values because of LUA_MULTRET,
so must restore stack manually */
lua_pushinteger(L, p0);
lua_pushinteger(L, p1);
/* similar to iuplua_call */
status = iuplua_call_raw(L, 2 + 2, LUA_MULTRET); /* 2 args + 2 args(errormsg, ih), variable number of returns */
if (status != LUA_OK)
return IUP_DEFAULT;
else
{
/* can do:
return r, g, b, iup.DEFAULT
return r, g, b, iup.IGNORE
return iup.IGNORE
return -- same as iup.IGNORE
*/
int tmp = (int)lua_isnil(L, -1)? IUP_IGNORE: (int)lua_tointeger(L, -1);
if (tmp == IUP_IGNORE)
{
lua_settop(L, top); /* remove the results */
return IUP_IGNORE;
}
*p2 = (int)lua_tointeger(L, -4); /* R */
*p3 = (int)lua_tointeger(L, -3); /* G */
*p4 = (int)lua_tointeger(L, -2); /* B */
lua_settop(L, top); /* remove the results */
return IUP_DEFAULT;
}
}
static int matrix_bgcolor_cb(Ihandle *self, int p0, int p1, int *p2, int *p3, int *p4)
{
return matrix_color_cb(self, p0, p1, p2, p3, p4, "bgcolor_cb");
}
static int matrix_fgcolor_cb(Ihandle *self, int p0, int p1, int *p2, int *p3, int *p4)
{
return matrix_color_cb(self, p0, p1, p2, p3, p4, "fgcolor_cb");
}
static int MatGetAttribute(lua_State *L)
{
Ihandle *ih = iuplua_checkihandle(L,1);
const char *name = luaL_checkstring(L,2);
int lin = (int)luaL_checkinteger(L,3);
int col = (int)luaL_checkinteger(L, 4);
const char *value = IupGetAttributeId2(ih, name, lin, col);
if (!value || iupATTRIB_ISINTERNAL(name))
lua_pushnil(L);
else
{
if (iupAttribIsNotString(ih, name))
{
if (iupAttribIsIhandle(ih, name))
iuplua_pushihandle(L, (Ihandle*)value);
else
lua_pushlightuserdata(L, (void*)value);
}
else
lua_pushstring(L,value);
}
return 1;
}
static int MatStoreAttribute(lua_State *L)
{
Ihandle *ih = iuplua_checkihandle(L,1);
const char *a = luaL_checkstring(L,2);
int lin = luaL_checkinteger(L,3);
int col = luaL_checkinteger(L,4);
if (lua_isnil(L,5))
IupSetAttributeId2(ih,a,lin, col,NULL);
else
{
const char *v;
if(lua_isuserdata(L,5))
{
v = lua_touserdata(L,5);
IupSetAttributeId2(ih,a,lin, col,v);
}
else
{
v = luaL_checkstring(L,5);
IupStoreAttributeId2(ih,a,lin, col,v);
}
}
return 0;
}
static IFnii iMatrixOriginalKeyPress_CB = NULL;
static IFniiiis iMatrixOriginalButton_CB = NULL;
static IFniis iMatrixOriginalMotion_CB = NULL;
static int MatButtonCB(lua_State *L)
{
Ihandle *ih = iuplua_checkihandle(L, 1);
int p0 = luaL_checkinteger(L, 2);
int p1 = luaL_checkinteger(L, 3);
int p2 = luaL_checkinteger(L, 4);
int p3 = luaL_checkinteger(L, 5);
const char* p4 = luaL_checkstring(L, 6);
int ret = iMatrixOriginalButton_CB(ih, p0, p1, p2, p3, (char*)p4);
lua_pushinteger(L, ret);
return 1;
}
static int MatMotionCB(lua_State *L)
{
Ihandle *ih = iuplua_checkihandle(L, 1);
int p0 = luaL_checkinteger(L, 2);
int p1 = luaL_checkinteger(L, 3);
const char* p2 = luaL_checkstring(L, 4);
int ret = iMatrixOriginalMotion_CB(ih, p0, p1, (char*)p2);
lua_pushinteger(L, ret);
return 1;
}
static int MatKeyPressCB(lua_State *L)
{
Ihandle *ih = iuplua_checkihandle(L, 1);
int p0 = luaL_checkinteger(L, 2);
int p1 = luaL_checkinteger(L, 3);
int ret = iMatrixOriginalKeyPress_CB(ih, p0, p1);
lua_pushinteger(L, ret);
return 1;
}
void iuplua_matrixfuncs_open(lua_State *L)
{
iuplua_register(L, MatrixSetFormula, "MatrixSetFormula");
iuplua_register(L, MatrixSetDynamic, "MatrixSetDynamic");
/* DEPRECATED backward compatibility */
iuplua_register(L, MatGetAttribute, "MatGetAttribute");
iuplua_register(L, MatStoreAttribute, "MatStoreAttribute");
iuplua_register(L, MatStoreAttribute, "MatSetAttribute");
/* Original Callback Export */
iuplua_register(L, MatButtonCB, "MatButtonCb");
iuplua_register(L, MatMotionCB, "MatMotionCb");
iuplua_register(L, MatKeyPressCB, "MatKeyPressCb");
{
Ihandle* mat = IupMatrix(NULL);
iMatrixOriginalKeyPress_CB = (IFnii)IupGetCallback(mat, "KEYPRESS_CB");
iMatrixOriginalButton_CB = (IFniiiis)IupGetCallback(mat, "BUTTON_CB");
iMatrixOriginalMotion_CB = (IFniis)IupGetCallback(mat, "MOTION_CB");
IupDestroy(mat);
}
iuplua_register_cb(L, "BGCOLOR_CB", (lua_CFunction)matrix_bgcolor_cb, NULL);
iuplua_register_cb(L, "FGCOLOR_CB", (lua_CFunction)matrix_fgcolor_cb, NULL);
iuplua_register_cb(L, "DRAW_CB", (lua_CFunction)matrix_draw_cb, NULL);
iuplua_register_cb(L, "LISTDRAW_CB", (lua_CFunction)matrix_draw_cb, NULL);
}