= rect.left && event.clientX < rect.right && event.clientY >= rect.top && event.clientY < rect.bottom){\n";
val += "\t\t\t\tgameaction(event,'select:'+i+','+(event.clientX-rect.left)+','+(event.clientY-rect.top)+ ','+event.which+','+event.shiftKey+','+event.ctrlKey);\n";
val += "\t\t\t\tbreak;\n";
val += "\t\t\t}\n";
val += "\t\t}\n";
val += "\t}\n";
val += "}\n";
val += "window.calc_gamemove = function(gameid,docnum,event){\n";
val += "}\n";
val += "window.calc_gamemouseup = function(event){\n";
val += "}\n";
val += "window.calc_gamewheel = function(event){\n";
val += "\tif (event.deltaY < 0){\n";
val += "\t\tgameaction(event,'wheel:1,'+event.shiftKey+','+event.ctrlKey);\n";
val += "\t}else{\n";
val += "\t\tgameaction(event,'wheel:-1,'+event.shiftKey+','+event.ctrlKey);\n";
val += "\t}\n";
val += "\tevent.stopPropagation();\n";
val += "}\n";
val += documentd_js_loop_function("tbl","calc",ctx.docnum);
return val;
}
string CALC::define_styles(const DOC_CONTEXT &ctx, const DOC_UI_SPECS_receive &sp)
{
string val;
val += ".calc{\n"
"\ttable-layout: fixed;\n"
"}\n";
#if 0
val += ".calc ttd{\n"
"\tmin-width:50px;\n"
"\tmax-width:50px;\n"
"\toverflow: hidden;\n"
"}\n";
#endif
return val;
}
void CALC::setfocus(VARVAL &script_var)
{
documentd_setfocus(script_var,string_f("text-%s",gameid.c_str()));
}
/*
Update the background color of the column and line, matching the new cursor position
*/
void CALC::update_lines_cols(CALC_PREF &pref, const CELL_COOR &old, const CELL_COOR &new_pos, VARVAL &var)
{
var.val += "calc_loop_board(function(e,calc){\n";
var.val += "\tvar tds=e.getElementsByTagName('td');\n";
if (old.line != new_pos.line){
var.val += string_f("\tvar td=tds[%u];\n",(old.line-pref.offset_line+1)*(TBL_FIXED_COLUMNS+1));
var.val += "\ttd.style.background='#E8E8E8';\n";
var.val += string_f("\tvar td=tds[%u];\n",(new_pos.line-pref.offset_line+1)*(TBL_FIXED_COLUMNS+1));
var.val += "\ttd.style.background='lightblue';\n";
}
if (old.col != new_pos.col){
var.val += string_f("\tvar td=tds[%u];\n",old.col-pref.offset_col+1);
var.val += "\ttd.style.background='#E8E8E8';\n";
var.val += string_f("\tvar td=tds[%u];\n",new_pos.col-pref.offset_col+1);
var.val += "\ttd.style.background='lightblue';\n";
}
var.val += "});\n";
}
/*
Send update about some cells to all users
if optim is true, only cell with a value are sent. This is used for scroll operation where we know
that cells only contains cells in newly created tags (so with an empty value).
*/
void CALC::update_cells(set &cells, VARVAL &var, bool optim)
{
if (cells.size() > 0){
// We always draw a fixed number of lines and columns. There is no ids for td tag.
// So we count them
var.val += "calc_loop_board(function(e,calc){\n";
var.val += "\tvar tds=e.getElementsByTagName('td');\n";
var.val += "\tconsole.log('update_cells calc='+calc);\n";
for (auto &c:cells){
string sessions;
for (auto &p:prefs){
if (p.second.cursor == c){
if (sessions.size() > 0) sessions += ',';
sessions += "'" + p.first + "'";
}
}
auto g = grid.find(c);
if (!optim || g != grid.end() || sessions.size() > 0){
var.val += string_f("\tcalc.selcell(tds,%u,%u,function(td){\n",c.line,c.col);
if (g != grid.end()){
const char *color = g->second.getcolor();
const char *align = g->second.getalign();
unsigned precision = 2;
auto f = col_formats.find(c.col);
if (f != col_formats.end()){
precision = f->second.precision;
if (f->second.align != COL_DEFAULT) align = tbalign[f->second.align];
}
var.val += string_f("\t\ttd.innerHTML='%s';\n",documentd_escape(g->second.gettext(precision)).c_str());
var.val += string_f("\t\ttd.style.color='%s';\n",color);
var.val += string_f("\t\ttd.style.textAlign='%s';\n",align);
}else{
var.val += "\t\ttd.innerHTML='';\n";
}
if (sessions.size() > 0){
var.val += string_f("\t\tset_td_bg(td,[%s]);\n",sessions.c_str());
}else{
var.val += "\t\ttd.style.background='white';\n";
}
var.val += "\t});\n";
}
}
var.val += "});\n";
}
}
/*
Change the name of the current cell in the top status line.
*/
void CALC::update_cellname(CALC_PREF &pref, VARVAL &var)
{
var.val += string_f("var elm = document.getElementById('cell-%s');\n",gameid.c_str());
var.val += "if (elm != null){\n";
var.val += string_f("\telm.innerHTML='%s';\n",pref.cursor.tostring().c_str());
var.val += "}\n";
}
/*
Change the content of the edit field.
*/
void CALC::update_celledit(CALC_PREF &pref, VARVAL &var)
{
var.val += string_f("var elm = document.getElementById('edit-%s');\n",gameid.c_str());
var.val += "if (elm != null){\n";
const char *celltext = "";
auto c = grid.find(pref.cursor);
if (c != grid.end()) celltext = c->second.text.c_str();
var.val += string_f("\telm.value='%s';\n",documentd_escape(celltext).c_str());
var.val += "}\n";
}
/*
Update the content of one cell. If the content is empty, the cell is removed from the grid map.
*/
void CALC::update_onecell (CALC_PREF &pref, PARAM_STRING buf)
{
if (buf.ptr[0] != '\0'){
auto &c = grid[pref.cursor];
c.text = buf.ptr;
c.state = CELL_STATE_UNKNOWN;
c.eval0();
}else{
auto g = grid.find(pref.cursor);
if (g != grid.end()){
grid.erase(g);
}
}
}
void CALC::update_col_width(VARVAL &var, unsigned col)
{
unsigned width = DEFAULT_COL_WIDTH;
auto f = col_formats.find(col);
if (f != col_formats.end()) width = f->second.width;
var.val += "calc_loop_board(function(e,calc){\n";
var.val += string_f("\tcalc.updcolwidth(e,%u,%u);\n",col,width);
var.val += "});\n";
}
void CALC::update_offsets(VARVAL &var, const DOC_CONTEXT &ctx, CALC_PREF &pref)
{
var.val += string_f("calc_%u.updoff(%u,%u);\n",ctx.docnum,pref.offset_line,pref.offset_col);
}
/*
Insert a line or a column in the grid.
This is done by copying the grid into a vector, updating the coordinate
and rebuilding the grid.
We update the formula as well.
*/
void CALC::insert_line_col(unsigned line, unsigned col, int offline, int offcol)
{
struct KEYVAL {
CELL_COOR coor;
CALC_CELL cell;
KEYVAL(const CELL_COOR &_coor, const CALC_CELL &_cell):coor(_coor),cell(_cell){}
};
vector vec;
CELL_COOR ref (line,col);
for (auto &g:grid){
CELL_COOR coor(g.first);
if (coor.line >= line) coor.line++;
if (coor.col >= col) coor.col++;
g.second.applyoffset(ref,offline,offcol);
vec.emplace_back(coor,g.second);
}
grid.clear();
for (auto &v:vec) grid[v.coor] = v.cell;
struct KEYFORMAT{
unsigned col;
CALC_COL_FORMAT format;
KEYFORMAT (unsigned _col, const CALC_COL_FORMAT &_format)
:col(_col),format(_format){}
};
vector vecf;
for (auto &f:col_formats){
unsigned first = f.first;
if (first >= col) first++;
vecf.emplace_back(first,f.second);
}
col_formats.clear();
for (auto &v:vecf) col_formats[v.col] = v.format;
}
void CALC::delete_line_col(unsigned line, unsigned col, int offline, int offcol)
{
struct KEYVAL {
CELL_COOR coor;
CALC_CELL cell;
KEYVAL(const CELL_COOR &_coor, const CALC_CELL &_cell):coor(_coor),cell(_cell){}
};
vector vec;
CELL_COOR ref (line,col);
for (auto &g:grid){
CELL_COOR coor(g.first);
if (coor.line != line && coor.col != col){
if (coor.line > line) coor.line--;
if (coor.col > col) coor.col--;
g.second.applyoffset(ref,offline,offcol);
vec.emplace_back(coor,g.second);
}
}
grid.clear();
for (auto &v:vec) grid[v.coor] = v.cell;
struct KEYFORMAT{
unsigned col;
CALC_COL_FORMAT format;
KEYFORMAT (unsigned _col, const CALC_COL_FORMAT &_format)
:col(_col),format(_format){}
};
vector vecf;
for (auto &f:col_formats){
unsigned first = f.first;
if (first != col){
if (first > col) first--;
vecf.emplace_back(first,f.second);
}
}
col_formats.clear();
for (auto &v:vecf) col_formats[v.col] = v.format;
}
/*
Insert one line in the grid.
Generate the javascript code.
*/
void CALC::insert_line(VARVAL &var, unsigned line)
{
insert_line_col(line,(unsigned)-1,1,0);
// For each listener, we must remove the last and insert a new one, it it applies
// The first contains the heading.
var.val += "calc_loop_board(function(tbl,calc){\n";
var.val += string_f("\tvar offl=%u-calc.offline;\n",line);
var.val += "console.log('offl='+offl);\n";
var.val += string_f("\tif (offl >= 0 && offl < %u){\n",TBL_FIXED_LINES);
var.val += "\t\tvar trs = tbl.getElementsByTagName('tr');\n";
var.val += string_f("\t\ttrs[%u].parentNode.removeChild(trs[%u]);\n",TBL_FIXED_LINES,TBL_FIXED_LINES);
var.val += "\t\tvar newtr = document.createElement('tr');\n";
var.val += "\t\ttrs[0].parentNode.insertBefore(newtr,trs[offl+1]);\n";
var.val += "\t\tvar newtd = document.createElement('td');\n";
var.val += "\t\tnewtd.style.textAlign='center';\n";
var.val += "\t\tnewtr.appendChild(newtd);\n";
var.val += string_f("\t\tfor (var i=0; i<%u; i++){\n",TBL_FIXED_COLUMNS);
var.val += "\t\t\tvar newtd = document.createElement('td');\n";
var.val += "\t\t\tnewtr.appendChild(newtd);\n";
var.val += "\t\t}\n";
// Clear the cursor for all users on this line
map sessions; // Sessions associated with one column on that line
for (auto &p:prefs){
if (p.second.offset_line > line) p.second.offset_line++;
if (p.second.cursor.line > line) p.second.cursor.line++;
if (p.second.cursor.line == line){
auto &s = sessions[p.second.cursor.col];
if (s.size() > 0) s += ',';
s += "'" + p.first + "'";
}
}
for (auto &s:sessions){
var.val += string_f("\t\tvar offc = %u - calc.offcol;\n",s.first);
var.val += string_f("\t\tif(offc >= 0 && offc < %u){\n",TBL_FIXED_COLUMNS);
var.val += "\t\t\tvar tds=trs[offl+2].getElementsByTagName('td');\n";
var.val += "\t\t\ttds[0].style.background='#E8E8E8';\n";
var.val += "\t\t\ttds[offc+1].style.background='white';\n";
var.val += "\t\t\tvar tds=trs[offl+1].getElementsByTagName('td');\n";
var.val += string_f("\t\t\tset_td_bg(tds[0],[%s]);\n",s.second.c_str());
var.val += string_f("\t\t\tset_td_bg(tds[offc+1],[%s]);\n",s.second.c_str());
var.val += "\t\t}\n";
}
var.val += "\t}\n";
var.val += string_f("\tif(calc.offline > %u) calc.offline++;\n",line);
var.val += "\tcalc.updoff(calc.offline,calc.offcol);\n";
var.val += "});\n";
}
void CALC::insert_col(VARVAL &var, unsigned col)
{
insert_line_col((unsigned)-1,col,0,1);
// For each listener, we must remove the last of a line and insert a new one, if it applies
var.val += "calc_loop_board(function(tbl,calc){\n";
var.val += string_f("\tvar offc=%u-calc.offcol;\n",col);
var.val += "console.log('offc='+offc);\n";
var.val += "\tif (offc >= 0 && offc < calc.columns){\n";
// Remove last col, insert one
var.val += "\t\tvar cols = tbl.getElementsByTagName('col');\n";
var.val += "\t\tcols[0].parentNode.removeChild(cols[calc.columns]);\n";
var.val += "\t\tvar newcol = document.createElement('col');\n";
var.val += "\t\tcols[0].parentNode.insertBefore(newcol,cols[offc+calc.heading]);\n";
// Remote last td and insert one for each tr
var.val += "\t\tvar trs = tbl.getElementsByTagName('tr');\n";
var.val += string_f("\t\tfor (var i=0; i<=%u; i++){\n",TBL_FIXED_LINES);
var.val += "\t\t\tvar tds = trs[i].getElementsByTagName('td');\n";
var.val += string_f("\t\t\ttds[0].parentNode.removeChild(tds[%u]);\n",TBL_FIXED_COLUMNS);
var.val += "\t\t\tvar newtd = document.createElement('td');\n";
var.val += "\t\t\tif (i==0){\n";
var.val += "\t\t\t\tnewtd.style.textAlign='center';\n";
var.val += "\t\t\t\tnewtd.style.background='#E8E8E8';\n";
var.val += "\t\t\t}\n";
var.val += "\t\t\ttds[0].parentNode.insertBefore(newtd,tds[offc+1]);\n";
var.val += "\t\t}\n";
var.val += "\t\tcalc.updoff(calc.offline,calc.offcol);\n";
// Clear the cursor for all users on this column
// Clear and set the background on the column header
var.val += "\t\tvar defcol='#E8E8E8';\n";
var.val += string_f("\t\tfor (var i=0; i<=%u; i++){\n",TBL_FIXED_LINES);
var.val += "\t\t\tvar tds=trs[i].getElementsByTagName('td');\n";
var.val += "\t\t\tvar bgcol = window.getComputedStyle(tds[offc+2], null).getPropertyValue('background-color');\n";
var.val += "console.log('bgcol='+bgcol);\n";
var.val += "\t\t\tif(bgcol!=defcol){\n";
var.val += "\t\t\t\ttds[offc+1].style.background=bgcol;\n";
var.val += "\t\t\t\ttds[offc+2].style.background=defcol;\n";
var.val += "\t\t\t}else{\n";
var.val += "\t\t\t\ttds[offc+1].style.background=defcol;\n";
var.val += "\t\t\t}\n";
var.val += "\t\t\tdefcol='white';\n";
var.val += "\t\t}\n";
var.val += "\t}\n";
var.val += "});\n";
update_col_width(var,col);
}
void CALC::delete_line(VARVAL &var, unsigned line)
{
delete_line_col (line,(unsigned)-1,-1,0);
// For each listener, we must remove the line and append a new one at the end, if it applies
var.val += "calc_loop_board(function(tbl,calc){\n";
var.val += string_f("\tvar offl=%u-calc.offline;\n",line);
var.val += "console.log('offl='+offl);\n";
var.val += string_f("\tif (offl >= 0 && offl < %u){\n",TBL_FIXED_LINES);
var.val += "\t\tvar trs = tbl.getElementsByTagName('tr');\n";
var.val += "\t\ttrs[0].parentNode.removeChild(trs[offl+1]);\n";
var.val += "\t\tvar newtr = document.createElement('tr');\n";
var.val += "\t\ttrs[0].parentNode.appendChild(newtr);\n";
var.val += "\t\tvar newtd = document.createElement('td');\n";
var.val += "\t\tnewtd.style.textAlign='center';\n";
var.val += "\t\tnewtr.appendChild(newtd);\n";
var.val += string_f("\t\tfor (var i=0; i<%u; i++){\n",TBL_FIXED_COLUMNS);
var.val += "\t\t\tvar newtd = document.createElement('td');\n";
var.val += "\t\t\tnewtr.appendChild(newtd);\n";
var.val += "\t\t}\n";
// Set the cursor for all users on this line
map sessions; // Sessions associated with one column on that line
for (auto &p:prefs){
if (p.second.offset_line > line) p.second.offset_line--;
if (p.second.cursor.line > line) p.second.cursor.line--;
if (p.second.cursor.line == line){
auto &s = sessions[p.second.cursor.col];
if (s.size() > 0) s += ',';
s += "'" + p.first + "'";
}
}
for (auto &s:sessions){
var.val += string_f("\t\tvar offc = %u - calc.offcol;\n",s.first);
var.val += string_f("\t\tif(offc >= 0 && offc < %u){\n",TBL_FIXED_COLUMNS);
var.val += "\t\t\tvar tds=trs[offl+1].getElementsByTagName('td');\n";
var.val += string_f("\t\t\tset_td_bg(tds[0],[%s]);\n",s.second.c_str());
var.val += string_f("\t\t\tset_td_bg(tds[offc+1],[%s]);\n",s.second.c_str());
var.val += "\t\t}\n";
}
var.val += "\t}\n";
var.val += string_f("\tif(calc.offline > %u) calc.offline--;\n",line);
var.val += "\tcalc.updoff(calc.offline,calc.offcol);\n";
var.val += "});\n";
}
void CALC::delete_col(VARVAL &var, unsigned col)
{
delete_line_col ((unsigned)-1,col,0,-1);
// For each listener, we must remove the col of each line and append a new one, if it applies
var.val += "calc_loop_board(function(tbl,calc){\n";
var.val += string_f("\tvar offc=%u-calc.offcol;\n",col);
var.val += "console.log('offc='+offc);\n";
var.val += string_f("\tif (offc >= 0 && offc < %u){\n",TBL_FIXED_COLUMNS);
// move the cursor for all users on this column to the next
var.val += "\t\tvar trs = tbl.getElementsByTagName('tr');\n";
// Clear and set the background on the column header
var.val += "\t\tvar defcol='#E8E8E8';\n";
var.val += string_f("\t\tfor (var i=0; i<%u; i++){\n",TBL_FIXED_LINES);
var.val += "\t\t\tvar tds=trs[i].getElementsByTagName('td');\n";
var.val += "\t\t\tif(tds[offc+1].style.background!=defcol){\n";
var.val += "\t\t\t\ttds[offc+2].style.background=tds[offc+1].style.background;\n";
var.val += "\t\t\t}else{\n";
var.val += "\t\t\t\ttds[offc+2].style.background=defcol;\n";
var.val += "\t\t\t}\n";
var.val += "\t\t\tdefcol='white';\n";
var.val += "\t\t}\n";
// Remove current col, append one
var.val += "\t\tvar cols = tbl.getElementsByTagName('col');\n";
var.val += "\t\tcols[0].parentNode.removeChild(cols[offc+1]);\n";
var.val += "\t\tvar newcol = document.createElement('col');\n";
var.val += string_f("\t\tnewcol.style.minWidth='%upx';\n",DEFAULT_COL_WIDTH);
var.val += string_f("\t\tnewcol.style.maxWidth='%upx';\n",DEFAULT_COL_WIDTH);
var.val += "\t\tcols[0].parentNode.appendChild(newcol);\n";
// Remote cur td and append one for each tr
var.val += string_f("\t\tfor (var i=0; i<=%u; i++){\n",TBL_FIXED_LINES);
var.val += "\t\t\tvar tds = trs[i].getElementsByTagName('td');\n";
var.val += "\t\t\ttds[0].parentNode.removeChild(tds[offc+1]);\n";
var.val += "\t\t\tvar newtd = document.createElement('td');\n";
var.val += "\t\t\tif (i==0){\n";
var.val += "\t\t\t\tnewtd.style.textAlign='center';\n";
var.val += "\t\t\t\tnewtd.style.background='#E8E8E8';\n";
var.val += "\t\t\t}\n";
var.val += "\t\t\ttds[0].parentNode.appendChild(newtd);\n";
var.val += "\t\t}\n";
var.val += "\t\tcalc.updoff(calc.offline,calc.offcol);\n";
var.val += "\t}\n";
var.val += "});\n";
}
// Execute a one line scroll
void CALC::vscroll (VARVAL &var, CALC_PREF &pref, int move)
{
unsigned old_offset = pref.offset_line;
if (move == -1){
if (pref.offset_line > 0) pref.offset_line--;
}else{
pref.offset_line++;
}
if (old_offset != pref.offset_line){
set update_ids;
unsigned newline = move < 0 ? 0 : TBL_FIXED_LINES;
newline += pref.offset_line;
for (unsigned col=0; col 0) pref.offset_col--;
}else{
pref.offset_col++;
}
if (old_offset != pref.offset_col){
set update_ids;
unsigned newcol = move < 0 ? 0 : TBL_FIXED_COLUMNS;
newcol += pref.offset_col;
for (unsigned line=0; line
void CALC::remove_session (const char *session)
{
auto p = prefs.find(session);
if (p != prefs.end()) prefs.erase(p);
//tlmp_warning ("CALC::remove_session %s -> %d %lu",session,p!=prefs.end(),prefs.size());
}
void CALC::execstep (
const char *var,
const char *val,
const DOC_CONTEXT &ctx,
const DOC_UI_SPECS_receive &sp,
VARVAL &script_var,
VARVAL ¬ify_var,
set ¬ify_ids,
std::vector &res,
std::vector &unotifies,
string &error,
string &status)
{
auto &pref = prefs[ctx.session];
string api_error;
setactivity();
string tmpvar,tmpval;
unsigned lastline = 25;
unsigned lastcol = 8;
if (strcmp(var,"kbd")==0){
wordproc_kbd(val,pref.mod,tmpvar,tmpval,lastline,lastcol);
var = tmpvar.c_str();
val = tmpval.c_str();
//tlmp_warning ("var=%s val=%s lastline=%u lastcol=%u",var,val,lastline,lastcol);
}
if (strcmp(var,REQ_PRINT)==0){
if (is_any_of(val,"","full")){
VARVAL v;
glocal CALC *doc = this;
glocal pref;
glocal grid;
(val,this,ctx,sp,"calc",true,is_eq(val,"full"),v);
return glocal.doc->define_styles(ctx,sp);
return glocal.doc->define_functions(ctx,glocal.pref);
#define BUTTON_NEWDOC 0
#define BUTTON_COLSTYLE 1
#define BUTTON_INSCOLLINE 2
CALC_MENU menu(specs);
documentd_bar_button (lines,BUTTON_NEWDOC,menu.svg_clear,specs,false,MSG_U(I_RESETDOC,"Reset document"));
documentd_bar_button (lines,BUTTON_INSCOLLINE,"+",specs,false,MSG_U(I_INSCOLLINE,"Insert/Delete lines or columns"));
documentd_bar_button (lines,BUTTON_COLSTYLE,"F",specs,false,MSG_U(I_FORMATCELL,"Specify format for column cells"));
lines += string_f("page 1 \n",gameid);
lines += string_f("%s \n",gameid,glocal.pref.cursor.tostring().c_str());
const char *content = "";
auto g = glocal.grid.find(glocal.pref.cursor);
if (g != glocal.grid.end()) content = g->second.text.c_str();
lines += string_f(" \n"
,gameid,documentd_escape_html(content).c_str());
onevent = "onkeydown='calc_gamepress(event);return false;'";
return glocal.doc->draw_board(ctx,glocal.pref,board_width,board_height,sp.fontsize,ctx.docnum,true,CELL_COOR(),script);
return string_f(" \n"
,gameid,glocal.pref.offset_col*board_width/(4*board_width),board_width/4,scroll_thick);
return string_f(" \n"
,gameid,glocal.pref.offset_line*board_height/(4*board_height),scroll_thick,board_height/4);
res.emplace_back(move(v));
}
}else if (strcmp(var,REQ_FOCUS)==0){
setfocus(script_var);
}else if (strcmp(var,REQ_FUNCTIONS)==0){
VARVAL var;
var.var = VAR_DEFSCRIPT;
var.val = define_functions (ctx,pref);
res.emplace_back(move(var));
}else if (strcmp(var,REQ_STYLES)==0){
VARVAL var;
var.var = VAR_STYLES;
var.val += define_styles(ctx,sp);
res.emplace_back(move(var));
}else if (strcmp(var,REQ_REGION)==0){
// For embedding
// A region is either a range (a1:c3) or a name (not done yet)
CELL_COOR start,end;
if (calc_parserange(val,start,end)){
CALC_PREF p;
p.offset_line = start.line;
p.offset_col = start.col;
CELL_COOR area;
area.line = end.line - start.line+1;
area.col = end.col - start.col+1;
VARVAL var,var_script;
var.var = VAR_CONTENT;
var_script.var = VAR_DEFSCRIPT;
var.val += "\n"; // trick to pass rendering information to the parent document
var.val += draw_board(ctx,p,0,0,sp.fontsize,ctx.docnum,false,area,var_script.val);
res.emplace_back(move(var));
res.emplace_back(move(var_script));
}
}else if (strcmp(var,REQ_CHAT)==0){
appendchat(val,notify_var.val,res,ctx);
}else if (strcmp(var,REQ_GETFIELDS)==0){
VARVAL var;
var.var = VAR_FIELDS;
if (strcmp(val,DIALOG_CALC_FORMATCELL)==0){
// We return the format of the currently selected cell, or selected column.
auto c = col_formats.find(pref.cursor.col);
CALC_COL_FORMAT format;
if (c != col_formats.end()) format = c->second;
var.val = string_f("width:%u\nprecision:%u\nalign:%u\n",format.width,format.precision,format.align);
}
res.emplace_back(var);
}else if (strcmp(var,"dump")==0){
VARVAL var;
var.var = "elements";
var.val += "\n";
static const char *tb[]={"UNKNOWN","NUM","TEXT","FORMULAERR","FORMULA","COMPUTING","EVALED"};
static_assert(sizeof(tb)/sizeof(tb[0])==CELL_STATE_LAST,"tb is incomplete");
for (auto &g:grid){
var.val += string_f("[%u,%u]=%s,%s,%lf\n",g.first.line,g.first.col,tb[g.second.state],g.second.text.c_str(),g.second.value);
}
res.emplace_back(var);
}else if (is_any_of(var,KBD_PAGEUP,KBD_PAGEDOWN)){
if (is_eq(var,KBD_PAGEUP)){
if (pref.offset_line < lastline){
if (pref.cursor.line >= lastline) pref.cursor.line -= pref.offset_line;
pref.offset_line = 0;
}else{
pref.offset_line -= lastline;
pref.cursor.line -= lastline;
}
}else{
pref.offset_line += lastline;
pref.cursor.line += lastline;
}
update_offsets(script_var,ctx,pref);
set ids;
for (unsigned line=0; linesecond.text;
}
if (is_eq(var,KBD_DELETECHAR)){
buf.clear();
}else if (is_eq(var,KBD_BACKSPACE)){
documentd_eraselast(buf);
}else{
buf += val;
}
update_onecell (pref,buf);
update_celledit (pref,script_var);
notify_ids.insert(pref.cursor);
setmodified(ctx.username);
}else if (is_any_of(var,KBD_VMOVE,KBD_BREAK)){
int move;
if (is_eq(var,KBD_BREAK)){
move = 1;
}else{
move = atoi(val);
}
if (move > 0){
pref.cursor.line += move;
}else if (-move <= pref.cursor.line){
pref.cursor.line += move;
}
if (pref.cursor.line < pref.offset_line
|| pref.cursor.line > pref.offset_line+lastline-1){
vscroll (script_var,pref,move);
}
}else if (is_any_of(var,KBD_HMOVE,KBD_TAB)){
int move;
bool shift = pref.mod.shift;
if (is_eq(var,KBD_TAB)){
move = shift ? -1 : 1;
shift = false;
}else{
move = atoi(val);
}
if (shift){
// Modify the current column width
unsigned width = DEFAULT_COL_WIDTH;
auto c = col_formats.find(pref.cursor.col);
if (c != col_formats.end()) width = c->second.width;
width += move*5;
if (width != DEFAULT_COL_WIDTH){
col_formats[pref.cursor.col].width = width;
}else if (c != col_formats.end()){
col_formats.erase(c);
}
update_col_width(notify_var,pref.cursor.col);
setmodified (ctx.username);
}else{
if (move > 0){
pref.cursor.col += move;
}else if (-move <= pref.cursor.col){
pref.cursor.col += move;
}
if (pref.cursor.col < pref.offset_col
|| pref.cursor.col > pref.offset_col+lastcol-1){
hscroll (script_var,pref,move);
}
}
}else if (strcmp(var,"select")==0){
auto tb = str_splitline(val,',');
if (tb.size() == 6){
unsigned cell = atoi(tb[0].c_str());
#if 0
unsigned x = atoi(tb[1].c_str());
unsigned y = atoi(tb[2].c_str());
unsigned button = atoi(tb[3].c_str());
bool shiftkey = tb[4] == "true";
bool ctrlkey = tb[5] == "true";
#endif
unsigned line = pref.offset_line + cell/(TBL_FIXED_COLUMNS+1);
unsigned col = pref.offset_col + cell%(TBL_FIXED_COLUMNS+1);
if (line > 0 && col > 0){
pref.cursor.line = line-1;
pref.cursor.col = col-1;
}else if (line == 0){
// Select one column
}else if (col == 0){
// Select one line
}
}
}else if (strcmp(var,"mousemove")==0){
}else if (strcmp(var,"newgame")==0){
int uval = atoi(val);
if (uval == BUTTON_RELOAD){
documentd_action_reload(res);
}else if (uval == BUTTON_NEWDOC){
VARVAL var;
var.var = VAR_DIALOG;
var.val = DIALOG_CALC_NEW;
res.emplace_back(var);
}else if (uval == BUTTON_COLSTYLE){
VARVAL var;
var.var = VAR_DIALOG;
var.val = DIALOG_CALC_FORMATCELL;
res.emplace_back(var);
}else if (uval == BUTTON_INSCOLLINE){
VARVAL var;
var.var = VAR_DIALOG;
var.val = DIALOG_CALC_INSCOLLINE;
res.emplace_back(var);
}else{
tlmp_error ("calc newgame=%d",uval);
}
setfocus(script_var);
}else if (strcmp(var,"edit")==0){
const char *pt = strchr(val,',');
if (pt != nullptr){
// we only care about the key transfered if it is a Enter, so we change cell
// tlmp_warning ("edit val=%s",val);
string key = string(val,pt-val);
pt++;
update_onecell(pref,pt);
notify_ids.insert (pref.cursor);
setmodified(ctx.username);
if (key=="Enter"){
pref.cursor.line++;
notify_ids.insert (pref.cursor);
setfocus(script_var);
}else if (key=="Tab"){
pref.cursor.col++;
notify_ids.insert (pref.cursor);
setfocus(script_var);
}
}
// The other actions are Used to script modifications of the document
}else if (strcmp(var,"resetgame")==0){
resetgame();
// We erased all the td tag in the table except the first line and the first column
notify_var.val += "calc_loop_board(function(tbl){\n";
notify_var.val += "\tvar tds = tbl.getElementsByTagName('td');\n";
notify_var.val += string_f("\tfor (var i=1; i<=%u; i++){\n",TBL_FIXED_LINES);
notify_var.val += string_f("\t\tfor (var j=1; j<=%u; j++){\n",TBL_FIXED_COLUMNS);
notify_var.val += string_f("\t\t\ttds[i*%u+j].innerHTML = '';\n",TBL_FIXED_COLUMNS+1);
notify_var.val += "\t\t}\n";
notify_var.val += "\t}\n";
notify_var.val += "\tvar cols = tbl.getElementsByTagName('col');\n";
notify_var.val += string_f("\tfor (var j=1; j<=%u; j++){\n",TBL_FIXED_COLUMNS);
notify_var.val += string_f("\t\tcols[j].style.minWidth='%upx';\n",DEFAULT_COL_WIDTH);
notify_var.val += string_f("\t\tcols[j].style.maxWidth='%upx';\n",DEFAULT_COL_WIDTH);
notify_var.val += "\t}\n";
notify_var.val += "});\n";
// Reset position of all users
for (auto &p:prefs){
if (p.second.is_modified()) p.second.reset();
}
update_offsets (notify_var,ctx,pref);
setmodified(ctx.username);
}else if (strcmp(var,"inscolline")==0){
if (strcmp(val,"inslines")==0){
insert_line (notify_var,pref.cursor.line);
}else if (strcmp(val,"inscolumns")==0){
insert_col (notify_var,pref.cursor.col);
}else if (strcmp(val,"dellines")==0){
delete_line (notify_var,pref.cursor.line);
}else if (strcmp(val,"delcolumns")==0){
delete_col (notify_var,pref.cursor.col);
}
update_celledit (pref,script_var);
setmodified(ctx.username);
setfocus (script_var);
}else if (strcmp(var,"formatcol")==0){
vector fields;
documentd_parsefields(val,fields);
CALC_COL_FORMAT format;
bool error = false;
for (auto &f:fields){
if (f.var == "width"){
format.width = atoi(f.val.c_str());;
}else if (f.var == "precision"){
format.precision = atoi(f.val.c_str());
}else if (f.var == "align"){
static map s2a={
{"default",COL_DEFAULT},{"left",COL_LEFT},{"center",COL_CENTER},{"right",COL_RIGHT}};
auto s = s2a.find(f.val);
if (s != s2a.end()){
format.align = s->second;
}else{
error = true;
}
}
}
if (!error){
CALC_COL_FORMAT old_format;
if (format.is_default()){
auto c = col_formats.find(pref.cursor.col);
if (c != col_formats.end()){
old_format = c->second;
col_formats.erase(c);
}
}else{
auto &f = col_formats[pref.cursor.col];
old_format = f;
f = format;
}
if (format.width != old_format.width){
update_col_width(notify_var,pref.cursor.col);
}
if (format.precision != old_format.precision || format.align != old_format.align){
// We have to update all cells in the column, but we only send updates for
// cells actually visible by some users.
for (auto &pp:prefs){
auto &p = pp.second;
if (pref.cursor.col >= p.offset_col && pref.cursor.col < p.offset_col + TBL_FIXED_COLUMNS){
CELL_COOR cell (0,pref.cursor.col);
for (cell.line=p.offset_line; cell.line < p.offset_line+TBL_FIXED_LINES; cell.line++) notify_ids.insert(cell);
}
}
}
setmodified(ctx.username);
}
}else if (strcmp(var,"setcells")==0){
auto tb = str_splitlineq(val,',');
CELL_COOR start,end;
if (tb.size() >= 2 && calc_parserange(tb[0],start,end)){
if (start.line != end.line){
api_error = MSG_U(E_RANGELINE,"range must be on the same line");
}else if (start.col > end.col){
api_error = MSG_R(E_IVLDRANGE_COL);
}else if ((unsigned)(end.col-start.col)+1 != tb.size()-1){
api_error = MSG_U(E_IVLDNBCELLS,"invalid number of cells supplied");
}else{
for (unsigned i=1; i 0) content += ',';
content += getvals ? c.gettext(2) : c.text;
nocol++;
if (nocol == nbcol){
content += '\n';
nocol = 0;
}
});
VARVAL var;
var.var = VAR_RESULT;
var.val = move(content);
res.emplace_back(move(var));
}
}
if (old_cursor != pref.cursor){
notify_ids.insert(old_cursor);
notify_ids.insert(pref.cursor);
update_cellname (pref,script_var);
update_celledit (pref,script_var);
update_lines_cols (pref,old_cursor,pref.cursor,script_var);
}
}else{
error = MSG_U(E_READONLYDOC,"You do not have write access to this document");
}
if (api_error.size() > 0){
VARVAL var;
var.var = VAR_ERROR;
var.val = move(api_error);
res.emplace_back(move(var));
}
if (!pref.is_modified()){
// The prefs was allocated on the fly at the start of this function, but were not used
auto p = prefs.find(ctx.session);
if (p != prefs.end()) prefs.erase(p);
}
}
void CALC::manyexec (
const vector &steps,
const DOC_CONTEXT &ctx,
const DOC_UI_SPECS_receive &sp,
vector &res,
vector &unotifies)
{
string cur_whi = string_f("doc_cur_gameid='%s';\n",gameid.c_str());
VARVAL script_var;
script_var.var = VAR_SCRIPT;
script_var.val = cur_whi;
VARVAL notify_var;
notify_var.var = VAR_NOTIFY;
notify_var.val = cur_whi;
set notify_ids; // Lines to update using SCRIPT
string error,status;
for (auto &v:steps){
execstep (v.var,v.val,ctx,sp,script_var,notify_var,notify_ids,res,unotifies,error,status);
}
if (error.size() == 0){
reset_eval();
eval (notify_ids,error);
}
if (error.size() > 0){
update_msg(false,error,"red",res);
}else if (status.size() > 0){
update_msg(true,status,"red",res);
}else{
update_msg(false," ","white",res);
}
update_cells(notify_ids,notify_var,false);
if (notify_var.val != cur_whi) res.emplace_back(move(notify_var));
if (script_var.val != cur_whi) res.emplace_back(move(script_var));
}