Article-17653-MQL5-MVC-Tabl.../Article-17653-MQL5-MVC-Table-Model.mq5

1543 lines
78 KiB
MQL5

//+------------------------------------------------------------------+
2026-03-25 14:07:04 +07:00
//| Article-17653-MQL5-MVC-Table-Model.mq5 |
//| Copyright 2026, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Включаемые библиотеки |
//+------------------------------------------------------------------+
#include <Arrays\List.mqh>
//--- Форвард-декларация классов
class CTableCell; // Класс ячейки таблицы
class CTableRow; // Класс строки таблицы
class CTableModel; // Класс модели таблицы
//+------------------------------------------------------------------+
//| Макросы |
//+------------------------------------------------------------------+
#define MARKER_START_DATA -1 // Маркер начала данных в файле
#define MAX_STRING_LENGTH 128 // Максимальная длина строки в ячейке
//+------------------------------------------------------------------+
//| Перечисления |
//+------------------------------------------------------------------+
enum ENUM_OBJECT_TYPE // Перечисление типов объектов
{
OBJECT_TYPE_TABLE_CELL=10000, // Ячейка таблицы
OBJECT_TYPE_TABLE_ROW, // Строка таблицы
OBJECT_TYPE_TABLE_MODEL, // Модель таблицы
};
enum ENUM_CELL_COMPARE_MODE // Режимы сравнения ячеек таблицы
{
CELL_COMPARE_MODE_COL, // Сравнение по номеру колонки
CELL_COMPARE_MODE_ROW, // Сравнение по номеру строки
CELL_COMPARE_MODE_ROW_COL, // Сравнение по строке и колонке
};
//+------------------------------------------------------------------+
//| Функции |
//+------------------------------------------------------------------+
//--- Возвращает тип объекта как строку
string TypeDescription(const ENUM_OBJECT_TYPE type)
{
string array[];
int total=StringSplit(EnumToString(type),StringGetCharacter("_",0),array);
string result="";
for(int i=2;i<total;i++)
{
array[i]+=" ";
array[i].Lower();
array[i].SetChar(0,ushort(array[i].GetChar(0)-0x20));
result+=array[i];
}
result.TrimLeft();
result.TrimRight();
return result;
}
//+------------------------------------------------------------------+
//| Классы |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс связанного списка объектов |
//+------------------------------------------------------------------+
class CListObj : public CList
{
protected:
ENUM_OBJECT_TYPE m_element_type; // Тип создаваемого объекта в CreateElement()
public:
//--- Виртуальный метод (1) загрузки списка из файла, (2) создания элемента списка
virtual bool Load(const int file_handle);
virtual CObject *CreateElement(void);
};
//+------------------------------------------------------------------+
//| Загрузка списка из файла |
//+------------------------------------------------------------------+
bool CListObj::Load(const int file_handle)
{
//--- Переменные
CObject *node;
bool result=true;
//--- Проверяем хэндл
if(file_handle==INVALID_HANDLE)
return(false);
//--- Загрузка и проверка маркера начала списка - 0xFFFFFFFFFFFFFFFF
if(::FileReadLong(file_handle)!=MARKER_START_DATA)
return(false);
//--- Загрузка и проверка типа списка
if(::FileReadInteger(file_handle,INT_VALUE)!=Type())
return(false);
//--- Чтение размера списка (количество объектов)
uint num=::FileReadInteger(file_handle,INT_VALUE);
//--- Последовательно заново создаём элементы списка с помощью вызова метода Load() объектов node
this.Clear();
for(uint i=0; i<num; i++)
{
//--- Читаем и проверяем маркер начала данных объекта - 0xFFFFFFFFFFFFFFFF
if(::FileReadLong(file_handle)!=MARKER_START_DATA)
return false;
//--- Читаем тип объекта
this.m_element_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE);
node=this.CreateElement();
if(node==NULL)
return false;
this.Add(node);
//--- Сейчас файловый указатель смещён относительно начала маркера объекта на 12 байт (8 - маркер, 4 - тип)
//--- Поставим указатель на начало данных объекта и загрузим свойства объекта из файла методом Load() элемента node.
if(!::FileSeek(file_handle,-12,SEEK_CUR))
return false;
result &=node.Load(file_handle);
}
//--- Результат
return result;
}
//+------------------------------------------------------------------+
//| Метод создания элемента списка |
//+------------------------------------------------------------------+
CObject *CListObj::CreateElement(void)
{
//--- В зависимости от типа объекта в m_element_type, создаём новый объект
switch(this.m_element_type)
{
case OBJECT_TYPE_TABLE_CELL : return new CTableCell();
case OBJECT_TYPE_TABLE_ROW : return new CTableRow();
case OBJECT_TYPE_TABLE_MODEL : return new CTableModel();
default : return NULL;
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс ячейки таблицы |
//+------------------------------------------------------------------+
class CTableCell : public CObject
{
protected:
//--- Объединение для хранения значений ячейки (double, long, string)
union DataType
{
protected:
double double_value;
long long_value;
ushort ushort_value[MAX_STRING_LENGTH];
public:
//--- Установка значений
void SetValueD(const double value) { this.double_value=value; }
void SetValueL(const long value) { this.long_value=value; }
void SetValueS(const string value) { ::StringToShortArray(value,ushort_value); }
//--- Возврат значений
double ValueD(void) const { return this.double_value; }
long ValueL(void) const { return this.long_value; }
string ValueS(void) const
{
string res=::ShortArrayToString(this.ushort_value);
res.TrimLeft();
res.TrimRight();
return res;
}
};
//--- Переменные
DataType m_datatype_value; // Значение
ENUM_DATATYPE m_datatype; // Тип данных
CObject *m_object; // Объект в ячейке
ENUM_OBJECT_TYPE m_object_type; // Тип объекта в ячейке
int m_row; // Номер строки
int m_col; // Номер столбца
int m_digits; // Точность представления данных
uint m_time_flags; // Флаги отображения даты/времени
bool m_color_flag; // Флаг отображения наименования цвета
bool m_editable; // Флаг редактируемой ячейки
public:
//--- Возврат координат и свойств ячейки
uint Row(void) const { return this.m_row; }
uint Col(void) const { return this.m_col; }
ENUM_DATATYPE Datatype(void) const { return this.m_datatype; }
int Digits(void) const { return this.m_digits; }
uint DatetimeFlags(void) const { return this.m_time_flags; }
bool ColorNameFlag(void) const { return this.m_color_flag; }
bool IsEditable(void) const { return this.m_editable; }
//--- Возвращает (1) double, (2) long, (3) string значение
double ValueD(void) const { return this.m_datatype_value.ValueD(); }
long ValueL(void) const { return this.m_datatype_value.ValueL(); }
string ValueS(void) const { return this.m_datatype_value.ValueS(); }
//--- Возвращает значение в виде форматированной строки
string Value(void) const
{
switch(this.m_datatype)
{
case TYPE_DOUBLE : return(::DoubleToString(this.ValueD(),this.Digits()));
case TYPE_LONG : return(::IntegerToString(this.ValueL()));
case TYPE_DATETIME: return(::TimeToString(this.ValueL(),this.m_time_flags));
case TYPE_COLOR : return(::ColorToString((color)this.ValueL(),this.m_color_flag));
default : return this.ValueS();
}
}
string DatatypeDescription(void) const
{
string type=::StringSubstr(::EnumToString(this.m_datatype),5);
type.Lower();
return type;
}
//--- Установка значений переменных
void SetRow(const uint row) { this.m_row=(int)row; }
void SetCol(const uint col) { this.m_col=(int)col; }
void SetDatatype(const ENUM_DATATYPE datatype) { this.m_datatype=datatype; }
void SetDigits(const int digits) { this.m_digits=digits; }
void SetDatetimeFlags(const uint flags) { this.m_time_flags=flags; }
void SetColorNameFlag(const bool flag) { this.m_color_flag=flag; }
void SetEditable(const bool flag) { this.m_editable=flag; }
void SetPositionInTable(const uint row,const uint col)
{
this.SetRow(row);
this.SetCol(col);
}
//--- Назначает объект в ячейку
void AssignObject(CObject *object)
{
if(object==NULL)
{
::PrintFormat("%s: Error. Empty object passed",__FUNCTION__);
return;
}
this.m_object=object;
this.m_object_type=(ENUM_OBJECT_TYPE)object.Type();
}
//--- Снимает назначение объекта
void UnassignObject(void)
{
this.m_object=NULL;
this.m_object_type=-1;
}
//--- Устанавливает double-значение
void SetValue(const double value)
{
this.m_datatype=TYPE_DOUBLE;
if(this.m_editable)
this.m_datatype_value.SetValueD(value);
}
//--- Устанавливает long-значение
void SetValue(const long value)
{
this.m_datatype=TYPE_LONG;
if(this.m_editable)
this.m_datatype_value.SetValueL(value);
}
//--- Устанавливает datetime-значение
void SetValue(const datetime value)
{
this.m_datatype=TYPE_DATETIME;
if(this.m_editable)
this.m_datatype_value.SetValueL(value);
}
//--- Устанавливает color-значение
void SetValue(const color value)
{
this.m_datatype=TYPE_COLOR;
if(this.m_editable)
this.m_datatype_value.SetValueL(value);
}
//--- Устанавливает string-значение
void SetValue(const string value)
{
this.m_datatype=TYPE_STRING;
if(this.m_editable)
this.m_datatype_value.SetValueS(value);
}
//--- Очищает данные
void ClearData(void)
{
if(this.Datatype()==TYPE_STRING)
this.SetValue("");
else
this.SetValue(0.0);
}
//--- (1) Возвращает, (2) выводит в журнал описание объекта
string Description(void);
void Print(void);
//--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта
virtual int Compare(const CObject *node,const int mode=0) const;
virtual bool Save(const int file_handle);
virtual bool Load(const int file_handle);
virtual int Type(void) const { return(OBJECT_TYPE_TABLE_CELL);}
//--- Конструкторы/деструктор
CTableCell(void) : m_row(0), m_col(0), m_datatype(-1), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1)
{
this.m_datatype_value.SetValueD(0);
}
//--- Принимает double-значение
CTableCell(const uint row,const uint col,const double value,const int digits) :
m_row((int)row), m_col((int)col), m_datatype(TYPE_DOUBLE), m_digits(digits), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1)
{
this.m_datatype_value.SetValueD(value);
}
//--- Принимает long-значение
CTableCell(const uint row,const uint col,const long value) :
m_row((int)row), m_col((int)col), m_datatype(TYPE_LONG), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1)
{
this.m_datatype_value.SetValueL(value);
}
//--- Принимает datetime-значение
CTableCell(const uint row,const uint col,const datetime value,const uint time_flags) :
m_row((int)row), m_col((int)col), m_datatype(TYPE_DATETIME), m_digits(0), m_time_flags(time_flags), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1)
{
this.m_datatype_value.SetValueL(value);
}
//--- Принимает color-значение
CTableCell(const uint row,const uint col,const color value,const bool color_names_flag) :
m_row((int)row), m_col((int)col), m_datatype(TYPE_COLOR), m_digits(0), m_time_flags(0), m_color_flag(color_names_flag), m_editable(true), m_object(NULL), m_object_type(-1)
{
this.m_datatype_value.SetValueL(value);
}
//--- Принимает string-значение
CTableCell(const uint row,const uint col,const string value) :
m_row((int)row), m_col((int)col), m_datatype(TYPE_STRING), m_digits(0), m_time_flags(0), m_color_flag(false), m_editable(true), m_object(NULL), m_object_type(-1)
{
this.m_datatype_value.SetValueS(value);
}
~CTableCell(void) {}
};
//+------------------------------------------------------------------+
//| Сравнение двух объектов |
//+------------------------------------------------------------------+
int CTableCell::Compare(const CObject *node,const int mode=0) const
{
const CTableCell *obj=node;
switch(mode)
{
case CELL_COMPARE_MODE_COL : return(this.Col()>obj.Col() ? 1 : this.Col()<obj.Col() ? -1 : 0);
case CELL_COMPARE_MODE_ROW : return(this.Row()>obj.Row() ? 1 : this.Row()<obj.Row() ? -1 : 0);
//---CELL_COMPARE_MODE_ROW_COL
default : return
(
this.Row()>obj.Row() ? 1 : this.Row()<obj.Row() ? -1 :
this.Col()>obj.Col() ? 1 : this.Col()<obj.Col() ? -1 : 0
);
}
}
//+------------------------------------------------------------------+
//| Сохранение в файл |
//+------------------------------------------------------------------+
bool CTableCell::Save(const int file_handle)
{
//--- Проверяем хэндл
if(file_handle==INVALID_HANDLE)
return(false);
//--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF
if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long))
return(false);
//--- Сохраняем тип объекта
if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем тип данных
if(::FileWriteInteger(file_handle,this.m_datatype,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем тип объекта в ячейке
if(::FileWriteInteger(file_handle,this.m_object_type,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем номер строки
if(::FileWriteInteger(file_handle,this.m_row,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем номер столбца
if(::FileWriteInteger(file_handle,this.m_col,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем точность представления данных
if(::FileWriteInteger(file_handle,this.m_digits,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем флаги отображения даты/времени
if(::FileWriteInteger(file_handle,this.m_time_flags,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем флаг отображения наименования цвета
if(::FileWriteInteger(file_handle,this.m_color_flag,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем флаг редактируемой ячейки
if(::FileWriteInteger(file_handle,this.m_editable,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем значение
if(::FileWriteStruct(file_handle,this.m_datatype_value)!=sizeof(this.m_datatype_value))
return(false);
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Загрузка из файла |
//+------------------------------------------------------------------+
bool CTableCell::Load(const int file_handle)
{
//--- Проверяем хэндл
if(file_handle==INVALID_HANDLE)
return(false);
//--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF
if(::FileReadLong(file_handle)!=MARKER_START_DATA)
return(false);
//--- Загружаем тип объекта
if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type())
return(false);
//--- Загружаем тип данных
this.m_datatype=(ENUM_DATATYPE)::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем тип объекта в ячейке
this.m_object_type=(ENUM_OBJECT_TYPE)::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем номер строки
this.m_row=::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем номер столбца
this.m_col=::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем точность представления данных
this.m_digits=::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем флаги отображения даты/времени
this.m_time_flags=::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем флаг отображения наименования цвета
this.m_color_flag=::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем флаг редактируемой ячейки
this.m_editable=::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем значение
if(::FileReadStruct(file_handle,this.m_datatype_value)!=sizeof(this.m_datatype_value))
return(false);
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Возвращает описание объекта |
//+------------------------------------------------------------------+
string CTableCell::Description(void)
{
return(::StringFormat("%s: Row %u, Col %u, %s <%s>Value: %s",
TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.Row(),this.Col(),
(this.m_editable ? "Editable" : "Uneditable"),this.DatatypeDescription(),this.Value()));
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание объекта |
//+------------------------------------------------------------------+
void CTableCell::Print(void)
{
::Print(this.Description());
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс строки таблицы |
//+------------------------------------------------------------------+
class CTableRow : public CObject
{
protected:
CTableCell m_cell_tmp; // Объект ячейки для поиска в списке
CListObj m_list_cells; // Список ячеек
uint m_index; // Индекс строки
//--- Добавляет указанную ячейку в конец списка
bool AddNewCell(CTableCell *cell);
public:
//--- (1) Устанавливает, (2) возвращает индекс строки
void SetIndex(const uint index) { this.m_index=index; }
uint Index(void) const { return this.m_index; }
//--- Устанавливает позиции строки и колонки всем ячейкам
void CellsPositionUpdate(void);
//--- Создаёт новую ячейку и добавляет в конец списка
CTableCell *CreateNewCell(const double value);
CTableCell *CreateNewCell(const long value);
CTableCell *CreateNewCell(const datetime value);
CTableCell *CreateNewCell(const color value);
CTableCell *CreateNewCell(const string value);
//--- Возвращает (1) ячейку по индексу, (2) количество ячеек
CTableCell *GetCell(const uint index) { return this.m_list_cells.GetNodeAtIndex(index); }
uint CellsTotal(void) const { return this.m_list_cells.Total(); }
//--- Устанавливает значение в указанную ячейку
void CellSetValue(const uint index,const double value);
void CellSetValue(const uint index,const long value);
void CellSetValue(const uint index,const datetime value);
void CellSetValue(const uint index,const color value);
void CellSetValue(const uint index,const string value);
//--- (1) назначает в ячейку, (2) снимает с ячейки назначенный объект
void CellAssignObject(const uint index,CObject *object);
void CellUnassignObject(const uint index);
//--- (1) Удаляет (2) перемещает ячейку
bool CellDelete(const uint index);
bool CellMoveTo(const uint cell_index, const uint index_to);
//--- Обнуляет данные ячеек строки
void ClearData(void);
//--- (1) Возвращает, (2) выводит в журнал описание объекта
string Description(void);
void Print(const bool detail, const bool as_table=false, const int cell_width=10);
//--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта
virtual int Compare(const CObject *node,const int mode=0) const;
virtual bool Save(const int file_handle);
virtual bool Load(const int file_handle);
virtual int Type(void) const { return(OBJECT_TYPE_TABLE_ROW); }
//--- Конструкторы/деструктор
CTableRow(void) : m_index(0) {}
CTableRow(const uint index) : m_index(index) {}
~CTableRow(void){}
};
//+------------------------------------------------------------------+
//| Сравнение двух объектов |
//+------------------------------------------------------------------+
int CTableRow::Compare(const CObject *node,const int mode=0) const
{
const CTableRow *obj=node;
return(this.Index()>obj.Index() ? 1 : this.Index()<obj.Index() ? -1 : 0);
}
//+------------------------------------------------------------------+
//| Создаёт новую double-ячейку и добавляет в конец списка |
//+------------------------------------------------------------------+
CTableCell *CTableRow::CreateNewCell(const double value)
{
//--- Создаём новый объект ячейки, хранящей значение с типом double
CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,2);
if(cell==NULL)
{
::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal());
return NULL;
}
//--- Добавляем созданную ячейку в конец списка
if(!this.AddNewCell(cell))
{
delete cell;
return NULL;
}
//--- Возвращаем указатель на объект
return cell;
}
//+------------------------------------------------------------------+
//| Создаёт новую long-ячейку и добавляет в конец списка |
//+------------------------------------------------------------------+
CTableCell *CTableRow::CreateNewCell(const long value)
{
//--- Создаём новый объект ячейки, хранящей значение с типом long
CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value);
if(cell==NULL)
{
::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal());
return NULL;
}
//--- Добавляем созданную ячейку в конец списка
if(!this.AddNewCell(cell))
{
delete cell;
return NULL;
}
//--- Возвращаем указатель на объект
return cell;
}
//+------------------------------------------------------------------+
//| Создаёт новую datetime-ячейку и добавляет в конец списка |
//+------------------------------------------------------------------+
CTableCell *CTableRow::CreateNewCell(const datetime value)
{
//--- Создаём новый объект ячейки, хранящей значение с типом datetime
CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,TIME_DATE|TIME_MINUTES|TIME_SECONDS);
if(cell==NULL)
{
::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal());
return NULL;
}
//--- Добавляем созданную ячейку в конец списка
if(!this.AddNewCell(cell))
{
delete cell;
return NULL;
}
//--- Возвращаем указатель на объект
return cell;
}
//+------------------------------------------------------------------+
//| Создаёт новую color-ячейку и добавляет в конец списка |
//+------------------------------------------------------------------+
CTableCell *CTableRow::CreateNewCell(const color value)
{
//--- Создаём новый объект ячейки, хранящей значение с типом color
CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value,true);
if(cell==NULL)
{
::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal());
return NULL;
}
//--- Добавляем созданную ячейку в конец списка
if(!this.AddNewCell(cell))
{
delete cell;
return NULL;
}
//--- Возвращаем указатель на объект
return cell;
}
//+------------------------------------------------------------------+
//| Создаёт новую string-ячейку и добавляет в конец списка |
//+------------------------------------------------------------------+
CTableCell *CTableRow::CreateNewCell(const string value)
{
//--- Создаём новый объект ячейки, хранящей значение с типом string
CTableCell *cell=new CTableCell(this.m_index,this.CellsTotal(),value);
if(cell==NULL)
{
::PrintFormat("%s: Error. Failed to create new cell in row %u at position %u",__FUNCTION__, this.m_index, this.CellsTotal());
return NULL;
}
//--- Добавляем созданную ячейку в конец списка
if(!this.AddNewCell(cell))
{
delete cell;
return NULL;
}
//--- Возвращаем указатель на объект
return cell;
}
//+------------------------------------------------------------------+
//| Добавляет ячейку в конец списка |
//+------------------------------------------------------------------+
bool CTableRow::AddNewCell(CTableCell *cell)
{
//--- Если передан пустой объект - сообщаем и возвращаем false
if(cell==NULL)
{
::PrintFormat("%s: Error. Empty CTableCell object passed",__FUNCTION__);
return false;
}
//--- Устанавливаем индекс ячейки в списке и добавляем созданную ячейку в конец списка
cell.SetPositionInTable(this.m_index,this.CellsTotal());
if(this.m_list_cells.Add(cell)==WRONG_VALUE)
{
::PrintFormat("%s: Error. Failed to add cell (%u,%u) to list",__FUNCTION__,this.m_index,this.CellsTotal());
return false;
}
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает double-значение в указанную ячейку |
//+------------------------------------------------------------------+
void CTableRow::CellSetValue(const uint index,const double value)
{
//--- Получаем из списка нужную ячейку и записываем в неё новое значение
CTableCell *cell=this.GetCell(index);
if(cell!=NULL)
cell.SetValue(value);
}
//+------------------------------------------------------------------+
//| Устанавливает long-значение в указанную ячейку |
//+------------------------------------------------------------------+
void CTableRow::CellSetValue(const uint index,const long value)
{
//--- Получаем из списка нужную ячейку и записываем в неё новое значение
CTableCell *cell=this.GetCell(index);
if(cell!=NULL)
cell.SetValue(value);
}
//+------------------------------------------------------------------+
//| Устанавливает datetime-значение в указанную ячейку |
//+------------------------------------------------------------------+
void CTableRow::CellSetValue(const uint index,const datetime value)
{
//--- Получаем из списка нужную ячейку и записываем в неё новое значение
CTableCell *cell=this.GetCell(index);
if(cell!=NULL)
cell.SetValue(value);
}
//+------------------------------------------------------------------+
//| Устанавливает color-значение в указанную ячейку |
//+------------------------------------------------------------------+
void CTableRow::CellSetValue(const uint index,const color value)
{
//--- Получаем из списка нужную ячейку и записываем в неё новое значение
CTableCell *cell=this.GetCell(index);
if(cell!=NULL)
cell.SetValue(value);
}
//+------------------------------------------------------------------+
//| Устанавливает string-значение в указанную ячейку |
//+------------------------------------------------------------------+
void CTableRow::CellSetValue(const uint index,const string value)
{
//--- Получаем из списка нужную ячейку и записываем в неё новое значение
CTableCell *cell=this.GetCell(index);
if(cell!=NULL)
cell.SetValue(value);
}
//+------------------------------------------------------------------+
//| Назначает в ячейку объект |
//+------------------------------------------------------------------+
void CTableRow::CellAssignObject(const uint index,CObject *object)
{
//--- Получаем из списка нужную ячейку и записываем в неё указатель на объект
CTableCell *cell=this.GetCell(index);
if(cell!=NULL)
cell.AssignObject(object);
}
//+------------------------------------------------------------------+
//| Отменяет для ячейки назначенный объект |
//+------------------------------------------------------------------+
void CTableRow::CellUnassignObject(const uint index)
{
//--- Получаем из списка нужную ячейку и отменяем в ней указатель на объект и его тип
CTableCell *cell=this.GetCell(index);
if(cell!=NULL)
cell.UnassignObject();
}
//+------------------------------------------------------------------+
//| Удаляет ячейку |
//+------------------------------------------------------------------+
bool CTableRow::CellDelete(const uint index)
{
//--- Удаляем ячейку в списке по индексу
if(!this.m_list_cells.Delete(index))
return false;
//--- Обновляем индексы для оставшихся ячеек в списке
this.CellsPositionUpdate();
return true;
}
//+------------------------------------------------------------------+
//| Перемещает ячейку на указанную позицию |
//+------------------------------------------------------------------+
bool CTableRow::CellMoveTo(const uint cell_index,const uint index_to)
{
//--- Получаем нужную ячейку по индексу в списке, делая её текущей
CTableCell *cell=this.GetCell(cell_index);
//--- Перемещаем текущую ячейку на указанную позицию в списке
if(cell==NULL || !this.m_list_cells.MoveToIndex(index_to))
return false;
//--- Обновляем индексы всех ячеек в списке
this.CellsPositionUpdate();
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает позиции строки и колонки всем ячейкам |
//+------------------------------------------------------------------+
void CTableRow::CellsPositionUpdate(void)
{
//--- В цикле по всем ячейкам в списке
for(int i=0;i<this.m_list_cells.Total();i++)
{
//--- получаем очередную ячейку и устанавливаем в неё индексы строки и столбца
CTableCell *cell=this.GetCell(i);
if(cell!=NULL)
cell.SetPositionInTable(this.Index(),this.m_list_cells.IndexOf(cell));
}
}
//+------------------------------------------------------------------+
//| Обнуляет данные ячеек строки |
//+------------------------------------------------------------------+
void CTableRow::ClearData(void)
{
//--- В цикле по всем ячейкам в списке
for(uint i=0;i<this.CellsTotal();i++)
{
//--- получаем очередную ячейку и устанавливаем в неё пустое значение
CTableCell *cell=this.GetCell(i);
if(cell!=NULL)
cell.ClearData();
}
}
//+------------------------------------------------------------------+
//| Возвращает описание объекта |
//+------------------------------------------------------------------+
string CTableRow::Description(void)
{
return(::StringFormat("%s: Position %u, Cells total: %u",
TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.Index(),this.CellsTotal()));
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание объекта |
//+------------------------------------------------------------------+
void CTableRow::Print(const bool detail, const bool as_table=false, const int cell_width=10)
{
//--- Количество ячеек
int total=(int)this.CellsTotal();
//--- Если вывод в табличном виде
string res="";
if(as_table)
{
//--- создаём строку таблицы из значений всех ячеек
string head=" Row "+(string)this.Index();
string res=::StringFormat("|%-*s |",cell_width,head);
for(int i=0;i<total;i++)
{
CTableCell *cell=this.GetCell(i);
if(cell==NULL)
continue;
res+=::StringFormat("%*s |",cell_width,cell.Value());
}
//--- Выводим строку в журнал
::Print(res);
return;
}
//--- Выводим заголовок в виде описания строки
::Print(this.Description()+(detail ? ":" : ""));
//--- Если детализированное описание
if(detail)
{
//--- Вывод не в табличном виде
//--- В цикле по спискук ячеек строки
for(int i=0; i<total; i++)
{
//--- получаем текущую ячейку и добавляем в итоговую строку её описание
CTableCell *cell=this.GetCell(i);
if(cell!=NULL)
res+=" "+cell.Description()+(i<total-1 ? "\n" : "");
}
//--- Выводим в журнал созданную в цикле строку
::Print(res);
}
}
//+------------------------------------------------------------------+
//| Сохранение в файл |
//+------------------------------------------------------------------+
bool CTableRow::Save(const int file_handle)
{
//--- Проверяем хэндл
if(file_handle==INVALID_HANDLE)
return(false);
//--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF
if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long))
return(false);
//--- Сохраняем тип объекта
if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем индекс
if(::FileWriteInteger(file_handle,this.m_index,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем список ячеек
if(!this.m_list_cells.Save(file_handle))
return(false);
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//| Загрузка из файла |
//+------------------------------------------------------------------+
bool CTableRow::Load(const int file_handle)
{
//--- Проверяем хэндл
if(file_handle==INVALID_HANDLE)
return(false);
//--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF
if(::FileReadLong(file_handle)!=MARKER_START_DATA)
return(false);
//--- Загружаем тип объекта
if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type())
return(false);
//--- Загружаем индекс
this.m_index=::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем список ячеек
if(!this.m_list_cells.Load(file_handle))
return(false);
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс модели таблицы |
//+------------------------------------------------------------------+
class CTableModel : public CObject
{
protected:
CTableRow m_row_tmp; // Объект строки для поиска в списке
CListObj m_list_rows; // Список строк таблицы
//--- Создаёт модель таблицы из двумерного массива
template<typename T>
void CreateTableModel(T &array[][]);
//--- Возвращает корректный тип данных
ENUM_DATATYPE GetCorrectDatatype(string type_name)
{
return
(
//--- Целочисленное значение
type_name=="bool" || type_name=="char" || type_name=="uchar" ||
type_name=="short"|| type_name=="ushort" || type_name=="int" ||
type_name=="uint" || type_name=="long" || type_name=="ulong" ? TYPE_LONG :
//--- Вещественное значение
type_name=="float"|| type_name=="double" ? TYPE_DOUBLE :
//--- Значение даты/времени
type_name=="datetime" ? TYPE_DATETIME :
//--- Значение цвета
type_name=="color" ? TYPE_COLOR :
/*--- Строковое значение */ TYPE_STRING );
}
//--- Создаёт и добавляет новую пустую строку в конец списка
CTableRow *CreateNewEmptyRow(void);
//--- Добавляет строку в конец списка
bool AddNewRow(CTableRow *row);
//--- Устанавливает позиции строки и колонки всем ячейкам таблицы
void CellsPositionUpdate(void);
public:
//--- Возвращает (1) ячейку, (2) строку по индексу, количество (3) строк, ячеек (4) в указанной строке, (5) в таблице
CTableCell *GetCell(const uint row, const uint col);
CTableRow *GetRow(const uint index) { return this.m_list_rows.GetNodeAtIndex(index); }
uint RowsTotal(void) const { return this.m_list_rows.Total(); }
uint CellsInRow(const uint index);
uint CellsTotal(void);
//--- Устанавливает (1) значение, (2) точность, (3) флаги отображения времени, (4) флаг отображения имён цветов в указанную ячейку
template<typename T>
void CellSetValue(const uint row, const uint col, const T value);
void CellSetDigits(const uint row, const uint col, const int digits);
void CellSetTimeFlags(const uint row, const uint col, const uint flags);
void CellSetColorNamesFlag(const uint row, const uint col, const bool flag);
//--- (1) Назначает, (2) отменяет объект в ячейке
void CellAssignObject(const uint row, const uint col,CObject *object);
void CellUnassignObject(const uint row, const uint col);
//--- (1) Удаляет (2) перемещает ячейку
bool CellDelete(const uint row, const uint col);
bool CellMoveTo(const uint row, const uint cell_index, const uint index_to);
//--- (1) Возвращает, (2) выводит в журнал описание ячейки, (3) назначенный в ячейку объект
string CellDescription(const uint row, const uint col);
void CellPrint(const uint row, const uint col);
CObject *CellGetObject(const uint row, const uint col);
public:
//--- Создаёт новую строку и (1) добавляет в конец списка, (2) вставляет в указанную позицию списка
CTableRow *RowAddNew(void);
CTableRow *RowInsertNewTo(const uint index_to);
//--- (1) Удаляет (2) перемещает строку, (3) очищает данные строки
bool RowDelete(const uint index);
bool RowMoveTo(const uint row_index, const uint index_to);
void RowResetData(const uint index);
//--- (1) Возвращает, (2) выводит в журнал описание строки
string RowDescription(const uint index);
void RowPrint(const uint index,const bool detail);
//--- (1) Удаляет (2) перемещает столбец, (3) очищает данные столбца
bool ColumnDelete(const uint index);
bool ColumnMoveTo(const uint row_index, const uint index_to);
void ColumnResetData(const uint index);
//--- (1) Возвращает, (2) выводит в журнал описание таблицы
string Description(void);
void Print(const bool detail);
void PrintTable(const int cell_width=10);
//--- (1) Очищает данные, (2) уничтожает модель
void ClearData(void);
void Destroy(void);
//--- Виртуальные методы (1) сравнения, (2) сохранения в файл, (3) загрузки из файла, (4) тип объекта
virtual int Compare(const CObject *node,const int mode=0) const;
virtual bool Save(const int file_handle);
virtual bool Load(const int file_handle);
virtual int Type(void) const { return(OBJECT_TYPE_TABLE_MODEL); }
//--- Конструкторы/деструктор
CTableModel(void){}
CTableModel(double &array[][]) { this.CreateTableModel(array); }
CTableModel(long &array[][]) { this.CreateTableModel(array); }
CTableModel(datetime &array[][]) { this.CreateTableModel(array); }
CTableModel(color &array[][]) { this.CreateTableModel(array); }
CTableModel(string &array[][]) { this.CreateTableModel(array); }
~CTableModel(void){}
};
//+------------------------------------------------------------------+
//| Создаёт модель таблицы из двумерного массива |
//+------------------------------------------------------------------+
template<typename T>
void CTableModel::CreateTableModel(T &array[][])
{
//--- Получаем из свойств массива количество строк и столбцов таблицы
int rows_total=::ArrayRange(array,0);
int cols_total=::ArrayRange(array,1);
//--- В цикле по индексам строк
for(int r=0; r<rows_total; r++)
{
//--- создаём новую пустую строку и добавляем её в конец списка строк
CTableRow *row=this.CreateNewEmptyRow();
//--- Если строка создана и добавлена в список,
if(row!=NULL)
{
//--- В цикле по количеству ячеек в строке
//--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки
for(int c=0; c<cols_total; c++)
row.CreateNewCell(array[r][c]);
}
}
}
//+------------------------------------------------------------------+
//| Создаёт новую пустую строку и добавляет в конец списка |
//+------------------------------------------------------------------+
CTableRow *CTableModel::CreateNewEmptyRow(void)
{
//--- Создаём новый объект строки
CTableRow *row=new CTableRow(this.m_list_rows.Total());
if(row==NULL)
{
::PrintFormat("%s: Error. Failed to create new row at position %u",__FUNCTION__, this.m_list_rows.Total());
return NULL;
}
//--- Если строку не удалось добавить в список - удаляем созданный новый объект и возвращаем NULL
if(!this.AddNewRow(row))
{
delete row;
return NULL;
}
//--- Успешно - возвращаем указатель на созданный объект
return row;
}
//+------------------------------------------------------------------+
//| Добавляет строку в конец списка |
//+------------------------------------------------------------------+
bool CTableModel::AddNewRow(CTableRow *row)
{
//--- Если передан пустой объект - сообщаем об этом и возвращаем false
if(row==NULL)
{
::PrintFormat("%s: Error. Empty CTableRow object passed",__FUNCTION__);
return false;
}
//--- Устанавливаем строке её индекс в списке и добавляем её в конец списка
row.SetIndex(this.RowsTotal());
if(this.m_list_rows.Add(row)==WRONG_VALUE)
{
::PrintFormat("%s: Error. Failed to add row (%u) to list",__FUNCTION__,row.Index());
return false;
}
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//| Создаёт новую строку и добавляет в конец списка |
//+------------------------------------------------------------------+
CTableRow *CTableModel::RowAddNew(void)
{
//--- Создаём новую пустую строку и добавляем её в конец списка строк
CTableRow *row=this.CreateNewEmptyRow();
if(row==NULL)
return NULL;
//--- Создаём ячейки по количеству ячеек первой строки
for(uint i=0;i<this.CellsInRow(0);i++)
row.CreateNewCell(0.0);
row.ClearData();
//--- Успешно - возвращаем указатель на созданный объект
return row;
}
//+------------------------------------------------------------------+
//| Создаёт и добавляет новую строку в указанную позицию списка |
//+------------------------------------------------------------------+
CTableRow *CTableModel::RowInsertNewTo(const uint index_to)
{
//--- Создаём новую пустую строку и добавляем её в конец списка строк
CTableRow *row=this.CreateNewEmptyRow();
if(row==NULL)
return NULL;
//--- Создаём ячейки по количеству ячеек первой строки
for(uint i=0;i<this.CellsInRow(0);i++)
row.CreateNewCell(0.0);
row.ClearData();
//--- Смещаем строку на позицию index_to
this.RowMoveTo(this.m_list_rows.IndexOf(row),index_to);
//--- Успешно - возвращаем указатель на созданный объект
return row;
}
//+------------------------------------------------------------------+
//| Устанавливает значение в указанную ячейку |
//+------------------------------------------------------------------+
template<typename T>
void CTableModel::CellSetValue(const uint row,const uint col,const T value)
{
//--- Получаем ячейку по индексам строки и столбца
CTableCell *cell=this.GetCell(row,col);
if(cell==NULL)
return;
//--- Получаем корректный тип устанавливаемых данных (double, long, datetime, color, string)
ENUM_DATATYPE type=this.GetCorrectDatatype(typename(T));
//--- В зависимости от типа данных вызываем соответствующий типу данных
//--- метод ячейки для установки значения, явно указывая требуемый тип
switch(type)
{
case TYPE_DOUBLE : cell.SetValue((double)value); break;
case TYPE_LONG : cell.SetValue((long)value); break;
case TYPE_DATETIME: cell.SetValue((datetime)value); break;
case TYPE_COLOR : cell.SetValue((color)value); break;
case TYPE_STRING : cell.SetValue((string)value); break;
default : break;
}
}
//+------------------------------------------------------------------+
//| Устанавливает точность отображения данных в указанную ячейку |
//+------------------------------------------------------------------+
void CTableModel::CellSetDigits(const uint row,const uint col,const int digits)
{
//--- Получаем ячейку по индексам строки и столбца и
//--- вызываем её соответствующий метод для установки значения
CTableCell *cell=this.GetCell(row,col);
if(cell!=NULL)
cell.SetDigits(digits);
}
//+------------------------------------------------------------------+
//| Устанавливает флаги отображения времени в указанную ячейку |
//+------------------------------------------------------------------+
void CTableModel::CellSetTimeFlags(const uint row,const uint col,const uint flags)
{
//--- Получаем ячейку по индексам строки и столбца и
//--- вызываем её соответствующий метод для установки значения
CTableCell *cell=this.GetCell(row,col);
if(cell!=NULL)
cell.SetDatetimeFlags(flags);
}
//+------------------------------------------------------------------+
//| Устанавливает флаг отображения имён цветов в указанную ячейку |
//+------------------------------------------------------------------+
void CTableModel::CellSetColorNamesFlag(const uint row,const uint col,const bool flag)
{
//--- Получаем ячейку по индексам строки и столбца и
//--- вызываем её соответствующий метод для установки значения
CTableCell *cell=this.GetCell(row,col);
if(cell!=NULL)
cell.SetColorNameFlag(flag);
}
//+------------------------------------------------------------------+
//| Назначает объект в ячейку |
//+------------------------------------------------------------------+
void CTableModel::CellAssignObject(const uint row,const uint col,CObject *object)
{
//--- Получаем ячейку по индексам строки и столбца и
//--- вызываем её соответствующий метод для установки значения
CTableCell *cell=this.GetCell(row,col);
if(cell!=NULL)
cell.AssignObject(object);
}
//+------------------------------------------------------------------+
//| Отменяет назначение объекта в ячейке |
//+------------------------------------------------------------------+
void CTableModel::CellUnassignObject(const uint row,const uint col)
{
//--- Получаем ячейку по индексам строки и столбца и
//--- вызываем её соответствующий метод для установки значения
CTableCell *cell=this.GetCell(row,col);
if(cell!=NULL)
cell.UnassignObject();
}
//+------------------------------------------------------------------+
//| Удаляет ячейку |
//+------------------------------------------------------------------+
bool CTableModel::CellDelete(const uint row,const uint col)
{
//--- Получаем строку по индексу и возвращаем результат удаления ячейки из списка
CTableRow *row_obj=this.GetRow(row);
return(row_obj!=NULL ? row_obj.CellDelete(col) : false);
}
//+------------------------------------------------------------------+
//| Перемещает ячейку |
//+------------------------------------------------------------------+
bool CTableModel::CellMoveTo(const uint row,const uint cell_index,const uint index_to)
{
//--- Получаем строку по индексу и возвращаем результат перемещения ячейки на новую позицию
CTableRow *row_obj=this.GetRow(row);
return(row_obj!=NULL ? row_obj.CellMoveTo(cell_index,index_to) : false);
}
//+------------------------------------------------------------------+
//| Возвращает количество ячеек в указанной строке |
//+------------------------------------------------------------------+
uint CTableModel::CellsInRow(const uint index)
{
CTableRow *row=this.GetRow(index);
return(row!=NULL ? row.CellsTotal() : 0);
}
//+------------------------------------------------------------------+
//| Возвращает количество ячеек в таблице |
//+------------------------------------------------------------------+
uint CTableModel::CellsTotal(void)
{
//--- подсчёт ячеек в цикле по строкам (медленно при большом количестве строк)
uint res=0, total=this.RowsTotal();
for(int i=0; i<(int)total; i++)
{
CTableRow *row=this.GetRow(i);
res+=(row!=NULL ? row.CellsTotal() : 0);
}
return res;
}
//+------------------------------------------------------------------+
//| Возвращает указанную ячейку таблицы |
//+------------------------------------------------------------------+
CTableCell *CTableModel::GetCell(const uint row,const uint col)
{
//--- Получаем строку по индексу row и возвращаем по индексу col ячейку строки
CTableRow *row_obj=this.GetRow(row);
return(row_obj!=NULL ? row_obj.GetCell(col) : NULL);
}
//+------------------------------------------------------------------+
//| Возвращает описание ячейки |
//+------------------------------------------------------------------+
string CTableModel::CellDescription(const uint row,const uint col)
{
CTableCell *cell=this.GetCell(row,col);
return(cell!=NULL ? cell.Description() : "");
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание ячейки |
//+------------------------------------------------------------------+
void CTableModel::CellPrint(const uint row,const uint col)
{
//--- Получаем ячейку по индексу строки и колонки и возвращаем её описание
CTableCell *cell=this.GetCell(row,col);
if(cell!=NULL)
cell.Print();
}
//+------------------------------------------------------------------+
//| Удаляет строку |
//+------------------------------------------------------------------+
bool CTableModel::RowDelete(const uint index)
{
//--- Удаляем строку из списка по индексу
if(!this.m_list_rows.Delete(index))
return false;
//--- После удаления строки необходимо обновить все индексы всех ячеек таблицы
this.CellsPositionUpdate();
return true;
}
//+------------------------------------------------------------------+
//| Перемещает строку на указанную позицию |
//+------------------------------------------------------------------+
bool CTableModel::RowMoveTo(const uint row_index,const uint index_to)
{
//--- Получаем строку по индексу, делая её текущей
CTableRow *row=this.GetRow(row_index);
//--- Перемещаем текущую строку на указанную позицию в списке
if(row==NULL || !this.m_list_rows.MoveToIndex(index_to))
return false;
//--- После перемещения строки необходимо обновить все индексы всех ячеек таблицы
this.CellsPositionUpdate();
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает позиции строки и колонки всем ячейкам |
//+------------------------------------------------------------------+
void CTableModel::CellsPositionUpdate(void)
{
//--- В цикле по списку строк
for(int i=0;i<this.m_list_rows.Total();i++)
{
//--- получаем очередную строку
CTableRow *row=this.GetRow(i);
if(row==NULL)
continue;
//--- устанавливаем строке индекс, найденный методом IndexOf() списка
row.SetIndex(this.m_list_rows.IndexOf(row));
//--- Обновляем индексы позиций ячеек строки
row.CellsPositionUpdate();
}
}
//+------------------------------------------------------------------+
//| Очищает строку (только данные в ячйках) |
//+------------------------------------------------------------------+
void CTableModel::RowResetData(const uint index)
{
//--- Получаем строку из списка и очищаем данные ячеек строки методом ClearData()
CTableRow *row=this.GetRow(index);
if(row!=NULL)
row.ClearData();
}
//+------------------------------------------------------------------+
//| Очищает таблицу (данные всех ячеек) |
//+------------------------------------------------------------------+
void CTableModel::ClearData(void)
{
//--- В цикле по всем строкам таблицы очищаем данные каждой строки
for(uint i=0;i<this.RowsTotal();i++)
this.RowResetData(i);
}
//+------------------------------------------------------------------+
//| Возвращает описание строки |
//+------------------------------------------------------------------+
string CTableModel::RowDescription(const uint index)
{
//--- Получаем строку по индексу и возвращаем её описание
CTableRow *row=this.GetRow(index);
return(row!=NULL ? row.Description() : "");
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание строки |
//+------------------------------------------------------------------+
void CTableModel::RowPrint(const uint index,const bool detail)
{
CTableRow *row=this.GetRow(index);
if(row!=NULL)
row.Print(detail);
}
//+------------------------------------------------------------------+
//| Удаляет столбец |
//+------------------------------------------------------------------+
bool CTableModel::ColumnDelete(const uint index)
{
bool res=true;
for(uint i=0;i<this.RowsTotal();i++)
{
CTableRow *row=this.GetRow(i);
if(row!=NULL)
res &=row.CellDelete(index);
}
return res;
}
//+------------------------------------------------------------------+
//| Перемещает столбец |
//+------------------------------------------------------------------+
bool CTableModel::ColumnMoveTo(const uint col_index,const uint index_to)
{
bool res=true;
for(uint i=0;i<this.RowsTotal();i++)
{
CTableRow *row=this.GetRow(i);
if(row!=NULL)
res &=row.CellMoveTo(col_index,index_to);
}
return res;
}
//+------------------------------------------------------------------+
//| Очищает данные столбца |
//+------------------------------------------------------------------+
void CTableModel::ColumnResetData(const uint index)
{
//--- В цикле по всем строкам таблицы
for(uint i=0;i<this.RowsTotal();i++)
{
//--- получаем из каждой строки ячейку с индексом столбца и очищаем её
CTableCell *cell=this.GetCell(i, index);
if(cell!=NULL)
cell.ClearData();
}
}
//+------------------------------------------------------------------+
//| Возвращает описание объекта |
//+------------------------------------------------------------------+
string CTableModel::Description(void)
{
return(::StringFormat("%s: Rows %u, Cells in row %u, Cells Total %u",
TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.RowsTotal(),this.CellsInRow(0),this.CellsTotal()));
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание объекта |
//+------------------------------------------------------------------+
void CTableModel::Print(const bool detail)
{
//--- Выводим в журнал заголовок
::Print(this.Description()+(detail ? ":" : ""));
//--- Если детализированное описание,
if(detail)
{
//--- В цикле по всем строкам таблицы
for(uint i=0; i<this.RowsTotal(); i++)
{
//--- получаем очередную строку и выводим в журнал её детализированное описание
CTableRow *row=this.GetRow(i);
if(row!=NULL)
row.Print(true,false);
}
}
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание объекта в табличном виде |
//+------------------------------------------------------------------+
void CTableModel::PrintTable(const int cell_width=10)
{
//--- Получаем указатель на первую строку (индекс 0)
CTableRow *row=this.GetRow(0);
if(row==NULL)
return;
//--- По количеству ячеек первой строки таблицы создаём строку заголовка таблицы
uint total=row.CellsTotal();
string head=" n/n";
string res=::StringFormat("|%*s |",cell_width,head);
for(uint i=0;i<total;i++)
{
if(this.GetCell(0, i)==NULL)
continue;
string cell_idx=" Column "+(string)i;
res+=::StringFormat("%*s |",cell_width,cell_idx);
}
//--- Выводим строку заголовка в журнал
::Print(res);
//--- Пройдём в цикле по всем строкам таблицы и распечатаем их в табличном виде
for(uint i=0;i<this.RowsTotal();i++)
{
CTableRow *row=this.GetRow(i);
if(row!=NULL)
row.Print(true,true,cell_width);
}
}
//+------------------------------------------------------------------+
//| Уничтожает модель |
//+------------------------------------------------------------------+
void CTableModel::Destroy(void)
{
//--- Очищаем список строк
m_list_rows.Clear();
}
//+------------------------------------------------------------------+
//| Сохранение в файл |
//+------------------------------------------------------------------+
bool CTableModel::Save(const int file_handle)
{
//--- Проверяем хэндл
if(file_handle==INVALID_HANDLE)
return(false);
//--- Сохраняем маркер начала данных - 0xFFFFFFFFFFFFFFFF
if(::FileWriteLong(file_handle,MARKER_START_DATA)!=sizeof(long))
return(false);
//--- Сохраняем тип объекта
if(::FileWriteInteger(file_handle,this.Type(),INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем список строк
if(!this.m_list_rows.Save(file_handle))
return(false);
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//| Загрузка из файла |
//+------------------------------------------------------------------+
bool CTableModel::Load(const int file_handle)
{
//--- Проверяем хэндл
if(file_handle==INVALID_HANDLE)
return(false);
//--- Загружаем и проверяем маркер начала данных - 0xFFFFFFFFFFFFFFFF
if(::FileReadLong(file_handle)!=MARKER_START_DATA)
return(false);
//--- Загружаем тип объекта
if(::FileReadInteger(file_handle,INT_VALUE)!=this.Type())
return(false);
//--- Загружаем список строк
if(!this.m_list_rows.Load(file_handle))
return(false);
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
#define PRINT_AS_TABLE true // Распечатывать модель как таблицу
2026-03-25 14:07:04 +07:00
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Объявляем и заполняем массив с размерностью 4x4
//--- Тип массива может быть double, long, datetime, color, string
long array[4][4]={{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12},
{13, 14, 15, 16}};
//--- Создаём модель таблицы из вышесозданного long-массива array 4x4
CTableModel *tm=new CTableModel(array);
2026-03-25 14:07:04 +07:00
//--- Если модель не создана - уходим
if(tm==NULL)
return;
//--- Распечатаем модель в табличном виде
Print("The table model has been successfully created:");
tm.PrintTable();
//--- Проверим работу с файлами и функционал модели таблицы
//--- Открываем файл для записи в него данных модели таблицы
int handle=FileOpen(MQLInfoString(MQL_PROGRAM_NAME)+".bin",FILE_READ|FILE_WRITE|FILE_BIN|FILE_COMMON);
if(handle==INVALID_HANDLE)
return;
//--- Сохраним в файл оригинальную созданную таблицу
if(tm.Save(handle))
Print("\nThe table model has been successfully saved to file.");
//--- Теперь вставим в таблицу новую строку в позицию 2
//--- Получим последнюю ячейку созданной строки и сделаем её нередактируемой
//--- Распечатаем в журнале изменённую модель таблицы
if(tm.RowInsertNewTo(2))
{
Print("\nInsert a new row at position 2 and set cell 3 to non-editable");
CTableCell *cell=tm.GetCell(2,3);
if(cell!=NULL)
cell.SetEditable(false);
TableModelPrint(tm);
}
//--- Теперь удалим столбец таблицы с индексом 1 и
//--- распечатаем в журнале полученную модель таблицы
if(tm.ColumnDelete(1))
{
Print("\nRemove column from position 1");
TableModelPrint(tm);
}
//--- При сохранении данных таблицы файловый указатель был смещён на последние записанные данные
//--- Поставим указатель в начало файла, загрузим ранее сохранённую оригинальную таблицу и распечатаем её
if(FileSeek(handle,0,SEEK_SET) && tm.Load(handle))
{
Print("\nLoad the original table view from the file:");
TableModelPrint(tm);
}
//--- Закроем открытый файл и удалим объект модели таблицы
FileClose(handle);
delete tm;
}
//+------------------------------------------------------------------+
//| Распечатывает модель таблицы |
//+------------------------------------------------------------------+
void TableModelPrint(CTableModel *tm)
{
if(PRINT_AS_TABLE)
tm.PrintTable(); // Распечатать модель как таблицу
else
tm.Print(true); // Распечатать детализированные данные таблицы
2026-03-25 14:07:04 +07:00
}
//+------------------------------------------------------------------+