forked from rosh/MQL5Book
245 lines
8.8 KiB
MQL5
245 lines
8.8 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| Tableau.mqh |
| |||
//| Copyright (c) 2021-2022, Marketeer |
| |||
//| https://www.mql5.com/en/users/marketeer |
| |||
//| On-chart table with given number of columns and rows |
| |||
//+------------------------------------------------------------------+
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Defines |
| |||
//+------------------------------------------------------------------+
| |||
#define TBL_FLAG_ROW_0_HEADER 1 // bold text in top-most row
| |||
#define TBL_FLAG_COL_0_HEADER 2 // bold text in left-most column
| |||
#define TBL_CELL_HEIGHT_AUTO -1
| |||
#define TBL_CELL_WIDTH_AUTO 100 // always auto width!
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| UI table |
| |||
//+------------------------------------------------------------------+
| |||
class Tableau
| |||
{
| |||
private:
| |||
const string prefix;
| |||
const ENUM_BASE_CORNER corner;
| |||
const int nrows, ncols;
| |||
int cellHeight, cellWidth;
| |||
const int gap;
| |||
const int fontSize;
| |||
const int flags;
| |||
const string res;
| |||
| |||
const string defaultFont;
| |||
const string boldFont;
| |||
const color bgColor;
| |||
const uchar bgTrans;
| |||
| |||
public:
| |||
static ENUM_ANCHOR_POINT corner2anchor(const ENUM_BASE_CORNER corner)
| |||
{
| |||
return (ENUM_ANCHOR_POINT)(corner * 2);
| |||
};
| |||
| |||
static bool isBottomSide(const ENUM_BASE_CORNER corner)
| |||
{
| |||
return corner == CORNER_LEFT_LOWER || corner == CORNER_RIGHT_LOWER;
| |||
}
| |||
| |||
static bool isRightSide(const ENUM_BASE_CORNER corner)
| |||
{
| |||
return corner == CORNER_RIGHT_UPPER || corner == CORNER_RIGHT_LOWER;
| |||
}
| |||
| |||
Tableau(const string pref, const int rows, const int cols,
| |||
const int height = TBL_CELL_HEIGHT_AUTO, const int width = TBL_CELL_WIDTH_AUTO,
| |||
const ENUM_BASE_CORNER c = CORNER_RIGHT_LOWER, const int g = 8,
| |||
const int fsize = 8, const string font = "Consolas", const string bold = "Arial Black",
| |||
const int mask = TBL_FLAG_COL_0_HEADER,
| |||
const color bgc = 0x808080, const uchar bgt = 0xC0):
| |||
prefix(pref + (string)rows + "x" + (string)cols), corner(c), nrows(rows), ncols(cols),
| |||
cellHeight(height), cellWidth(width), gap(g),
| |||
fontSize(fabs(fsize)), flags(mask), defaultFont(font), boldFont(bold),
| |||
bgColor(bgc), bgTrans(bgt),
| |||
res("::LLET" + (string)ChartID())
| |||
{
| |||
if(height == TBL_CELL_HEIGHT_AUTO) cellHeight = fontSize * 2;
| |||
| |||
const ENUM_ANCHOR_POINT anchor = corner2anchor(corner);
| |||
const bool invertY = isBottomSide(corner);
| |||
const bool invertX = isRightSide(corner);
| |||
| |||
const int w = ncols * (width + gap);
| |||
const int h = nrows * (cellHeight + gap / 2) + gap / 2;
| |||
| |||
uint img[];
| |||
ArrayResize(img, w * h);
| |||
ArrayInitialize(img, (uint)ColorToARGB(bgColor, bgTrans));
| |||
ResourceCreate(res, img, w, h, 0, 0, w, COLOR_FORMAT_ARGB_NORMALIZE);
| |||
| |||
string name = prefix + "_";
| |||
ObjectCreate(0, name, OBJ_BITMAP_LABEL, 0, 0, 0);
| |||
ObjectSetString(0, name, OBJPROP_BMPFILE, 0, res);
| |||
ObjectSetString(0, name, OBJPROP_BMPFILE, 1, res);
| |||
ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
| |||
ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
| |||
ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
| |||
ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
| |||
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, gap / 2);
| |||
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, gap / 2);
| |||
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, ColorToARGB((color)ChartGetInteger(0, CHART_COLOR_GRID), 192));
| |||
ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
| |||
| |||
for(int i = 0; i < nrows; i++)
| |||
{
| |||
int k = invertY ? nrows - i - 1 : i;
| |||
| |||
for(int j = 0; j < ncols; j++)
| |||
{
| |||
int m = invertX ? ncols - j - 1 : j;
| |||
| |||
name = prefix + "_" +(string)i + "_" + (string)j;
| |||
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
| |||
ObjectSetString(0, name, OBJPROP_TEXT, "...");
| |||
ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
| |||
ObjectSetInteger(0, name, OBJPROP_ANCHOR, invertY ? ANCHOR_RIGHT_LOWER : ANCHOR_RIGHT_UPPER /*anchor*/);
| |||
ObjectSetInteger(0, name, OBJPROP_XSIZE, width);
| |||
ObjectSetInteger(0, name, OBJPROP_YSIZE, cellHeight);
| |||
ObjectSetString(0, name, OBJPROP_TEXT, name);
| |||
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, m * width + m * gap + gap);
| |||
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, k * (cellHeight + gap / 2) + gap);
| |||
ObjectSetInteger(0, name, OBJPROP_COLOR, ChartGetInteger(0, CHART_COLOR_FOREGROUND));
| |||
ObjectSetString(0, name, OBJPROP_FONT, defaultFont);
| |||
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
| |||
| |||
if((flags & TBL_FLAG_COL_0_HEADER) != 0 && j == 0)
| |||
{
| |||
//if(!((flags & TBL_FLAG_ROW_0_HEADER) == 0 && i == 0))
| |||
{
| |||
ObjectSetString(0, name, OBJPROP_FONT, boldFont);
| |||
}
| |||
}
| |||
if((flags & TBL_FLAG_ROW_0_HEADER) != 0 && i == 0)
| |||
{
| |||
//if(!((flags & TBL_FLAG_COL_0_HEADER) == 0 && j == 0))
| |||
{
| |||
ObjectSetString(0, name, OBJPROP_FONT, boldFont);
| |||
}
| |||
}
| |||
}
| |||
}
| |||
}
| |||
| |||
~Tableau()
| |||
{
| |||
ObjectsDeleteAll(0, prefix, 0);
| |||
}
| |||
| |||
int getRows() const
| |||
{
| |||
return nrows;
| |||
}
| |||
| |||
int getColumns() const
| |||
{
| |||
return ncols;
| |||
}
| |||
| |||
bool fill(const string &data[], const string &hint[]) const
| |||
{
| |||
if(!TextSetFont(defaultFont, fontSize * -10))
| |||
{
| |||
Print("TextSetFont failed: ", _LastError);
| |||
}
| |||
| |||
const bool invertY = isBottomSide(corner);
| |||
const bool invertX = isRightSide(corner);
| |||
| |||
const int n = ArraySize(data);
| |||
int cols[];
| |||
int offsets[];
| |||
ArrayResize(cols, ncols);
| |||
ArrayInitialize(cols, 0);
| |||
ArrayResize(offsets, ncols + 1);
| |||
ArrayInitialize(offsets, 0);
| |||
| |||
for(int i = 0; i < n; i++)
| |||
{
| |||
const int y = i / ncols; // row
| |||
const int x = i % ncols; // column
| |||
int w, h;
| |||
| |||
if(x < ncols && TextGetSize(data[i], w, h))
| |||
{
| |||
//if((int)(w * 1.5) > cols[x]) cols[x] = (int)(w * 1.5);
| |||
if(w > cols[x]) cols[x] = w;
| |||
}
| |||
else
| |||
{
| |||
Print(__FUNCSIG__, " failed: ", _LastError);
| |||
return false;
| |||
}
| |||
}
| |||
| |||
if(invertX)
| |||
{
| |||
ArrayReverse(cols);
| |||
}
| |||
| |||
int width = 0;
| |||
for(int i = 0; i < ncols; i++)
| |||
{
| |||
offsets[i] = (i > 0 ? offsets[i - 1] + cols[i - 1] : 0);
| |||
width += cols[i] + gap;
| |||
}
| |||
offsets[ncols] = offsets[ncols - 1] + cols[ncols - 1];
| |||
| |||
if(!invertX)
| |||
{
| |||
for(int k = 0; k < ncols; k++)
| |||
{
| |||
offsets[k] = offsets[k + 1] + gap;
| |||
}
| |||
}
| |||
| |||
const int h = ArraySize(hint);
| |||
| |||
for(int i = 0; i < n; i++)
| |||
{
| |||
const int y = i / ncols;
| |||
const int x = i % ncols;
| |||
if(x < ncols)
| |||
{
| |||
int k = invertY ? nrows - y - 1 : y;
| |||
int m = invertX ? ncols - x - 1 : x;
| |||
| |||
string name = prefix + "_" +(string)y + "_" + (string)x;
| |||
// NB: setting dimensions for OBJ_LABEL does nothing,
| |||
// cause it's automatically sized to text content
| |||
// this is a problem, but has minor impact
| |||
ObjectSetInteger(0, name, OBJPROP_XSIZE, cols[x]);
| |||
ObjectSetInteger(0, name, OBJPROP_YSIZE, cellHeight);
| |||
ObjectSetString(0, name, OBJPROP_TEXT, data[i]);
| |||
if(i < h && StringLen(hint[i]) > 0)
| |||
{
| |||
ObjectSetString(0, name, OBJPROP_TOOLTIP, hint[i]);
| |||
}
| |||
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, offsets[m] + gap * m + gap);
| |||
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, k * (cellHeight + gap / 2) + gap);
| |||
}
| |||
}
| |||
| |||
string name = prefix + "_";
| |||
ObjectSetInteger(0, name, OBJPROP_XSIZE, width + gap);
| |||
| |||
const int w = width + gap;
| |||
const int h1 = nrows * (cellHeight + gap / 2) + gap / 2;
| |||
| |||
uint img[];
| |||
ArrayResize(img, w * h1);
| |||
ArrayInitialize(img, (uint)ColorToARGB(bgColor, bgTrans));
| |||
ResourceCreate(res, img, w, h1, 0, 0, w, COLOR_FORMAT_ARGB_NORMALIZE);
| |||
| |||
ChartRedraw();
| |||
return true;
| |||
}
| |||
};
| |||
//+------------------------------------------------------------------+
|