Article-17803-MQL5-MVC-Tabl.../Tables.mqh

2870 行
149 KiB
MQL5

2026-03-30 00:37:05 +07:00
//+------------------------------------------------------------------+
//| Tables.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
//+------------------------------------------------------------------+
//| Включаемые библиотеки |
//+------------------------------------------------------------------+
#include <Arrays\List.mqh>
//--- Форвард-декларация классов
class CTableCell; // Класс ячейки таблицы
class CTableRow; // Класс строки таблицы
class CTableModel; // Класс модели таблицы
class CColumnCaption; // Класс заголовка столбца таблицы
class CTableHeader; // Класс заголовка таблицы
class CTable; // Класс таблицы
class CTableByParam; // Класс таблицы на основе массива параметров
//+------------------------------------------------------------------+
//| Макросы |
//+------------------------------------------------------------------+
#define MARKER_START_DATA -1 // Маркер начала данных в файле
#define MAX_STRING_LENGTH 128 // Максимальная длина строки в ячейке
#define CELL_WIDTH_IN_CHARS 19 // Ширина ячейки таблицы в символах
//+------------------------------------------------------------------+
//| Перечисления |
//+------------------------------------------------------------------+
enum ENUM_OBJECT_TYPE // Перечисление типов объектов
{
OBJECT_TYPE_TABLE_CELL=10000, // Ячейка таблицы
OBJECT_TYPE_TABLE_ROW, // Строка таблицы
OBJECT_TYPE_TABLE_MODEL, // Модель таблицы
OBJECT_TYPE_COLUMN_CAPTION, // Заголовок столбца таблицы
OBJECT_TYPE_TABLE_HEADER, // Заголовок таблицы
OBJECT_TYPE_TABLE, // Таблица
OBJECT_TYPE_TABLE_BY_PARAM, // Таблица на данных массива параметров
};
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();
case OBJECT_TYPE_COLUMN_CAPTION : return new CColumnCaption();
case OBJECT_TYPE_TABLE_HEADER : return new CTableHeader();
case OBJECT_TYPE_TABLE : return new CTable();
case OBJECT_TYPE_TABLE_BY_PARAM : return new CTableByParam();
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; // Флаг редактируемой ячейки
//--- Устанавливает "пустое значение"
void SetEmptyValue(void)
{
switch(this.m_datatype)
{
case TYPE_LONG :
case TYPE_DATETIME:
case TYPE_COLOR : this.SetValue(LONG_MAX); break;
case TYPE_DOUBLE : this.SetValue(DBL_MAX); break;
default : this.SetValue(""); break;
}
}
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(this.ValueD()!=DBL_MAX ? ::DoubleToString(this.ValueD(),this.Digits()) : "");
case TYPE_LONG : return(this.ValueL()!=LONG_MAX ? ::IntegerToString(this.ValueL()) : "");
case TYPE_DATETIME: return(this.ValueL()!=LONG_MAX ? ::TimeToString(this.ValueL(),this.m_time_flags) : "");
case TYPE_COLOR : return(this.ValueL()!=LONG_MAX ? ::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 ClearData(void) { this.SetEmptyValue(); }
//--- Установка значений переменных
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;
}
//--- Возвращает (1) назначенный в ячейку объект, (2) тип назначенного в ячейку объекта
CObject *AssignedObject(void) { return this.m_object; }
ENUM_OBJECT_TYPE AssignedObjType(void) const { return this.m_object_type; }
//--- Устанавливает 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);
}
//--- (1) Возвращает, (2) выводит в журнал описание объекта
virtual 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 *CellAddNew(const double value);
CTableCell *CellAddNew(const long value);
CTableCell *CellAddNew(const datetime value);
CTableCell *CellAddNew(const color value);
CTableCell *CellAddNew(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) тип назначенного в ячейку объекта
CObject *CellGetObject(const uint index);
ENUM_OBJECT_TYPE CellGetObjType(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) выводит в журнал описание объекта
virtual string Description(void);
void Print(const bool detail, const bool as_table=false, const int cell_width=CELL_WIDTH_IN_CHARS);
//--- Виртуальные методы (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::CellAddNew(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::CellAddNew(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::CellAddNew(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::CellAddNew(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::CellAddNew(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();
}
//+------------------------------------------------------------------+
//| Возвращает назначенный в ячейку объект |
//+------------------------------------------------------------------+
CObject *CTableRow::CellGetObject(const uint index)
{
//--- Получаем из списка нужную ячейку и возвращаем указатель на назначенный объект
CTableCell *cell=this.GetCell(index);
return(cell!=NULL ? cell.AssignedObject() : NULL);
}
//+------------------------------------------------------------------+
//| Возвращает тип назначенного в ячейку объекта |
//+------------------------------------------------------------------+
ENUM_OBJECT_TYPE CTableRow::CellGetObjType(const uint index)
{
//--- Получаем из списка нужную ячейку и возвращаем тип назначенного объекта
CTableCell *cell=this.GetCell(index);
return(cell!=NULL ? cell.AssignedObjType() : (ENUM_OBJECT_TYPE)WRONG_VALUE);
}
//+------------------------------------------------------------------+
//| Удаляет ячейку |
//+------------------------------------------------------------------+
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=CELL_WIDTH_IN_CHARS)
{
//--- Количество ячеек
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 CMqlParamObj : public CObject
{
protected:
public:
MqlParam m_param;
//--- Установка параметров
void Set(const MqlParam &param)
{
this.m_param.type=param.type;
this.m_param.double_value=param.double_value;
this.m_param.integer_value=param.integer_value;
this.m_param.string_value=param.string_value;
}
//--- Возврат параметров
MqlParam Param(void) const { return this.m_param; }
ENUM_DATATYPE Datatype(void) const { return this.m_param.type; }
double ValueD(void) const { return this.m_param.double_value; }
long ValueL(void) const { return this.m_param.integer_value;}
string ValueS(void) const { return this.m_param.string_value; }
//--- Описание объекта
virtual string Description(void)
{
string t=::StringSubstr(::EnumToString(this.m_param.type),5);
t.Lower();
string v="";
switch(this.m_param.type)
{
case TYPE_STRING : v=this.ValueS(); break;
case TYPE_FLOAT : case TYPE_DOUBLE : v=::DoubleToString(this.ValueD()); break;
case TYPE_DATETIME: v=::TimeToString(this.ValueL(),TIME_DATE|TIME_MINUTES|TIME_SECONDS); break;
default : v=(string)this.ValueL(); break;
}
return(::StringFormat("<%s>%s",t,v));
}
//--- Конструкторы/деструктор
CMqlParamObj(void){}
CMqlParamObj(const MqlParam &param) { this.Set(param); }
~CMqlParamObj(void){}
};
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс для создания списков данных |
//+------------------------------------------------------------------+
class DataListCreator
{
public:
//--- Добавляет новую строку к списку CList list_data
static CList *AddNewRowToDataList(CList *list_data)
{
CList *row=new CList;
if(row==NULL || list_data.Add(row)<0)
return NULL;
return row;
}
//--- Создаёт новый объект параметров CMqlParamObj и добавляет его к списку CList
static bool AddNewCellParamToRow(CList *row,MqlParam &param)
{
CMqlParamObj *cell=new CMqlParamObj(param);
if(cell==NULL)
return false;
if(row.Add(cell)<0)
{
delete cell;
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| Класс модели таблицы |
//+------------------------------------------------------------------+
class CTableModel : public CObject
{
protected:
CTableRow m_row_tmp; // Объект строки для поиска в списке
CListObj m_list_rows; // Список строк таблицы
//--- Создаёт модель таблицы из двумерного массива
template<typename T>
void CreateTableModel(T &array[][]);
void CreateTableModel(const uint num_rows,const uint num_columns);
void CreateTableModel(const matrix &row_data);
void CreateTableModel(CList &list_param);
//--- Возвращает корректный тип данных
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) тип назначенного в ячейку объекта
CObject *CellGetObject(const uint row, const uint col);
ENUM_OBJECT_TYPE CellGetObjType(const uint row, const uint col);
//--- (1) Возвращает, (2) выводит в журнал описание ячейки, (3) назначенный в ячейку объект
string CellDescription(const uint row, const uint col);
void CellPrint(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 RowClearData(const uint index);
//--- (1) Возвращает, (2) выводит в журнал описание строки
string RowDescription(const uint index);
void RowPrint(const uint index,const bool detail);
//--- (1) Добавляет, (2) удаляет (3) перемещает столбец, (4) очищает данные, устанавливает (5) тип, (6) точность данных столбца
bool ColumnAddNew(const int index=-1);
bool ColumnDelete(const uint index);
bool ColumnMoveTo(const uint col_index, const uint index_to);
void ColumnClearData(const uint index);
void ColumnSetDatatype(const uint index,const ENUM_DATATYPE type);
void ColumnSetDigits(const uint index,const int digits);
//--- (1) Возвращает, (2) выводит в журнал описание таблицы
virtual string Description(void);
void Print(const bool detail);
void PrintTable(const int cell_width=CELL_WIDTH_IN_CHARS);
//--- (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); }
//--- Конструкторы/деструктор
template<typename T> CTableModel(T &array[][]) { this.CreateTableModel(array); }
CTableModel(const uint num_rows,const uint num_columns) { this.CreateTableModel(num_rows,num_columns); }
CTableModel(const matrix &row_data) { this.CreateTableModel(row_data); }
CTableModel(CList &row_data) { this.CreateTableModel(row_data); }
CTableModel(void){}
~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.CellAddNew(array[r][c]);
}
}
}
//+------------------------------------------------------------------+
//| Создаёт модель таблицы из указанного количества строк и столбцов |
//+------------------------------------------------------------------+
void CTableModel::CreateTableModel(const uint num_rows,const uint num_columns)
{
//--- В цикле по количеству строк
for(uint r=0; r<num_rows; r++)
{
//--- создаём новую пустую строку и добавляем её в конец списка строк
CTableRow *row=this.CreateNewEmptyRow();
//--- Если строка создана и добавлена в список,
if(row!=NULL)
{
//--- В цикле по количеству столбцов
//--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки
for(uint c=0; c<num_columns; c++)
{
CTableCell *cell=row.CellAddNew(0.0);
if(cell!=NULL)
cell.ClearData();
}
}
}
}
//+------------------------------------------------------------------+
//| Создаёт модель таблицы из указанной матрицы |
//+------------------------------------------------------------------+
void CTableModel::CreateTableModel(const matrix &row_data)
{
//--- Количество строк и столбцов
ulong num_rows=row_data.Rows();
ulong num_columns=row_data.Cols();
//--- В цикле по количеству строк
for(uint r=0; r<num_rows; r++)
{
//--- создаём новую пустую строку и добавляем её в конец списка строк
CTableRow *row=this.CreateNewEmptyRow();
//--- Если строка создана и добавлена в список,
if(row!=NULL)
{
//--- В цикле по количеству столбцов
//--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки
for(uint c=0; c<num_columns; c++)
row.CellAddNew(row_data[r][c]);
}
}
}
//+------------------------------------------------------------------+
//| Создаёт модель таблицы из списка параметров |
//+------------------------------------------------------------------+
void CTableModel::CreateTableModel(CList &list_param)
{
//--- Если передан пустой список - сообщаем об этом и уходим
if(list_param.Total()==0)
{
::PrintFormat("%s: Error. Empty list passed",__FUNCTION__);
return;
}
//--- Получаем указатель на первую строку таблицы для определения количества столбцов
//--- Если первую строку получить не удалось, или в ней нет ячеек - сообщаем об этом и уходим
CList *first_row=list_param.GetFirstNode();
if(first_row==NULL || first_row.Total()==0)
{
if(first_row==NULL)
::PrintFormat("%s: Error. Failed to get first row of list",__FUNCTION__);
else
::PrintFormat("%s: Error. First row does not contain data",__FUNCTION__);
return;
}
//--- Количество строк и столбцов
ulong num_rows=list_param.Total();
ulong num_columns=first_row.Total();
//--- В цикле по количеству строк
for(uint r=0; r<num_rows; r++)
{
//--- получаем очередную строку таблицы из списка list_param
CList *col_list=list_param.GetNodeAtIndex(r);
if(col_list==NULL)
continue;
//--- создаём новую пустую строку и добавляем её в конец списка строк
CTableRow *row=this.CreateNewEmptyRow();
//--- Если строка создана и добавлена в список,
if(row!=NULL)
{
//--- В цикле по количеству столбцов
//--- создаём все ячейки, добавляя каждую новую в конец списка ячеек строки
for(uint c=0; c<num_columns; c++)
{
CMqlParamObj *param=col_list.GetNodeAtIndex(c);
if(param==NULL)
continue;
//--- Объявляем указатель на ячейку и тип данных, которые будут в ней содержаться
CTableCell *cell=NULL;
ENUM_DATATYPE datatype=param.Datatype();
//--- В зависимости от типа данных
switch(datatype)
{
//--- вещественный тип данных
case TYPE_FLOAT :
case TYPE_DOUBLE : cell=row.CellAddNew((double)param.ValueD()); // Создаём новую ячейку с double-данными и
if(cell!=NULL)
cell.SetDigits((int)param.ValueL()); // записываем точность отображаемых данных
break;
//--- тип данных datetime
case TYPE_DATETIME: cell=row.CellAddNew((datetime)param.ValueL()); // Создаём новую ячейку с datetime-данными и
if(cell!=NULL)
cell.SetDatetimeFlags((int)param.ValueD()); // записываем флаги отображения даты/времени
break;
//--- тип данных color
case TYPE_COLOR : cell=row.CellAddNew((color)param.ValueL()); // Создаём новую ячейку с color-данными и
if(cell!=NULL)
cell.SetColorNameFlag((bool)param.ValueD()); // записваем флаг отображения наименования известных цветов
break;
//--- строковый тип данных
case TYPE_STRING : cell=row.CellAddNew((string)param.ValueS()); // Создаём новую ячейку со string-данными
break;
//--- целочисленный тип данных
default : cell=row.CellAddNew((long)param.ValueL()); // Создаём новую ячейку с long-данными
break;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Создаёт новую пустую строку и добавляет в конец списка |
//+------------------------------------------------------------------+
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.CellAddNew(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.CellAddNew(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);
}
//+------------------------------------------------------------------+
//| Возвращает назначенный в ячейку объект |
//+------------------------------------------------------------------+
CObject *CTableModel::CellGetObject(const uint row,const uint col)
{
//--- Получаем строку по индексу и возвращаем объект, назначенный на ячейку с индексом col
CTableRow *row_obj=this.GetRow(row);
return(row_obj!=NULL ? row_obj.CellGetObject(col) : NULL);
}
//+------------------------------------------------------------------+
//| Возвращает тип назначенного в ячейку объекта |
//+------------------------------------------------------------------+
ENUM_OBJECT_TYPE CTableModel::CellGetObjType(const uint row,const uint col)
{
//--- Получаем строку по индексу и возвращаем тип объекта, назначенного на ячейку с индексом col
CTableRow *row_obj=this.GetRow(row);
return(row_obj!=NULL ? row_obj.CellGetObjType(col) : (ENUM_OBJECT_TYPE)WRONG_VALUE);
}
//+------------------------------------------------------------------+
//| Возвращает количество ячеек в указанной строке |
//+------------------------------------------------------------------+
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::RowClearData(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.RowClearData(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::ColumnAddNew(const int index=-1)
{
//--- Объявляем переменные
CTableCell *cell=NULL;
bool res=true;
//--- В цикле по количеству строк
for(uint i=0;i<this.RowsTotal();i++)
{
//--- получаем очередную строку
CTableRow *row=this.GetRow(i);
if(row!=NULL)
{
//--- добавляем в конец строки ячейку с типом double
cell=row.CellAddNew(0.0);
if(cell==NULL)
res &=false;
//--- очищаем ячейку
else
cell.ClearData();
}
}
//--- Если передан индекс колонки не отрицательный - сдвигаем колонку на указанную позицию
if(res && index>-1)
res &=this.ColumnMoveTo(this.CellsInRow(0)-1,index);
//--- Возвращаем результат
return res;
}
//+------------------------------------------------------------------+
//| Удаляет столбец |
//+------------------------------------------------------------------+
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::ColumnClearData(const uint index)
{
//--- В цикле по всем строкам таблицы
for(uint i=0;i<this.RowsTotal();i++)
{
//--- получаем из каждой строки ячейку с индексом столбца и очищаем её
CTableCell *cell=this.GetCell(i, index);
if(cell!=NULL)
cell.ClearData();
}
}
//+------------------------------------------------------------------+
//| Устанавливает тип данных столбца |
//+------------------------------------------------------------------+
void CTableModel::ColumnSetDatatype(const uint index,const ENUM_DATATYPE type)
{
//--- В цикле по всем строкам таблицы
for(uint i=0;i<this.RowsTotal();i++)
{
//--- получаем из каждой строки ячейку с индексом столбца и устанавливаем тип данных
CTableCell *cell=this.GetCell(i, index);
if(cell!=NULL)
cell.SetDatatype(type);
}
}
//+------------------------------------------------------------------+
//| Устанавливает точность данных столбца |
//+------------------------------------------------------------------+
void CTableModel::ColumnSetDigits(const uint index,const int digits)
{
//--- В цикле по всем строкам таблицы
for(uint i=0;i<this.RowsTotal();i++)
{
//--- получаем из каждой строки ячейку с индексом столбца и устанавливаем точность данных
CTableCell *cell=this.GetCell(i, index);
if(cell!=NULL)
cell.SetDigits(digits);
}
}
//+------------------------------------------------------------------+
//| Возвращает описание объекта |
//+------------------------------------------------------------------+
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=CELL_WIDTH_IN_CHARS)
{
//--- Получаем указатель на первую строку (индекс 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)
{
//--- Очищаем список строк
this.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;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс заголовка столбца таблицы |
//+------------------------------------------------------------------+
class CColumnCaption : public CObject
{
protected:
//--- Переменные
ushort m_ushort_array[MAX_STRING_LENGTH]; // Массив символов заголовка
uint m_column; // Номер столбца
ENUM_DATATYPE m_datatype; // Тип данных
public:
//--- (1) Устанавливает, (2) возвращает номер столбца
void SetColumn(const uint column) { this.m_column=column; }
uint Column(void) const { return this.m_column; }
//--- (1) Устанавливает, (2) возвращает тип данных столбца
ENUM_DATATYPE Datatype(void) const { return this.m_datatype; }
void SetDatatype(const ENUM_DATATYPE datatype) { this.m_datatype=datatype;}
//--- Очищает данные
void ClearData(void) { this.SetValue(""); }
//--- Устанавливает заголовок
void SetValue(const string value)
{
::StringToShortArray(value,this.m_ushort_array);
}
//--- Возвращает текст заголовка
string Value(void) const
{
string res=::ShortArrayToString(this.m_ushort_array);
res.TrimLeft();
res.TrimRight();
return res;
}
//--- (1) Возвращает, (2) выводит в журнал описание объекта
virtual 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_COLUMN_CAPTION); }
//--- Конструкторы/деструктор
CColumnCaption(void) : m_column(0) { this.SetValue(""); }
CColumnCaption(const uint column,const string value) : m_column(column) { this.SetValue(value); }
~CColumnCaption(void) {}
};
//+------------------------------------------------------------------+
//| Сравнение двух объектов |
//+------------------------------------------------------------------+
int CColumnCaption::Compare(const CObject *node,const int mode=0) const
{
const CColumnCaption *obj=node;
return(this.Column()>obj.Column() ? 1 : this.Column()<obj.Column() ? -1 : 0);
}
//+------------------------------------------------------------------+
//| Сохранение в файл |
//+------------------------------------------------------------------+
bool CColumnCaption::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_column,INT_VALUE)!=INT_VALUE)
return(false);
//--- Сохраняем значение
if(::FileWriteArray(file_handle,this.m_ushort_array)!=sizeof(this.m_ushort_array))
return(false);
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Загрузка из файла |
//+------------------------------------------------------------------+
bool CColumnCaption::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_column=::FileReadInteger(file_handle,INT_VALUE);
//--- Загружаем значение
if(::FileReadArray(file_handle,this.m_ushort_array)!=sizeof(this.m_ushort_array))
return(false);
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Возвращает описание объекта |
//+------------------------------------------------------------------+
string CColumnCaption::Description(void)
{
return(::StringFormat("%s: Column %u, Value: \"%s\"",
TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.Column(),this.Value()));
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание объекта |
//+------------------------------------------------------------------+
void CColumnCaption::Print(void)
{
::Print(this.Description());
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс заголовка таблицы |
//+------------------------------------------------------------------+
class CTableHeader : public CObject
{
protected:
CColumnCaption m_caption_tmp; // Объект заголовка столбца для поиска в списке
CListObj m_list_captions; // Список заголовков столбцлв
//--- Добавляет указанный заголовок в конец списка
bool AddNewColumnCaption(CColumnCaption *caption);
//--- Создаёт заголовок таблицы из строкового массива
void CreateHeader(string &array[]);
//--- Устанавливает позицию столбца всем заголовкам столбцов
void ColumnPositionUpdate(void);
public:
//--- Создаёт новый заголовок и добавляет в конец списка
CColumnCaption *CreateNewColumnCaption(const string caption);
//--- Возвращает (1) заголовок по индексу, (2) количество заголовков столбцов
CColumnCaption *GetColumnCaption(const uint index) { return this.m_list_captions.GetNodeAtIndex(index); }
uint ColumnsTotal(void) const { return this.m_list_captions.Total(); }
//--- Устанавливает значение указанному заголовку столбца
void ColumnCaptionSetValue(const uint index,const string value);
//--- (1) Устанавливает, (2) возвращает тип данных для указанного заголовка столбца
void ColumnCaptionSetDatatype(const uint index,const ENUM_DATATYPE type);
ENUM_DATATYPE ColumnCaptionDatatype(const uint index);
//--- (1) Удаляет (2) перемещает заголовок столбца
bool ColumnCaptionDelete(const uint index);
bool ColumnCaptionMoveTo(const uint caption_index, const uint index_to);
//--- Очищает данные заголовков столбцов
void ClearData(void);
//--- Очищает список заголовков столбцов
void Destroy(void) { this.m_list_captions.Clear(); }
//--- (1) Возвращает, (2) выводит в журнал описание объекта
virtual string Description(void);
void Print(const bool detail, const bool as_table=false, const int column_width=CELL_WIDTH_IN_CHARS);
//--- Виртуальные методы (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_HEADER); }
//--- Конструкторы/деструктор
CTableHeader(void) {}
CTableHeader(string &array[]) { this.CreateHeader(array); }
~CTableHeader(void){}
};
//+------------------------------------------------------------------+
//| Создаёт новый заголовок и добавляет в конец списка |
//+------------------------------------------------------------------+
CColumnCaption *CTableHeader::CreateNewColumnCaption(const string caption)
{
//--- Создаём новый объект заголовка
CColumnCaption *caption_obj=new CColumnCaption(this.ColumnsTotal(),caption);
if(caption_obj==NULL)
{
::PrintFormat("%s: Error. Failed to create new column caption at position %u",__FUNCTION__, this.ColumnsTotal());
return NULL;
}
//--- Добавляем созданный заголовок в конец списка
if(!this.AddNewColumnCaption(caption_obj))
{
delete caption_obj;
return NULL;
}
//--- Возвращаем указатель на объект
return caption_obj;
}
//+------------------------------------------------------------------+
//| Добавляет заголовок в конец списка |
//+------------------------------------------------------------------+
bool CTableHeader::AddNewColumnCaption(CColumnCaption *caption)
{
//--- Если передан пустой объект - сообщаем и возвращаем false
if(caption==NULL)
{
::PrintFormat("%s: Error. Empty CColumnCaption object passed",__FUNCTION__);
return false;
}
//--- Устанавливаем индекс заголовка в списке и добавляем созданный заголовок в конец списка
caption.SetColumn(this.ColumnsTotal());
if(this.m_list_captions.Add(caption)==WRONG_VALUE)
{
::PrintFormat("%s: Error. Failed to add caption (%u) to list",__FUNCTION__,this.ColumnsTotal());
return false;
}
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//| Создаёт заголовок таблицы из строкового массива |
//+------------------------------------------------------------------+
void CTableHeader::CreateHeader(string &array[])
{
//--- Получаем из свойств массива количество столбцов таблицы
uint total=array.Size();
//--- В цикле по размеру массива
//--- создаём все заголовки, добавляя каждый новый в конец списка
for(uint i=0; i<total; i++)
this.CreateNewColumnCaption(array[i]);
}
//+------------------------------------------------------------------+
//| Устанавливает значение в указанный заголовок столбца |
//+------------------------------------------------------------------+
void CTableHeader::ColumnCaptionSetValue(const uint index,const string value)
{
//--- Получаем из списка нужный заголовок и записываем в него новое значение
CColumnCaption *caption=this.GetColumnCaption(index);
if(caption!=NULL)
caption.SetValue(value);
}
//+------------------------------------------------------------------+
//| Устанавливает тип данных для указанного заголовка столбца |
//+------------------------------------------------------------------+
void CTableHeader::ColumnCaptionSetDatatype(const uint index,const ENUM_DATATYPE type)
{
//--- Получаем из списка нужный заголовок и записываем в него новое значение
CColumnCaption *caption=this.GetColumnCaption(index);
if(caption!=NULL)
caption.SetDatatype(type);
}
//+------------------------------------------------------------------+
//| Возвращает тип данных указанного заголовка столбца |
//+------------------------------------------------------------------+
ENUM_DATATYPE CTableHeader::ColumnCaptionDatatype(const uint index)
{
//--- Получаем из списка нужный заголовок и возвращаем из него тип данных столбца
CColumnCaption *caption=this.GetColumnCaption(index);
return(caption!=NULL ? caption.Datatype() : (ENUM_DATATYPE)WRONG_VALUE);
}
//+------------------------------------------------------------------+
//| Удаляет заголовок указанного столбца |
//+------------------------------------------------------------------+
bool CTableHeader::ColumnCaptionDelete(const uint index)
{
//--- Удаляем заголовок в списке по индексу
if(!this.m_list_captions.Delete(index))
return false;
//--- Обновляем индексы для оставшихся заголовков в списке
this.ColumnPositionUpdate();
return true;
}
//+------------------------------------------------------------------+
//| Перемещает заголовок столбца на указанную позицию |
//+------------------------------------------------------------------+
bool CTableHeader::ColumnCaptionMoveTo(const uint caption_index,const uint index_to)
{
//--- Получаем нужный заголовок по индексу в списке, делая его текущим
CColumnCaption *caption=this.GetColumnCaption(caption_index);
//--- Перемещаем текущий заголовок на указанную позицию в списке
if(caption==NULL || !this.m_list_captions.MoveToIndex(index_to))
return false;
//--- Обновляем индексы всех заголовков в списке
this.ColumnPositionUpdate();
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает позиции столбца всем заголовкам |
//+------------------------------------------------------------------+
void CTableHeader::ColumnPositionUpdate(void)
{
//--- В цикле по всем заголовкам в списке
for(int i=0;i<this.m_list_captions.Total();i++)
{
//--- получаем очередной заголовок и устанавливаем в него индекс столбца
CColumnCaption *caption=this.GetColumnCaption(i);
if(caption!=NULL)
caption.SetColumn(this.m_list_captions.IndexOf(caption));
}
}
//+------------------------------------------------------------------+
//| Очищает данные заголовков столбцов в списке |
//+------------------------------------------------------------------+
void CTableHeader::ClearData(void)
{
//--- В цикле по всем заголовкам в списке
for(uint i=0;i<this.ColumnsTotal();i++)
{
//--- получаем очередной заголовок и устанавливаем в него пустое значение
CColumnCaption *caption=this.GetColumnCaption(i);
if(caption!=NULL)
caption.ClearData();
}
}
//+------------------------------------------------------------------+
//| Возвращает описание объекта |
//+------------------------------------------------------------------+
string CTableHeader::Description(void)
{
return(::StringFormat("%s: Captions total: %u",
TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.ColumnsTotal()));
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание объекта |
//+------------------------------------------------------------------+
void CTableHeader::Print(const bool detail, const bool as_table=false, const int column_width=CELL_WIDTH_IN_CHARS)
{
//--- Количество заголовков
int total=(int)this.ColumnsTotal();
//--- Если вывод в табличном виде
string res="";
if(as_table)
{
//--- создаём строку таблицы из значений всех заголовков
res="|";
for(int i=0;i<total;i++)
{
CColumnCaption *caption=this.GetColumnCaption(i);
if(caption==NULL)
continue;
res+=::StringFormat("%*s |",column_width,caption.Value());
}
//--- Выводим строку в журнал и уходим
::Print(res);
return;
}
//--- Выводим заголовок в виде описания строки
::Print(this.Description()+(detail ? ":" : ""));
//--- Если детализированное описание
if(detail)
{
//--- В цикле по списку заголовков строки
for(int i=0; i<total; i++)
{
//--- получаем текущий заголовок и добавляем в итоговую строку его описание
CColumnCaption *caption=this.GetColumnCaption(i);
if(caption!=NULL)
res+=" "+caption.Description()+(i<total-1 ? "\n" : "");
}
//--- Выводим в журнал созданную в цикле строку
::Print(res);
}
}
//+------------------------------------------------------------------+
//| Сохранение в файл |
//+------------------------------------------------------------------+
bool CTableHeader::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_captions.Save(file_handle))
return(false);
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//| Загрузка из файла |
//+------------------------------------------------------------------+
bool CTableHeader::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_captions.Load(file_handle))
return(false);
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс таблицы |
//+------------------------------------------------------------------+
class CTable : public CObject
{
private:
//--- Заполняет массив заголовков столбцов в стиле Excel
bool FillArrayExcelNames(const uint num_columns);
//--- Возвращает наименование столбца как в Excel
string GetExcelColumnName(uint column_number);
//--- Возвращает доступность заголовка
bool HeaderCheck(void) const { return(this.m_table_header!=NULL && this.m_table_header.ColumnsTotal()>0); }
protected:
CTableModel *m_table_model; // Указатель на модель таблицы
CTableHeader *m_table_header; // Указатель на заголовок таблицы
CList m_list_rows; // Список массивов параметров из полей структуры
string m_array_names[]; // Массив заголовков столбцов
int m_id; // Идентификатор таблицы
//--- Копирует массив наименований заголовков
bool ArrayNamesCopy(const string &column_names[],const uint columns_total);
public:
//--- (1) Устанавливает, (2) возвращает модель таблицы
void SetTableModel(CTableModel *table_model) { this.m_table_model=table_model; }
CTableModel *GetTableModel(void) { return this.m_table_model; }
//--- (1) Устанавливает, (2) возвращает заголовок
void SetTableHeader(CTableHeader *table_header) { this.m_table_header=m_table_header; }
CTableHeader *GetTableHeader(void) { return this.m_table_header; }
//--- (1) Устанавливает, (2) возвращает идентификатор таблицы
void SetID(const int id) { this.m_id=id; }
int ID(void) const { return this.m_id; }
//--- Очищает данные заголовков столбцов
void HeaderClearData(void)
{
if(this.m_table_header!=NULL)
this.m_table_header.ClearData();
}
//--- Удаляет заголовок таблицы
void HeaderDestroy(void)
{
if(this.m_table_header==NULL)
return;
this.m_table_header.Destroy();
this.m_table_header=NULL;
}
//--- (1) Очищает все данные, (2) уничтожает модель таблицы и заголовок
void ClearData(void)
{
if(this.m_table_model!=NULL)
this.m_table_model.ClearData();
}
void Destroy(void)
{
if(this.m_table_model==NULL)
return;
this.m_table_model.Destroy();
this.m_table_model=NULL;
}
//--- Возвращает (1) заголовок, (2) ячейку, (3) строку по индексу, количество (4) строк, (5) столбцов, ячеек (6) в указанной строке, (7) в таблице
CColumnCaption *GetColumnCaption(const uint index) { return(this.m_table_header!=NULL ? this.m_table_header.GetColumnCaption(index) : NULL); }
CTableCell *GetCell(const uint row, const uint col) { return(this.m_table_model!=NULL ? this.m_table_model.GetCell(row,col) : NULL); }
CTableRow *GetRow(const uint index) { return(this.m_table_model!=NULL ? this.m_table_model.GetRow(index) : NULL); }
uint RowsTotal(void) const { return(this.m_table_model!=NULL ? this.m_table_model.RowsTotal() : 0); }
uint ColumnsTotal(void) const { return(this.m_table_model!=NULL ? this.m_table_model.CellsInRow(0) : 0); }
uint CellsInRow(const uint index) { return(this.m_table_model!=NULL ? this.m_table_model.CellsInRow(index) : 0); }
uint CellsTotal(void) { return(this.m_table_model!=NULL ? this.m_table_model.CellsTotal() : 0); }
//--- Устанавливает (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);
//--- Возвращает строковое значение указанной ячейки
virtual string CellValueAt(const uint row, const uint col);
protected:
//--- (1) Удаляет (2) перемещает ячейку
bool CellDelete(const uint row, const uint col);
bool CellMoveTo(const uint row, const uint cell_index, const uint index_to);
public:
//--- (1) Возвращает, (2) выводит в журнал описание ячейки, (3) назначенный в ячейку объект
string CellDescription(const uint row, const uint col);
void CellPrint(const uint row, const uint col);
//---Возвращает (1) назначенный в ячейку объект, (2) тип назначенного в ячейку объекта
CObject *CellGetObject(const uint row, const uint col);
ENUM_OBJECT_TYPE CellGetObjType(const uint row, const uint col);
//--- Создаёт новую строку и (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 RowClearData(const uint index);
//--- (1) Возвращает, (2) выводит в журнал описание строки
string RowDescription(const uint index);
void RowPrint(const uint index,const bool detail);
//--- (1) Добавляет новый, (2) удаляет, (3) перемещает столбец, (4) очищает данные столбца
bool ColumnAddNew(const string caption,const int index=-1);
bool ColumnDelete(const uint index);
bool ColumnMoveTo(const uint index, const uint index_to);
void ColumnClearData(const uint index);
//--- Устанавливает (1) значение указанному заголовку, (2) точность данных указанному столбцу
void ColumnCaptionSetValue(const uint index,const string value);
void ColumnSetDigits(const uint index,const int digits);
//--- (1) Устанавливает, (2) возвращает тип данных для указанного столбца
void ColumnSetDatatype(const uint index,const ENUM_DATATYPE type);
ENUM_DATATYPE ColumnDatatype(const uint index);
//--- (1) Возвращает, (2) выводит в журнал описание объекта
virtual string Description(void);
void Print(const int column_width=CELL_WIDTH_IN_CHARS);
//--- Виртуальные методы (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); }
//--- Конструкторы/деструктор
CTable(void) : m_table_model(NULL), m_table_header(NULL) { this.m_list_rows.Clear();}
template<typename T> CTable(T &row_data[][],const string &column_names[]);
CTable(const uint num_rows, const uint num_columns);
CTable(const matrix &row_data,const string &column_names[]);
~CTable (void);
};
//+-------------------------------------------------------------------+
//| Конструктор с указанием массива таблицы и массива заголовков. |
//| Определяет количество и наименования колонок согласно column_names|
//| Количество строк определены размером массива данных row_data, |
//| который используется и для заполнения таблицы |
//+-------------------------------------------------------------------+
template<typename T>
CTable::CTable(T &row_data[][],const string &column_names[]) : m_id(-1)
{
this.m_table_model=new CTableModel(row_data);
if(column_names.Size()>0)
this.ArrayNamesCopy(column_names,row_data.Range(1));
else
{
::PrintFormat("%s: An empty array names was passed. The header array will be filled in Excel style (A, B, C)",__FUNCTION__);
this.FillArrayExcelNames((uint)::ArrayRange(row_data,1));
}
this.m_table_header=new CTableHeader(this.m_array_names);
}
//+------------------------------------------------------------------+
//| Конструктор таблицы с определением количества колонок и строк. |
//| Колонки будут иметь Excel-наименования "A", "B", "C" и т.д. |
//+------------------------------------------------------------------+
CTable::CTable(const uint num_rows,const uint num_columns) : m_table_header(NULL), m_id(-1)
{
this.m_table_model=new CTableModel(num_rows,num_columns);
if(this.FillArrayExcelNames(num_columns))
this.m_table_header=new CTableHeader(this.m_array_names);
}
//+-------------------------------------------------------------------+
//| Конструктор таблицы с инициализацией колонок согласно column_names|
//| Количество строк определены параметром row_data, с типом matrix |
//+-------------------------------------------------------------------+
CTable::CTable(const matrix &row_data,const string &column_names[]) : m_id(-1)
{
this.m_table_model=new CTableModel(row_data);
if(column_names.Size()>0)
this.ArrayNamesCopy(column_names,(uint)row_data.Cols());
else
{
::PrintFormat("%s: An empty array names was passed. The header array will be filled in Excel style (A, B, C)",__FUNCTION__);
this.FillArrayExcelNames((uint)row_data.Cols());
}
this.m_table_header=new CTableHeader(this.m_array_names);
}
//+------------------------------------------------------------------+
//| Деструктор |
//+------------------------------------------------------------------+
CTable::~CTable(void)
{
if(this.m_table_model!=NULL)
{
this.m_table_model.Destroy();
delete this.m_table_model;
}
if(this.m_table_header!=NULL)
{
this.m_table_header.Destroy();
delete this.m_table_header;
}
}
//+------------------------------------------------------------------+
//| Сравнение двух объектов |
//+------------------------------------------------------------------+
int CTable::Compare(const CObject *node,const int mode=0) const
{
const CTable *obj=node;
return(this.ID()>obj.ID() ? 1 : this.ID()<obj.ID() ? -1 : 0);
}
//+------------------------------------------------------------------+
//| Возвращает наименование столбца как в Excel |
//+------------------------------------------------------------------+
string CTable::GetExcelColumnName(uint column_number)
{
string column_name="";
uint index=column_number;
//--- Проверяем, что номер столбца больше 0
if(index==0)
return (__FUNCTION__+": Error. Invalid column number passed");
//--- Преобразование номера в название столбца
while(!::IsStopped() && index>0)
{
index--; // Уменьшаем номер на 1, чтобы сделать его 0-индексным
uint remainder =index % 26; // Остаток от деления на 26
uchar char_code ='A'+(uchar)remainder; // Рассчитываем код символа (буквы)
column_name=::CharToString(char_code)+column_name; // Добавляем букву в начало строки
index/=26; // Переходим к следующему разряду
}
return column_name;
}
//+------------------------------------------------------------------+
//| Заполняет массив заголовков столбцов в стиле Excel |
//+------------------------------------------------------------------+
bool CTable::FillArrayExcelNames(const uint num_columns)
{
::ResetLastError();
if(::ArrayResize(this.m_array_names,num_columns,num_columns)!=num_columns)
{
::PrintFormat("%s: ArrayResize() failed. Error %d",__FUNCTION__,::GetLastError());
return false;
}
for(int i=0;i<(int)num_columns;i++)
this.m_array_names[i]=this.GetExcelColumnName(i+1);
return true;
}
//+------------------------------------------------------------------+
//| Копирует массив наименований заголовков |
//+------------------------------------------------------------------+
bool CTable::ArrayNamesCopy(const string &column_names[],const uint columns_total)
{
if(columns_total==0)
{
::PrintFormat("%s: Error. The table has no columns",__FUNCTION__);
return false;
}
if(columns_total>column_names.Size())
{
::PrintFormat("%s: The number of header names is less than the number of columns. The header array will be filled in Excel style (A, B, C)",__FUNCTION__);
return this.FillArrayExcelNames(columns_total);
}
uint total=::fmin(columns_total,column_names.Size());
return(::ArrayCopy(this.m_array_names,column_names,0,0,total)==total);
}
//+------------------------------------------------------------------+
//| Устанавливает значение в указанную ячейку |
//+------------------------------------------------------------------+
template<typename T>
void CTable::CellSetValue(const uint row, const uint col, const T value)
{
if(this.m_table_model!=NULL)
this.m_table_model.CellSetValue(row,col,value);
}
//+------------------------------------------------------------------+
//| Устанавливает точность в указанную ячейку |
//+------------------------------------------------------------------+
void CTable::CellSetDigits(const uint row, const uint col, const int digits)
{
if(this.m_table_model!=NULL)
this.m_table_model.CellSetDigits(row,col,digits);
}
//+------------------------------------------------------------------+
//| Устанавливает флаги отображения времени в указанную ячейку |
//+------------------------------------------------------------------+
void CTable::CellSetTimeFlags(const uint row, const uint col, const uint flags)
{
if(this.m_table_model!=NULL)
this.m_table_model.CellSetTimeFlags(row,col,flags);
}
//+------------------------------------------------------------------+
//| Устанавливает флаг отображения имён цветов в указанную ячейку |
//+------------------------------------------------------------------+
void CTable::CellSetColorNamesFlag(const uint row, const uint col, const bool flag)
{
if(this.m_table_model!=NULL)
this.m_table_model.CellSetColorNamesFlag(row,col,flag);
}
//+------------------------------------------------------------------+
//| Назначает объект в ячейку |
//+------------------------------------------------------------------+
void CTable::CellAssignObject(const uint row, const uint col,CObject *object)
{
if(this.m_table_model!=NULL)
this.m_table_model.CellAssignObject(row,col,object);
}
//+------------------------------------------------------------------+
//| Отменяет объект в ячейке |
//+------------------------------------------------------------------+
void CTable::CellUnassignObject(const uint row, const uint col)
{
if(this.m_table_model!=NULL)
this.m_table_model.CellUnassignObject(row,col);
}
//+------------------------------------------------------------------+
//| Возвращает строковое значение указанной ячейки |
//+------------------------------------------------------------------+
string CTable::CellValueAt(const uint row,const uint col)
{
CTableCell *cell=this.GetCell(row,col);
return(cell!=NULL ? cell.Value() : "");
}
//+------------------------------------------------------------------+
//| Удаляет ячейку |
//+------------------------------------------------------------------+
bool CTable::CellDelete(const uint row, const uint col)
{
return(this.m_table_model!=NULL ? this.m_table_model.CellDelete(row,col) : false);
}
//+------------------------------------------------------------------+
//| Перемещает ячейку |
//+------------------------------------------------------------------+
bool CTable::CellMoveTo(const uint row, const uint cell_index, const uint index_to)
{
return(this.m_table_model!=NULL ? this.m_table_model.CellMoveTo(row,cell_index,index_to) : false);
}
//+------------------------------------------------------------------+
//| Возвращает назначенный в ячейку объект |
//+------------------------------------------------------------------+
CObject *CTable::CellGetObject(const uint row, const uint col)
{
return(this.m_table_model!=NULL ? this.m_table_model.CellGetObject(row,col) : NULL);
}
//+------------------------------------------------------------------+
//| Возвращает тип назначенного в ячейку объекта |
//+------------------------------------------------------------------+
ENUM_OBJECT_TYPE CTable::CellGetObjType(const uint row,const uint col)
{
return(this.m_table_model!=NULL ? this.m_table_model.CellGetObjType(row,col) : (ENUM_OBJECT_TYPE)WRONG_VALUE);
}
//+------------------------------------------------------------------+
//| Возвращает описание ячейки |
//+------------------------------------------------------------------+
string CTable::CellDescription(const uint row, const uint col)
{
return(this.m_table_model!=NULL ? this.m_table_model.CellDescription(row,col) : "");
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание ячейки |
//+------------------------------------------------------------------+
void CTable::CellPrint(const uint row, const uint col)
{
if(this.m_table_model!=NULL)
this.m_table_model.CellPrint(row,col);
}
//+------------------------------------------------------------------+
//| Создаёт новую строку и добавляет в конец списка |
//+------------------------------------------------------------------+
CTableRow *CTable::RowAddNew(void)
{
return(this.m_table_model!=NULL ? this.m_table_model.RowAddNew() : NULL);
}
//+------------------------------------------------------------------+
//| Создаёт новую строку и вставляет в указанную позицию списка |
//+------------------------------------------------------------------+
CTableRow *CTable::RowInsertNewTo(const uint index_to)
{
return(this.m_table_model!=NULL ? this.m_table_model.RowInsertNewTo(index_to) : NULL);
}
//+------------------------------------------------------------------+
//| Удаляет строку |
//+------------------------------------------------------------------+
bool CTable::RowDelete(const uint index)
{
return(this.m_table_model!=NULL ? this.m_table_model.RowDelete(index) : false);
}
//+------------------------------------------------------------------+
//| Перемещает строку |
//+------------------------------------------------------------------+
bool CTable::RowMoveTo(const uint row_index, const uint index_to)
{
return(this.m_table_model!=NULL ? this.m_table_model.RowMoveTo(row_index,index_to) : false);
}
//+------------------------------------------------------------------+
//| Очищает данные строки |
//+------------------------------------------------------------------+
void CTable::RowClearData(const uint index)
{
if(this.m_table_model!=NULL)
this.m_table_model.RowClearData(index);
}
//+------------------------------------------------------------------+
//| Возвращает описание строки |
//+------------------------------------------------------------------+
string CTable::RowDescription(const uint index)
{
return(this.m_table_model!=NULL ? this.m_table_model.RowDescription(index) : "");
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание строки |
//+------------------------------------------------------------------+
void CTable::RowPrint(const uint index,const bool detail)
{
if(this.m_table_model!=NULL)
this.m_table_model.RowPrint(index,detail);
}
//+------------------------------------------------------------------+
//| Создаёт новый столбец и добавляет его в указанную позицию таблицы|
//+------------------------------------------------------------------+
bool CTable::ColumnAddNew(const string caption,const int index=-1)
{
//--- Если нет модели таблицы, либо ошибка добавления нового столбца к модели - возвращаем false
if(this.m_table_model==NULL || !this.m_table_model.ColumnAddNew(index))
return false;
//--- Если нет заголовка - возвращаем true (столбец добавлен без заголовка)
if(this.m_table_header==NULL)
return true;
//--- Проверяем создание нового заголовка столбца и, если не создан - возвращаем false
CColumnCaption *caption_obj=this.m_table_header.CreateNewColumnCaption(caption);
if(caption_obj==NULL)
return false;
//--- Если передан не отрицательный индекс - возвращаем результат перемещения заголовка на указанный индекс
//--- В ином случае уже всё готово - просто возвращаем true
return(index>-1 ? this.m_table_header.ColumnCaptionMoveTo(caption_obj.Column(),index) : true);
}
//+------------------------------------------------------------------+
//| Удаляет столбец |
//+------------------------------------------------------------------+
bool CTable::ColumnDelete(const uint index)
{
if(!this.HeaderCheck() || !this.m_table_header.ColumnCaptionDelete(index))
return false;
return this.m_table_model.ColumnDelete(index);
}
//+------------------------------------------------------------------+
//| Перемещает столбец |
//+------------------------------------------------------------------+
bool CTable::ColumnMoveTo(const uint index, const uint index_to)
{
if(!this.HeaderCheck() || !this.m_table_header.ColumnCaptionMoveTo(index,index_to))
return false;
return this.m_table_model.ColumnMoveTo(index,index_to);
}
//+------------------------------------------------------------------+
//| Очищает данные столбца |
//+------------------------------------------------------------------+
void CTable::ColumnClearData(const uint index)
{
if(this.m_table_model!=NULL)
this.m_table_model.ColumnClearData(index);
}
//+------------------------------------------------------------------+
//| Устанавливает значение указанному заголовку |
//+------------------------------------------------------------------+
void CTable::ColumnCaptionSetValue(const uint index,const string value)
{
CColumnCaption *caption=this.m_table_header.GetColumnCaption(index);
if(caption!=NULL)
caption.SetValue(value);
}
//+------------------------------------------------------------------+
//| Устанавливает тип данных для указанного столбца |
//+------------------------------------------------------------------+
void CTable::ColumnSetDatatype(const uint index,const ENUM_DATATYPE type)
{
//--- Если модель таблицы есть - устанавливаем тип данных для столбца
if(this.m_table_model!=NULL)
this.m_table_model.ColumnSetDatatype(index,type);
//--- Если заголовок есть - устанавливаем тип данных для заголовка
if(this.m_table_header!=NULL)
this.m_table_header.ColumnCaptionSetDatatype(index,type);
}
//+------------------------------------------------------------------+
//| Устанавливает точность данных указанному столбцу |
//+------------------------------------------------------------------+
void CTable::ColumnSetDigits(const uint index,const int digits)
{
if(this.m_table_model!=NULL)
this.m_table_model.ColumnSetDigits(index,digits);
}
//+------------------------------------------------------------------+
//| Возвращает тип данных для указанного столбца |
//+------------------------------------------------------------------+
ENUM_DATATYPE CTable::ColumnDatatype(const uint index)
{
return(this.m_table_header!=NULL ? this.m_table_header.ColumnCaptionDatatype(index) : (ENUM_DATATYPE)WRONG_VALUE);
}
//+------------------------------------------------------------------+
//| Возвращает описание объекта |
//+------------------------------------------------------------------+
string CTable::Description(void)
{
return(::StringFormat("%s: Rows total: %u, Columns total: %u",
TypeDescription((ENUM_OBJECT_TYPE)this.Type()),this.RowsTotal(),this.ColumnsTotal()));
}
//+------------------------------------------------------------------+
//| Выводит в журнал описание объекта |
//+------------------------------------------------------------------+
void CTable::Print(const int column_width=CELL_WIDTH_IN_CHARS)
{
if(this.HeaderCheck())
{
//--- Выводим заголовок в виде описания строки
::Print(this.Description()+":");
//--- Количество заголовков
int total=(int)this.ColumnsTotal();
string res="";
//--- создаём строку из значений всех заголовков столбцов таблицы
res="|";
for(int i=0;i<total;i++)
{
CColumnCaption *caption=this.GetColumnCaption(i);
if(caption==NULL)
continue;
res+=::StringFormat("%*s |",column_width,caption.Value());
}
//--- Дополняем строку слева заголовком
string hd="|";
hd+=::StringFormat("%*s ",column_width,"n/n");
res=hd+res;
//--- Выводим строку заголовка в журнал
::Print(res);
}
//--- Пройдём в цикле по всем строкам таблицы и распечатаем их в табличном виде
for(uint i=0;i<this.RowsTotal();i++)
{
CTableRow *row=this.GetRow(i);
if(row!=NULL)
{
//--- создаём строку таблицы из значений всех ячеек
string head=" "+(string)row.Index();
string res=::StringFormat("|%-*s |",column_width,head);
for(int i=0;i<(int)row.CellsTotal();i++)
{
CTableCell *cell=row.GetCell(i);
if(cell==NULL)
continue;
res+=::StringFormat("%*s |",column_width,cell.Value());
}
//--- Выводим строку в журнал
::Print(res);
}
}
}
//+------------------------------------------------------------------+
//| Сохранение в файл |
//+------------------------------------------------------------------+
bool CTable::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_id,INT_VALUE)!=INT_VALUE)
return(false);
//--- Проверяем модель таблицы
if(this.m_table_model==NULL)
return false;
//--- Сохраняем модель таблицы
if(!this.m_table_model.Save(file_handle))
return(false);
//--- Проверяем заголовок таблицы
if(this.m_table_header==NULL)
return false;
//--- Сохраняем заголовок таблицы
if(!this.m_table_header.Save(file_handle))
return(false);
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//| Загрузка из файла |
//+------------------------------------------------------------------+
bool CTable::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_id=::FileReadInteger(file_handle,INT_VALUE);
//--- Проверяем модель таблицы
if(this.m_table_model==NULL && (this.m_table_model=new CTableModel())==NULL)
return(false);
//--- Загружаем модель таблицы
if(!this.m_table_model.Load(file_handle))
return(false);
//--- Проверяем заголовок таблицы
if(this.m_table_header==NULL && (this.m_table_header=new CTableHeader())==NULL)
return false;
//--- Загружаем заголовок таблицы
if(!this.m_table_header.Load(file_handle))
return(false);
//--- Успешно
return true;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс для создания таблиц на основе массива параметров |
//+------------------------------------------------------------------+
class CTableByParam : public CTable
{
public:
virtual int Type(void) const { return(OBJECT_TYPE_TABLE_BY_PARAM); }
//--- Конструктор/деструктор
CTableByParam(void) { this.m_list_rows.Clear(); }
CTableByParam(CList &row_data,const string &column_names[]);
~CTableByParam(void) {}
};
//+------------------------------------------------------------------+
//| Конструктор с указанием массива таблицы на основе списка row_data|
//| содержащего объекты с данными полей структуры. |
//| Определяет количество и наименования колонок согласно количеству |
//| наименований столбцов в массиве column_names |
//+------------------------------------------------------------------+
CTableByParam::CTableByParam(CList &row_data,const string &column_names[])
{
//--- Копируем переданный список данных в переменную и
//--- создаём на основе этого списка модель таблицы
this.m_list_rows=row_data;
this.m_table_model=new CTableModel(this.m_list_rows);
//--- Копируем переданный список заголовков в m_array_names и
//--- создаём на основе этого списка заголовок таблицы
this.ArrayNamesCopy(column_names,column_names.Size());
this.m_table_header=new CTableHeader(this.m_array_names);
}
//+------------------------------------------------------------------+