27 #include "assetClass.rcd" 28 #include "godleyTableWindow.rcd" 29 #include "godleyTableWindow.xcd" 32 #include <boost/locale.hpp> 40 #include <cairo/cairo-ps.h> 41 #include <cairo/cairo-pdf.h> 42 #include <cairo/cairo-svg.h> 48 if (!x.empty()) x[0]=toupper(x[0]);
64 const CairoSave cs(cairo);
66 cairo_set_source_rgb(cairo,colour.r,colour.g,colour.b);
77 const int button=x/buttonSpacing;
81 godleyIcon.table.insertRow(idx+1);
84 if (pos!=first && pos!=firstAndLast) godleyIcon.deleteRow(idx+1);
88 godleyIcon.table.moveRow(idx,1);
89 else if (pos!=first && pos!=firstAndLast)
90 godleyIcon.table.moveRow(idx,-1);
94 godleyIcon.table.moveRow(idx,1);
97 try {godleyIcon.update();}
104 const int button=x/buttonSpacing;
105 if (!
cminsky().multipleEquities() && godleyIcon.table.singleEquity()) {
110 godleyIcon.table.insertCol(idx+1);
113 godleyIcon.table.deleteCol(idx+1);
117 godleyIcon.table.moveCol(idx,1);
119 godleyIcon.table.moveCol(idx,-1);
123 godleyIcon.table.moveCol(idx,1);
130 godleyIcon.table.insertCol(idx+1);
133 godleyIcon.table.deleteCol(idx+1);
137 godleyIcon.table.moveCol(idx,1);
139 godleyIcon.table.moveCol(idx,-1);
143 godleyIcon.table.moveCol(idx,1);
147 try {godleyIcon.update();}
151 bool GodleyTableEditor::selectedCellInTable()
const 153 return m_godleyIcon.table.cellInTable(selectedRow, selectedCol);
156 void GodleyTableEditor::draw(cairo_t *cairo)
158 const CairoSave cs(cairo);
159 cairo_scale(cairo,zoomFactor,zoomFactor);
161 pango.setMarkup(
"Flows ↓ / Stock Vars →");
162 rowHeight=pango.height()+2;
163 const double tableHeight=(m_godleyIcon.table.rows()-scrollRowStart+1)*rowHeight;
164 double x=leftTableOffset;
165 double lastAssetBoundary=x;
166 auto assetClass=GodleyAssetClass::noAssetClass;
168 const bool resizeGrid=selectedCol<0 || selectedRow<0 || motionRow>=0 || motionCol>=0;
170 colLeftMargin.clear();
172 for (
unsigned col=0; col<m_godleyIcon.table.cols(); ++col)
175 if (col>0 && col<scrollColStart)
continue;
177 if (assetClass!=m_godleyIcon.table._assetClass(col))
179 if (assetClass!=GodleyAssetClass::noAssetClass)
181 pango.setMarkup(
capitalise(enumKey<GodleyAssetClass::AssetClass>(assetClass)));
183 if (x < pango.width()+lastAssetBoundary+3)
184 x=pango.width()+lastAssetBoundary+3;
185 cairo_move_to(cairo,0.5*(x+lastAssetBoundary-pango.width()),0);
190 assetClass=m_godleyIcon.table._assetClass(col);
191 cairo_move_to(cairo,x+3,topTableOffset);
192 cairo_rel_line_to(cairo,0,tableHeight);
194 cairo_move_to(cairo,x,topTableOffset);
195 cairo_rel_line_to(cairo,0,tableHeight);
196 cairo_set_line_width(cairo,0.5);
199 if (drawButtons && col>0 && col<colWidgets.size())
201 const CairoSave cs(cairo);
202 cairo_move_to(cairo, x, columnButtonsOffset);
203 colWidgets[col].draw(cairo);
208 cairo_move_to(cairo,x-pulldownHot,topTableOffset);
209 pango.setMarkup(
"▼");
213 double y=topTableOffset;
214 double colWidth=minColumnWidth;
215 for (
unsigned row=0; row<m_godleyIcon.table.rows(); ++row)
217 if (row>0 && row<scrollRowStart)
continue;
219 if (drawButtons && col==0 && row>0 && row<rowWidgets.size())
221 const CairoSave cs(cairo);
222 cairo_move_to(cairo, 0, y);
223 rowWidgets[row].draw(cairo);
226 const CairoSave cs(cairo);
227 if (row!=0 || col!=0)
230 string text=utf_to_utf<char>(m_godleyIcon.table.cell(row,col));
235 if (
cminsky().displayValues && col!=0)
239 [
valueId(m_godleyIcon.group.lock(),utf_to_utf<char>(fc.
name))];
242 const double val=fc.
coef*vv->value();
244 if (ee.engExp==-3) ee.engExp=0;
248 catch (
const std::exception& ex)
250 value=string(
"= Err: ")+ex.what();
252 cairo_set_source_rgb(cairo,1,0,0);
257 if ((
int(row)!=selectedRow ||
int(col)!=selectedCol) && !m_godleyIcon.table.initialConditionRow(row))
262 cairo_set_source_rgb(cairo,1,0,0);
263 if (
cminsky().displayStyle==GodleyTable::DRCR)
265 if (assetClass==GodleyAssetClass::asset ||
266 assetClass==GodleyAssetClass::noAssetClass)
267 text = (fc.
coef<0)?
"CR ":
"DR ";
269 text = (fc.
coef<0)?
"DR ":
"CR ";
285 pango.setMarkup(text);
288 colWidth=max(colWidth,pango.width() + (row==0? pulldownHot:0));
289 cairo_move_to(cairo,x+3,y);
297 colLeftMargin.push_back(x);
300 else if (col+1<colLeftMargin.size())
301 x=colLeftMargin[col+1];
305 cairo_move_to(cairo,x-pulldownHot,topTableOffset);
306 pango.setMarkup(
"▼");
311 (
capitalise(enumKey<GodleyAssetClass::AssetClass>(assetClass)));
313 if (x < pango.width()+lastAssetBoundary+3)
314 x=pango.width()+lastAssetBoundary+3;
315 cairo_move_to(cairo,0.5*(x+lastAssetBoundary-pango.width()),0);
318 colLeftMargin.push_back(x);
319 cairo_move_to(cairo,x,topTableOffset);
320 cairo_rel_line_to(cairo,0,tableHeight);
321 cairo_move_to(cairo,x+3,topTableOffset);
322 cairo_rel_line_to(cairo,0,tableHeight);
323 cairo_set_line_width(cairo,0.5);
326 cairo_move_to(cairo,x-pulldownHot,topTableOffset);
330 double y=topTableOffset;
331 cairo_move_to(cairo,x,0);
332 pango.setMarkup(
"A-L-E");
334 double colWidth=pango.width();
336 for (
unsigned row=0; row<m_godleyIcon.table.rows(); ++row)
338 if (row >0 && row<scrollRowStart)
continue;
339 pango.setMarkup(
latexToPango(m_godleyIcon.rowSum(row)));
340 colWidth=max(colWidth,pango.width());
341 cairo_move_to(cairo,x,y);
348 for (
unsigned row=0; row<=m_godleyIcon.table.rows(); ++row)
351 if (row>0 && row<scrollRowStart)
continue;
352 cairo_move_to(cairo,leftTableOffset,y);
353 cairo_line_to(cairo,x,y);
354 cairo_set_line_width(cairo,0.5);
360 colLeftMargin.push_back(x);
361 cairo_move_to(cairo,x,topTableOffset);
362 cairo_rel_line_to(cairo,0,tableHeight);
363 cairo_set_line_width(cairo,0.5);
367 if ((hoverRow>0 || hoverCol>0) &&
368 size_t(hoverRow)<m_godleyIcon.table.rows() &&
369 size_t(hoverCol)<m_godleyIcon.table.cols())
371 const CairoSave cs(cairo);
372 cairo_rectangle(cairo,
373 colLeftMargin[hoverCol],hoverRow*rowHeight+topTableOffset,
374 colLeftMargin[hoverCol+1]-colLeftMargin[hoverCol],rowHeight);
375 cairo_set_line_width(cairo,1);
381 const CairoSave cs(cairo);
382 if (selectedRow==0 || (selectedRow>=
int(scrollRowStart) && selectedRow<
int(m_godleyIcon.table.rows())))
385 if (selectedRow>=
int(scrollRowStart)) j=selectedRow-scrollRowStart+1;
387 if (motionCol>=0 && selectedRow==0 && selectedCol>0)
389 highlightColumn(cairo,selectedCol);
390 highlightColumn(cairo,motionCol);
392 else if (motionRow>=0 && selectedCol==0 && selectedRow>0)
394 highlightRow(cairo,selectedRow);
395 highlightRow(cairo,motionRow);
397 else if (selectedCol==0 ||
398 (selectedCol>=
int(scrollColStart) && selectedCol<
int(m_godleyIcon.table.cols())))
400 if ((selectedRow>1 || selectedRow <0) || selectedCol!=0)
402 if (selectedCol>=
int(scrollColStart)) i=selectedCol-scrollColStart+1;
403 const double xx=colLeftMargin[i], yy=j*rowHeight+topTableOffset;
405 const cairo::CairoSave cs(cairo);
406 cairo_set_source_rgba(cairo,1,1,1,1);
407 cairo_rectangle(cairo,xx,yy,colLeftMargin[i+1]-xx,rowHeight);
408 cairo_fill_preserve(cairo);
409 cairo_set_source_rgba(cairo,1,.55,0,1);
410 cairo_set_line_width(cairo,2);
413 pango.setMarkup(
defang(m_godleyIcon.table.cell(selectedRow,selectedCol)));
414 cairo_move_to(cairo,xx,yy);
418 cairo_move_to(cairo,xx+pango.idxToPos(insertIdx),yy);
419 cairo_rel_line_to(cairo,0,rowHeight);
420 cairo_set_line_width(cairo,1);
422 if (motionRow>0 && motionCol>0)
423 highlightCell(cairo,motionRow,motionCol);
424 if (selectIdx!=insertIdx)
427 cairo_rectangle(cairo,xx+pango.idxToPos(insertIdx),yy,
428 pango.idxToPos(selectIdx)-pango.idxToPos(insertIdx),rowHeight);
429 cairo_set_source_rgba(cairo,0.5,0.5,0.5,0.5);
438 double GodleyTableEditor::height()
const 440 return godleyIcon().table.rows()*rowHeight;
443 int GodleyTableEditor::colX(
double x)
const 445 if (colLeftMargin.size()<2 || x<colLeftMargin[0])
return -1;
446 if (x<colLeftMargin[1])
return 0;
447 auto p=std::upper_bound(colLeftMargin.begin(), colLeftMargin.end(), x);
448 size_t r=p-colLeftMargin.begin()-2+scrollColStart;
449 if (r>m_godleyIcon.table.cols()-1) r=-1;
453 int GodleyTableEditor::rowY(
double y)
const 455 int c=(y-topTableOffset)/rowHeight;
456 if (c>0) c+=scrollRowStart-1;
457 if (c<0 ||
size_t(c)>m_godleyIcon.table.rows()) c=-1;
461 int GodleyTableEditor::textIdx(
double x)
const 463 const cairo::Surface surf(cairo_recording_surface_create(CAIRO_CONTENT_COLOR,NULL));
464 Pango pango(surf.cairo());
465 if (selectedCellInTable() && (selectedRow!=1 || selectedCol!=0))
468 auto&
str=m_godleyIcon.table.cell(selectedRow,selectedCol);
469 str=utf_to_utf<char>(
str);
472 if (selectedCol>=
int(scrollColStart)) j=selectedCol-scrollColStart+1;
473 x-=colLeftMargin[j]+2;
475 if (x>0 &&
str.length())
477 auto p=pango.posToIdx(x);
493 requestRedrawCanvas();
494 switch (clickType(x,y))
498 const unsigned r=rowY(y);
499 if (r<rowWidgets.size())
501 rowWidgets[r].invoke(x);
503 selectedCol=selectedRow=-1;
509 const unsigned c=colX(x);
510 const unsigned visibleCol=c-scrollColStart+1;
511 if (c<colWidgets.size() && visibleCol < colLeftMargin.size())
513 colWidgets[c].invoke(x-colLeftMargin[visibleCol]);
515 selectedCol=selectedRow=-1;
520 selectIdx=insertIdx=0;
521 selectedCol=selectedRow=-1;
524 if (selectedRow>=0 && selectedCol>=0)
526 selectedCol=selectedRow=-1;
531 if (selectedCellInTable() && (selectedRow!=1 || selectedCol!=0))
534 auto&
str=m_godleyIcon.table.cell(selectedRow,selectedCol);
535 str=utf_to_utf<char>(
str);
536 m_godleyIcon.table.savedText=
str;
537 selectIdx=insertIdx = textIdx(x);
540 selectIdx=insertIdx=0;
545 void GodleyTableEditor::mouseUp(
double x,
double y)
550 const int c=colX(x), r=rowY(y);
551 motionRow=motionCol=-1;
553 if ((selectedCol==0 && selectedRow==1) || (c==0 && r==1) ||
size_t(selectedRow)>=(m_godleyIcon.table.rows()) ||
size_t(r)>=(m_godleyIcon.table.rows()) ||
size_t(c)>=(m_godleyIcon.table.cols()) ||
size_t(selectedCol)>=(m_godleyIcon.table.cols()))
558 if (c>0 &&
size_t(c)<m_godleyIcon.table.cols() && selectedCol>0 && size_t(selectedCol)<m_godleyIcon.table.cols() && c!=selectedCol && !(colLeftMargin[c+1]-x < pulldownHot))
559 m_godleyIcon.table.moveCol(selectedCol,c-selectedCol);
561 else if (r>0 && selectedCol==0)
563 if (r!=selectedRow && !m_godleyIcon.table.initialConditionRow(selectedRow) && !m_godleyIcon.table.initialConditionRow(r))
564 m_godleyIcon.table.moveRow(selectedRow,r-selectedRow);
566 else if ((c!=selectedCol || r!=selectedRow) && c>0 && r>0)
568 swap(m_godleyIcon.table.cell(selectedRow,selectedCol), m_godleyIcon.table.cell(r,c));
574 else if (selectIdx!=insertIdx)
576 requestRedrawCanvas();
579 void GodleyTableEditor::mouseMoveB1(
double x,
double y)
583 motionCol=colX(x), motionRow=rowY(y);
584 if (motionCol==selectedCol && motionRow==selectedRow)
585 selectIdx=textIdx(x);
586 requestRedrawCanvas();
589 void GodleyTableEditor::mouseMove(
double x,
double y)
599 for (
auto& i: rowWidgets) i.hover(-1);
600 for (
auto& i: colWidgets) i.hover(-1);
601 hoverRow=hoverCol=-1;
602 switch (clickType(x,y))
606 const unsigned r=rowY(y);
607 if (r<rowWidgets.size())
608 rowWidgets[r].hover(x);
609 requestRedrawCanvas();
614 const unsigned c=colX(x);
615 if (c<colWidgets.size())
616 colWidgets[c].hover(x-colLeftMargin[c]);
617 requestRedrawCanvas();
624 if (hoverRow>0) hoverRow-=scrollRowStart-1;
626 if (hoverCol>0) hoverCol-=scrollColStart-1;
631 inline constexpr
char control(
char x) {
return x-
'`';}
633 void GodleyTableEditor::keyPress(
int keySym,
const std::string& utf8)
636 auto& table=m_godleyIcon.table;
637 if (selectedCellInTable() && (selectedCol!=0 || selectedRow!=1))
639 auto&
str=table.cell(selectedRow,selectedCol);
640 str=utf_to_utf<char>(
str);
641 if (utf8.length() && (keySym<0x7f || (0xffaa <= keySym && keySym <= 0xffbf)))
645 if (
unsigned(utf8[0])>=
' ' && utf8[0]!=0x7f)
648 if (insertIdx>=
str.length()) insertIdx=
str.length();
649 str.insert(insertIdx,utf8);
650 selectIdx=insertIdx+=utf8.length();
681 if (selectedRow>=0 &&
size_t(selectedRow)<=table.rows() &&
682 selectedCol>=0 && size_t(selectedCol)<=table.cols())
683 table.cell(selectedRow, selectedCol)=table.savedText;
684 selectedRow=selectedCol=-1;
689 selectedRow=selectedCol=-1;
697 else navigateRight();
722 case 0xff09:
case 0xff53:
723 selectedRow=0; selectedCol=1;
break;
725 selectedRow=table.rows()-1; selectedCol=table.cols()-1;
break;
727 selectedRow=0; selectedCol=table.cols()-1;
break;
729 selectedRow=2; selectedCol=0;
break;
731 selectedRow=table.rows()-1; selectedCol=0;
break;
736 requestRedrawCanvas();
739 void GodleyTableEditor::delSelection()
741 if (selectedCellInTable() && insertIdx!=selectIdx)
743 auto&
str=m_godleyIcon.table.cell(selectedRow,selectedCol);
744 str.erase(min(insertIdx,selectIdx),abs(
int(insertIdx)-
int(selectIdx)));
745 selectIdx=insertIdx=min(insertIdx,selectIdx);
749 void GodleyTableEditor::handleBackspace()
751 if (!selectedCellInTable())
return;
752 auto& table=m_godleyIcon.table;
753 auto&
str=table.cell(selectedRow,selectedCol);
754 if (insertIdx!=selectIdx)
756 else if (insertIdx>0 && insertIdx<=
str.length())
764 void GodleyTableEditor::handleDelete()
766 if (!selectedCellInTable())
return;
767 auto& table=m_godleyIcon.table;
768 auto&
str=table.cell(selectedRow,selectedCol);
769 if (insertIdx!=selectIdx)
771 else if (insertIdx<
str.length())
778 if (!selectedCellInTable())
return;
780 if (selectIdx==insertIdx)
782 m_godleyIcon.table.cell(selectedRow,selectedCol).clear();
785 requestRedrawCanvas();
788 void GodleyTableEditor::copy()
790 if (!selectedCellInTable())
return;
791 auto&
str=m_godleyIcon.table.cell(selectedRow,selectedCol);
792 if (selectIdx!=insertIdx)
793 cminsky().clipboard.putClipboard
794 (
str.substr(min(selectIdx,insertIdx), abs(
int(selectIdx)-
int(insertIdx))));
799 void GodleyTableEditor::paste()
801 if (!selectedCellInTable())
return;
803 auto&
str=m_godleyIcon.table.cell(selectedRow,selectedCol);
804 auto stringToInsert=
cminsky().clipboard.getClipboard();
806 auto p=stringToInsert.find(
'\n');
808 stringToInsert=stringToInsert.substr(0,p-1);
809 str.insert(insertIdx,stringToInsert);
810 selectIdx=insertIdx+=stringToInsert.length();
811 requestRedrawCanvas();
816 const int c=colX(x), r=rowY(y);
818 if (x<leftTableOffset && r>0)
820 if (y<topTableOffset && y>columnButtonsOffset && c>0)
825 if (colLeftMargin[c+1]-x < pulldownHot)
832 if (c>0 && c<
int(m_godleyIcon.table.cols()))
833 if (r>0 && r<
int(m_godleyIcon.table.rows()))
839 std::set<string> GodleyTableEditor::matchingTableColumns(
double x)
841 const int col=colXZoomed(x);
842 return matchingTableColumnsByCol(col);
845 std::set<string> GodleyTableEditor::matchingTableColumnsByCol(
int col)
847 if (col<0||col>=static_cast<int>(godleyIcon().table.cols()))
return {};
851 void GodleyTableEditor::addStockVar(
double x)
853 const int c=colXZoomed(x);
857 void GodleyTableEditor::addStockVarByCol(
int c)
859 if (c>0) m_godleyIcon.table.insertCol(c+1);
864 const int c=colXZoomed(x);
865 importStockVarByCol(name, c);
868 void GodleyTableEditor::importStockVarByCol(
const string& name,
int c)
870 if (c>0 &&
size_t(c)<m_godleyIcon.table.cols())
872 m_godleyIcon.table.cell(0,c)=name;
879 void GodleyTableEditor::deleteStockVar(
double x)
881 const int c=colXZoomed(x);
882 deleteStockVarByCol(c);
885 void GodleyTableEditor::deleteStockVarByCol(
int c)
888 m_godleyIcon.table.deleteCol(c+1);
891 void GodleyTableEditor::addFlow(
double y)
893 const int r=rowYZoomed(y);
897 void GodleyTableEditor::addFlowByRow(
int r)
900 m_godleyIcon.table.insertRow(r+1);
903 void GodleyTableEditor::deleteFlow(
double y)
905 const int r=rowYZoomed(y);
909 void GodleyTableEditor::deleteFlowByRow(
int r)
912 m_godleyIcon.deleteRow(r+1);
918 string tmpStr=
"This will convert "+
var+
" from "+classdesc::enumKey<GodleyAssetClass::AssetClass>(oldAC)+
" to "+classdesc::enumKey<GodleyAssetClass::AssetClass>(targetAC)+
". Are you sure?";
927 const unsigned c=colX(x);
929 if (c>=m_godleyIcon.table.cols())
return tmpStr;
930 if (clickType(x,y)==colWidget) {
931 const unsigned visibleCol=c-scrollColStart+1;
932 if (c<colWidgets.size() && visibleCol < colLeftMargin.size())
934 auto moveVar=m_godleyIcon.table.cell(0,c);
935 auto oldAssetClass=m_godleyIcon.table._assetClass(c);
936 auto targetAssetClassPlus=m_godleyIcon.table._assetClass(c+1);
937 auto targetAssetClassMinus=m_godleyIcon.table._assetClass(c-1);
938 if (colWidgets[c].button(x-colLeftMargin[visibleCol])==3 && oldAssetClass!=GodleyAssetClass::equity) {
939 if (targetAssetClassPlus!=oldAssetClass && !moveVar.empty() && targetAssetClassPlus!=GodleyAssetClass::equity && targetAssetClassPlus!=GodleyAssetClass::noAssetClass)
941 else if (targetAssetClassPlus==GodleyAssetClass::noAssetClass && !moveVar.empty())
942 tmpStr=
"Cannot convert stock variable to an equity class";
944 else if (colWidgets[c].button(x-colLeftMargin[visibleCol])==2 && oldAssetClass==GodleyAssetClass::asset && oldAssetClass!=GodleyAssetClass::equity && targetAssetClassMinus!=GodleyAssetClass::asset) {
945 if (targetAssetClassPlus!=oldAssetClass && !moveVar.empty() && targetAssetClassPlus!=GodleyAssetClass::equity && targetAssetClassPlus!=GodleyAssetClass::noAssetClass)
947 else if ((targetAssetClassPlus==GodleyAssetClass::equity || targetAssetClassPlus==GodleyAssetClass::noAssetClass) && !moveVar.empty())
948 tmpStr=
"Cannot convert stock variable to an equity class";
950 else if (colWidgets[c].button(x-colLeftMargin[visibleCol])==2 && oldAssetClass!=GodleyAssetClass::equity) {
951 if (targetAssetClassMinus!=oldAssetClass && !moveVar.empty())
964 if (selectedRow==0 &&
size_t(selectedCol)<m_godleyIcon.table.cols())
967 if (c>0 && selectedCol>0 && c!=selectedCol) {
968 auto swapVar=m_godleyIcon.table.cell(0,selectedCol);
969 auto oldAssetClass=m_godleyIcon.table._assetClass(selectedCol);
970 auto targetAssetClass=m_godleyIcon.table._assetClass(c);
971 if (!swapVar.empty() && !(colLeftMargin[c+1]-x < pulldownHot)) {
972 if (targetAssetClass!=oldAssetClass && targetAssetClass!=GodleyAssetClass::equity && targetAssetClass!=GodleyAssetClass::noAssetClass)
974 else if ((targetAssetClass==GodleyAssetClass::equity || targetAssetClass==GodleyAssetClass::noAssetClass) || oldAssetClass==GodleyAssetClass::noAssetClass)
975 tmpStr=
"Cannot convert stock variable to an equity class";
982 void GodleyTableEditor::highlightColumn(cairo_t* cairo,
unsigned col)
984 if (col<scrollColStart)
return;
985 const double x=colLeftMargin[col-scrollColStart+1];
986 const double width=colLeftMargin[col-scrollColStart+2]-x;
987 const double tableHeight=(m_godleyIcon.table.rows()-scrollRowStart+1)*rowHeight;
988 cairo_rectangle(cairo,x,topTableOffset,width,tableHeight);
989 cairo_set_source_rgba(cairo,1,1,1,0.5);
993 void GodleyTableEditor::highlightRow(cairo_t* cairo,
unsigned row)
995 if (row<scrollRowStart)
return;
996 const double y=(row-scrollRowStart+1)*rowHeight+topTableOffset;
997 cairo_rectangle(cairo,leftTableOffset,y,colLeftMargin.back()-leftTableOffset,rowHeight);
998 cairo_set_source_rgba(cairo,1,1,1,0.5);
1002 void GodleyTableEditor::highlightCell(cairo_t* cairo,
unsigned row,
unsigned col)
1004 if (row<scrollRowStart || col<scrollColStart)
return;
1005 const double x=colLeftMargin[col-scrollColStart+1];
1006 const double width=colLeftMargin[col-scrollColStart+2]-x;
1007 const double y=(row-scrollRowStart+1)*rowHeight+topTableOffset;
1008 cairo_rectangle(cairo,x,y,width,rowHeight);
1009 cairo_set_source_rgba(cairo,1,1,1,0.5);
1013 void GodleyTableEditor::pushHistory()
1015 while (history.size()>maxHistory) history.pop_front();
1017 if (history.empty() || !(history.back()==m_godleyIcon.table)) {
1018 history.push_back(m_godleyIcon.table);
1020 historyPtr=history.size();
1025 if (historyPtr==history.size())
1027 historyPtr-=changes;
1028 if (historyPtr > 0 && historyPtr <= history.size())
1030 auto& d=history[historyPtr-1];
1032 if (d.getData().empty())
return;
1033 m_godleyIcon.table=d;
1037 void GodleyTableEditor::adjustWidgets()
1040 for (
size_t i=0; i<m_godleyIcon.table.rows(); ++i)
1041 rowWidgets.emplace_back(m_godleyIcon, i);
1043 for (
size_t i=0; i<m_godleyIcon.table.cols(); ++i)
1044 colWidgets.emplace_back(m_godleyIcon, i);
1047 if (rowWidgets.size()==2)
1048 rowWidgets[1].pos=firstAndLast;
1049 else if (rowWidgets.size()==3)
1051 rowWidgets[1].pos=first;
1052 rowWidgets.back().pos=second;
1054 else if (rowWidgets.size()>3)
1056 rowWidgets[1].pos=first;
1057 rowWidgets[2].pos=second;
1058 rowWidgets.back().pos=last;
1060 if (colWidgets.size()==2)
1061 colWidgets[1].pos=firstAndLast;
1062 else if (colWidgets.size()>2)
1064 colWidgets[1].pos=first;
1065 colWidgets.back().pos=last;
1069 void GodleyTableEditor::update()
1071 if (selectedCol>0 && selectedCol<
int(m_godleyIcon.table.cols()))
1077 for (
const auto& sv: m_godleyIcon.stockVars())
1078 if (sv->valueId()==m_godleyIcon.valueId(m_godleyIcon.table.savedText))
1082 auto newName=utf_to_utf<char>(m_godleyIcon.table.cell(selectedRow,selectedCol));
1083 if (!newName.empty())
1085 savedItem.swap(
minsky().canvas.item);
1091 if (m_godleyIcon.table.initialConditionRow(selectedRow))
1094 if (!m_godleyIcon.table.savedText.empty() && m_godleyIcon.table.cell(selectedRow,selectedCol).empty())
1095 m_godleyIcon.table.cell(selectedRow,selectedCol)=
"0";
1102 for (
auto& i: godleyTables)
1103 if (
auto* g=dynamic_cast<GodleyIcon*>(i.get()))
1110 void GodleyTableEditor::checkCell00()
1112 if (selectedCol==0 && (selectedRow==0 || selectedRow ==1))
1120 void GodleyTableEditor::navigateRight()
1127 if (selectedCol>=
int(m_godleyIcon.table.cols()))
1129 if (selectedRow>0) selectedCol=0;
1137 void GodleyTableEditor::navigateLeft()
1145 selectedCol=m_godleyIcon.table.cols()-1;
1149 insertIdx=godleyIcon().table.cellInTable(selectedRow, selectedCol)?
1150 godleyIcon().table.cell(selectedRow, selectedCol).length(): 0;
1154 void GodleyTableEditor::navigateUp()
1158 selectedRow=(selectedRow-1)%m_godleyIcon.table.rows();
1162 void GodleyTableEditor::navigateDown()
1166 selectedRow=(selectedRow+1)%m_godleyIcon.table.rows();
1170 template <ButtonW
idgetEnums::RowCol rowCol>
1175 cairo_get_current_point(cairo,&x0, &y0);
1177 const CairoSave cs(cairo);
1180 pango.setFontSize(0.8*buttonSpacing);
1181 pango.setMarkup(label);
1182 cairo_set_source_rgb(cairo,r,g,b);
1186 cairo_rectangle(cairo, x0, y0+0.2*pango.height(), buttonSpacing, buttonSpacing);
1187 if (idx==m_mouseOver)
1188 cairo_set_source_rgb(cairo,0,0,0);
1190 cairo_set_source_rgb(cairo,0.5,0.5,0.5);
1191 cairo_set_line_width(cairo,1);
1192 cairo_stroke(cairo);
1193 cairo_move_to(cairo,x0+buttonSpacing,y0);
1196 template <ButtonW
idgetEnums::RowCol rowCol>
1199 const CairoSave cs(cairo);
1201 if (rowCol==row || (!
cminsky().multipleEquities() && godleyIcon.table.singleEquity())) {
1202 if (rowCol == row || (rowCol == col && pos!=last))
1203 drawButton(cairo,
"+",0,1,0,idx++);
1204 if ((rowCol == row && pos!=first && pos!=firstAndLast) || (rowCol == col && pos!=last))
1205 drawButton(cairo,
"—",1,0,0,idx++);
1206 if ((rowCol == row && pos!=first && pos!=second && pos!=firstAndLast) || (rowCol == col && pos!=first && pos!=last))
1207 drawButton(cairo,rowCol==row?
"↑":
"←",0,0,0,idx++);
1208 if ((pos!=first && pos!=last && pos!=firstAndLast) || (rowCol == col && pos!=last))
1209 drawButton(cairo,rowCol==row?
"↓":
"→",0,0,0,idx++);
1211 drawButton(cairo,
"+",0,1,0,idx++);
1212 if ((pos!=first && pos!=firstAndLast) || rowCol == col)
1213 drawButton(cairo,
"—",1,0,0,idx++);
1214 if (pos!=first && pos!=second && pos!=firstAndLast)
1215 drawButton(cairo,rowCol==row?
"↑":
"←",0,0,0,idx++);
1216 if ((pos!=first && pos!=last && pos!=firstAndLast) || (rowCol == col && pos!=last))
1217 drawButton(cairo,rowCol==row?
"↓":
"→",0,0,0,idx++);
std::string expMultiplier(int exp)
void importDuplicateColumn(GodleyTable &srcTable, int srcCol)
find any duplicate column, and use it as a source column for balanceDuplicateColumns ...
string constructMessage(GodleyAssetClass::AssetClass &targetAC, GodleyAssetClass::AssetClass &oldAC, string &var)
void balanceDuplicateColumns(const GodleyIcon &srcTable, int srcCol)
makes all duplicated columns consistent with srcTable, srcCol
void redrawAllGodleyTables()
request all Godley table windows to redraw
std::string latexToPango(const char *s)
EngNotation engExp(double value)
return formatted mantissa and exponent in engineering format
unsigned numBytes(unsigned char x)
a wrapper around std::ofstream that checks the write succeeded, throwing an exception if not ...
VariableValues variableValues
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::GodleyTableWindow)
std::shared_ptr< Item > ItemPtr
string valueId(const string &name)
construct a valueId from fully qualified name @ name should not be canonicalised
void renameAllInstances(const std::string &newName)
rename all instances of variable as item to newName
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky's state cha...
size_t prevIndex(const std::string &str, size_t index)
return index of previous character to index
std::string str(T x)
utility function to create a string representation of a numeric type
const Minsky & cminsky()
const version to help in const correctness
std::string mantissa(double value, const EngNotation &, int digits=3)
struct anonymous_namespace{godleyTableWindow.cc}::Colour assetColour[]
void showAsset(Pango &pango, cairo_t *cairo, GodleyAssetClass::AssetClass assetClass)
represents a numerical coefficient times a variable (a "flow")
void requestRedraw()
request a redraw on the screen
string capitalise(string x)
constexpr char control(char x)
std::set< string > matchingTableColumns(const GodleyIcon &currTable, GodleyAssetClass::AssetClass ac)
ItemPtr item
item or wire obtained by get*At() calls