1543 líneas
78 KiB
MQL5
1543 líneas
78 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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 // Распечатывать модель как таблицу
|
|
//+------------------------------------------------------------------+
|
|
//| 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);
|
|
|
|
//--- Если модель не создана - уходим
|
|
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); // Распечатать детализированные данные таблицы
|
|
}
|
|
//+------------------------------------------------------------------+
|