EasyAndFastMod/Controls/Table.mqh
2026-01-15 07:23:17 -05:00

4028 líneas
338 KiB
MQL5

//+------------------------------------------------------------------+
//| Table.mqh |
//| Copyright 2015, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Element.mqh"
#include "Pointer.mqh"
#include "Scrolls.mqh"
#include "TextEdit.mqh"
#include "ComboBox.mqh"
//+------------------------------------------------------------------+
//| Класс для создания нарисованной таблицы |
//+------------------------------------------------------------------+
class CTable : public CElement
{
private:
//--- Объекты для создания таблицы
CRectCanvas m_headers;
CRectCanvas m_table;
CScrollV m_scrollv;
CScrollH m_scrollh;
CTextEdit m_edit;
CComboBox m_combobox;
CPointer m_column_resize;
//--- Свойства ячеек таблицы
struct CTCell
{
ENUM_TYPE_CELL m_type; // Тип ячейки
CImage m_images[]; // Массив изображений
int m_selected_image; // Индекс выбранной (отображаемой) картинки
string m_full_text; // Полный текст
string m_short_text; // Сокращённый текст
string m_value_list[]; // Массив значений (для ячеек с комбо-боксами)
int m_selected_item; // Выбранный пункт в списке комбо-бокса
color m_text_color; // Цвет текста
color m_back_color; // Цвет фона
uint m_digits; // Количество знаков после запятой
};
//--- Массив строк и свойства столбцов таблицы
struct CTOptions
{
int m_x; // X-координата левого края столбца
int m_x2; // X-координата правого края столбца
int m_width; // Ширина столбца
ENUM_DATATYPE m_data_type; // Тип данных в столбце
ENUM_ALIGN_MODE m_text_align; // Способ выравнивания текста в ячейках столбца
int m_text_x_offset; // Отступ текста
int m_image_x_offset; // Отступ картинки от X-края ячейки
int m_image_y_offset; // Отступ картинки от Y-края ячейки
string m_header_text; // Текст заголовка столбца
CTCell m_rows[]; // Массив строк таблицы
};
CTOptions m_columns[];
//--- Массив свойств строк таблицы
struct CTRowOptions
{
int m_y; // Y-координата верхнего края строки
int m_y2; // Y-координата нижнего края строки
};
CTRowOptions m_rows[];
//--- Количество строк и столбцов
uint m_rows_total;
uint m_columns_total;
//--- Общий размер и размер видимой части таблицы
int m_table_x_size;
int m_table_y_size;
int m_table_visible_x_size;
int m_table_visible_y_size;
//--- Наличие ячеек с полями ввода и комбо-боксами
bool m_edit_state;
bool m_combobox_state;
//--- Индексы столбца и ряда последней редактированной ячейки
int m_last_edit_row_index;
int m_last_edit_column_index;
//--- Минимальная ширина для столбцов
int m_min_column_width;
//--- Значения по умолчанию: (1) ширина, (2) тип данных, (3) выравнивание текста
int m_default_width;
ENUM_DATATYPE m_default_type_data;
ENUM_ALIGN_MODE m_default_text_align;
//--- Цвет сетки
color m_grid_color;
//--- Режим показа заголовков таблицы
bool m_show_headers;
//--- Размер (высота) заголовков
int m_header_y_size;
//--- Цвет заголовков (фон) в разных состояниях
color m_headers_color;
color m_headers_color_hover;
color m_headers_color_pressed;
//--- Цвет текста заголовков
color m_headers_text_color;
//--- Ярлыки для признака отсортированных данных
CImage m_sort_arrows[];
//--- Отступы для ярлыка-признака отсортированных данных
int m_sort_arrow_x_gap;
int m_sort_arrow_y_gap;
//--- Размер (высота) ячеек
int m_cell_y_size;
//--- Цвет ячеек в разных состояниях
color m_cell_color;
color m_cell_color_hover;
//--- Цвет (1) фона и (2) текста выделенной строки
color m_selected_row_color;
color m_selected_row_text_color;
//--- (1) Индекс и (2) текст выделенной строки
int m_selected_item;
string m_selected_item_text;
//--- Индекс предыдущего выделенной строки
int m_prev_selected_item;
//--- Отступ от границ разделительных линий для показа указателя мыши в режиме изменения ширины столбцов
int m_sep_x_offset;
//--- Режим подсветки строки при наведении курсора мыши
bool m_lights_hover;
//--- Режим сортируемых данных по столбцам
bool m_is_sort_mode;
//--- Индекс отсортированного столбца (WRONG_VALUE - таблица не отсортирована)
int m_is_sorted_column_index;
//--- Последнее направление сортировки
ENUM_CSORT_MODE m_last_sort_direction;
//--- Режим выделяемой строки
bool m_selectable_row;
//--- Без отмены выделения строки при повторном нажатии
bool m_is_without_deselect;
//--- Режим форматирования в стиле "Зебра"
color m_is_zebra_format_rows;
//--- Состояние левой кнопки мыши (зажата/отжата)
bool m_mouse_state;
//--- Счётчик таймера для перемотки списка
int m_timer_counter;
//--- Для определения фокуса строки
int m_item_index_focus;
//--- Для определение момента перехода курсора мыши с одной строки на другую
int m_prev_item_index_focus;
//--- Для определение момента перехода курсора мыши с одного заголовка на другой
int m_prev_header_index_focus;
//--- Авторазмер столбцов
bool m_autoresize_columns;
//--- Режим авто-ширина столбцов (по максимальной ширине текста в столбце)
bool m_auto_correct_columns_width_mode;
//--- Режим изменения ширины столбцов
bool m_column_resize_mode;
//--- Состояние захвата границы заголовка для изменения ширины столбца
int m_column_resize_control;
//--- Вспомогательные поля для расчётов в изменении ширины столбцов
int m_column_resize_x_fixed;
int m_column_resize_prev_width;
int m_column_resize_prev_thumb;
//--- Для определения индексов видимой части таблицы
uint m_visible_table_from_index;
uint m_visible_table_to_index;
//--- Размер шага для смещения по горизонтали
int m_shift_x_step;
//--- Ограничения на смещение
int m_shift_x2_limit;
int m_shift_y2_limit;
//--- Отключение показа полос прокрутки
bool m_is_disabled_scrolls;
//---
public:
CTable(void);
~CTable(void);
//--- Методы для создания таблицы
bool CreateTable(const int x_gap,const int y_gap);
//---
private:
void InitializeProperties(const int x_gap,const int y_gap);
bool CreateCanvas(void);
bool CreateHeaders(void);
bool CreateTable(void);
bool CreateScrollV(void);
bool CreateScrollH(void);
bool CreateEdit(void);
bool CreateCombobox(void);
bool CreateColumnResizePointer(void);
//---
public:
//--- Возвращает указатели на элементы
CScrollV *GetScrollVPointer(void) { return(::GetPointer(m_scrollv)); }
CScrollH *GetScrollHPointer(void) { return(::GetPointer(m_scrollh)); }
CTextEdit *GetTextEditPointer(void) { return(::GetPointer(m_edit)); }
CComboBox *GetComboboxPointer(void) { return(::GetPointer(m_combobox)); }
//--- Возвращает наличие элементов (поле ввода, комбо-бокс) в ячейках таблицы
bool HasEditElements(void) const { return(m_edit_state); }
bool HasComboboxElements(void) const { return(m_combobox_state); }
//--- Цвет (1) сетки и (2) ячеек таблицы
void GridColor(const color clr) { m_grid_color=clr; }
void CellColor(const color clr) { m_cell_color=clr; }
void CellColorHover(const color clr) { m_cell_color_hover=clr; }
//--- (1) Режим показа заголовков, высота (2) заголовков и (3) ячеек, (4) отключение полос прокрутки
void ShowHeaders(const bool flag) { m_show_headers=flag; }
void HeaderYSize(const int y_size) { m_header_y_size=y_size; }
void CellYSize(const int y_size) { m_cell_y_size=y_size; }
void IsDisabledScrolls(const bool flag) { m_is_disabled_scrolls=flag; }
//--- Цвета (1) фона и (2) текста заголовков
void HeadersColor(const color clr) { m_headers_color=clr; }
void HeadersColorHover(const color clr) { m_headers_color_hover=clr; }
void HeadersColorPressed(const color clr) { m_headers_color_pressed=clr; }
void HeadersTextColor(const color clr) { m_headers_text_color=clr; }
//--- Отступы для признака отсортированной таблицы
void SortArrowXGap(const int x_gap) { m_sort_arrow_x_gap=x_gap; }
void SortArrowYGap(const int y_gap) { m_sort_arrow_y_gap=y_gap; }
//--- Установка картинок для признака отсортированных данных
void SortArrowFileAscend(const string path) { m_sort_arrows[0].BmpPath(path); }
void SortArrowFileDescend(const string path) { m_sort_arrows[1].BmpPath(path); }
//--- Возвращает общее количество (1) рядов и (2) столбцов, (3) количество строк видимой части таблицы
uint RowsTotal(void) const { return(m_rows_total); }
uint ColumnsTotal(void) const { return(m_columns_total); }
int VisibleRowsTotal(void);
//--- Возвращает (1) индекс и (2) текст выделенного ряда в таблице
int SelectedItem(void) const { return(m_selected_item); }
string SelectedItemText(void) const { return(m_selected_item_text); }
//--- Режимы (1) подсветка строк при наведении курсора мыши, (2) режим сортируемых данных
void LightsHover(const bool flag) { m_lights_hover=flag; }
void IsSortMode(const bool flag) { m_is_sort_mode=flag; }
//--- Режимы (1) выделение строки, (2) без отмены выделения при повторном нажатии
void SelectableRow(const bool flag) { m_selectable_row=flag; }
void IsWithoutDeselect(const bool flag) { m_is_without_deselect=flag; }
//--- (1) Формат строк типа "Зебра", (2) режим изменения ширины столбцов, (3) режим "Авторазмер столбцов", (4) режим автокоррекции ширины столбцов
void IsZebraFormatRows(const color clr) { m_is_zebra_format_rows=clr; }
void ColumnResizeMode(const bool flag) { m_column_resize_mode=flag; }
void AutoResizeColumnsMode(const bool flag) { m_autoresize_columns=flag; }
void AutoCorrectColumnsWidthMode(const bool flag) { m_auto_correct_columns_width_mode=flag; }
//--- Авторазмер столбцов
void AutoResizeColumns(void);
void AutoCorrectWidthColumns(void);
//--- Процесс изменения ширины столбцов
bool ColumnResizeControl(void) const { return(m_column_resize_control!=WRONG_VALUE); }
//--- Возвращает количество картинок в указанной ячейке
int ImagesTotal(const uint column_index,const uint row_index);
//--- Минимальная ширина столбцов
void MinColumnWidth(const int width);
//--- Значения по умолчанию: (1) ширина, (2) тип данных, (3) выравнивание текста
void DefaultWidth(const int width) { m_default_width =width; }
void DefaultTypeData(const ENUM_DATATYPE type) { m_default_type_data =type; }
void DefaultTextAlign(const ENUM_ALIGN_MODE align) { m_default_text_align =align; }
//--- Устанавливает основной размер таблицы
void TableSize(const int columns_total,const int rows_total,const bool init=true);
//--- Реконструкция таблицы
void Rebuilding(const int columns_total,const int rows_total,const bool redraw=false);
//--- Добавляет столбец в таблицу по указанному индексу
void AddColumn(const int column_index,const bool redraw=false);
//--- Удаляет столбец в таблице по указанному индексу
void DeleteColumn(const int column_index,const bool redraw=false);
//--- Добавляет строку в таблицу по указанному индексу
void AddRow(const int row_index,const bool redraw=false);
//--- Удаляет строку в таблице по указанному индексу
void DeleteRow(const int row_index,const bool redraw=false);
//--- Удаляет все строки
void DeleteAllRows(const bool redraw=false);
//--- Очищает таблицу. Остаётся только один столбец и одна строка.
void Clear(const bool redraw=false);
//--- (1) Установка текста в указанный заголовок, (2) получение текста указанного заголовка, (3) получение заголовков в переданный массив
void SetHeaderText(const uint column_index,const string value);
string GetHeaderText(const uint column_index);
uint GetHeadersText(string &headers[]);
//--- Установка (1) режима выравнивания текста, (2) отступ текста в ячейке по оси X и (3) ширины для каждого столбца
void TextAlign(const ENUM_ALIGN_MODE &array[]);
void TextAlign(const uint column_index,const ENUM_ALIGN_MODE align);
void TextXOffset(const int &array[]);
void ColumnsWidth(const int &array[]);
//--- Смещение картинок по X- и Y-осям
void ImageXOffset(const int &array[]);
void ImageYOffset(const int &array[]);
//--- Установка/получение типа данных
void DataType(const uint column_index,const ENUM_DATATYPE type);
ENUM_DATATYPE DataType(const uint column_index);
//--- Установка/получение типа ячейки
void CellType(const uint column_index,const uint row_index,const ENUM_TYPE_CELL type);
ENUM_TYPE_CELL CellType(const uint column_index,const uint row_index);
//--- Устанавливает картинки в указанную ячейку (путь к ресурсу)
void SetImages(const uint column_index,const uint row_index,const string &bmp_file_path[]);
//--- Устанавливает картинки в указанную ячейку (индекс к ресурсу)
void SetImages(const uint column_index,const uint row_index,const uint &resource_index[]);
//--- (1) Изменяет/получает (индекс) картинку в указанной ячейке, (2) возвращает индекс текущей картинки, (3) возвращает индекс выбранного пункта в списке комбо-бокса
void ChangeImage(const uint column_index,const uint row_index,const uint image_index,const bool redraw=false);
int SelectedImageIndex(const uint column_index,const uint row_index);
int SelectedComboboxItemIndex(const uint column_index,const uint row_index);
//--- Устанавливает цвет текста в указанную ячейку таблицы
void TextColor(const uint column_index,const uint row_index,const color clr,const bool redraw=false);
//--- (1) Устанавливает и (2) возвращает цвет фона в/из указанную-(ой) ячейку-(и) таблицы
void BackColor(const uint column_index,const uint row_index,const color clr,const bool redraw=false);
color BackColor(const uint column_index,const uint row_index);
//--- Устанавливает/получает значение в указанной ячейке таблицы
void SetValue(const uint column_index,const uint row_index,const string value="",const uint digits=0,const bool redraw=false);
string GetValue(const uint column_index,const uint row_index);
//--- Выделение строки в таблице
void SelectRow(const int row_index);
//--- Снятие выделения
void DeselectRow(void) { m_selected_item=WRONG_VALUE; }
//--- Добавить список значений в комбо-бокс
void AddValueList(const uint column_index,const uint row_index,const string &array[],const uint selected_item=0);
//--- Прокрутка таблицы: (1) вертикальная и (2) горизонтальная
void VerticalScrolling(const int pos=WRONG_VALUE);
void HorizontalScrolling(const int pos=WRONG_VALUE);
//--- Смещение таблицы относительно позиций полос прокрутки
void ShiftTable(void);
//--- Сортировать данные по указанному столбцу
void SortData(const uint column_index=0,const int direction=WRONG_VALUE);
//--- (1) Текущее направление сортировки, (2) индекс отсортированного массива
int IsSortDirection(void) const { return(m_last_sort_direction); }
int IsSortedColumnIndex(void) const { return(m_is_sorted_column_index); }
//--- Сброс сортировки
void ResetSort(void) { m_is_sorted_column_index=WRONG_VALUE; }
//---
public:
//--- Изменение положения таблицы по оси X
void MovingX(const int x_gap);
//---
public:
//--- Обработчик событий графика
virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
//--- Таймер
virtual void OnEventTimer(void);
//--- Перемещение элемента
virtual void Moving(const bool only_visible=true);
//--- Управление
virtual void Show(void);
virtual void Hide(void);
virtual void Delete(void);
//--- (1) Установка, (2) сброс приоритетов на нажатие левой кнопки мыши
virtual void SetZorders(void);
virtual void ResetZorders(void);
//--- Обновление элемента
virtual void Update(const bool redraw=false);
//---
private:
//--- Обработка нажатия на заголовке
bool OnClickHeaders(const string clicked_object);
//--- Обработка нажатия на таблице
bool OnClickTable(const string clicked_object);
//--- Обработка двойного нажатия на таблице
bool OnDoubleClickTable(const string clicked_object);
//--- Обработка окончания ввода значения в ячейку
bool OnEndEditCell(const int id);
//--- Обработка выбора пункта в выпадающем списке ячейки
bool OnClickComboboxItem(const int id);
//--- Обработка выделения ряда
bool OnSelectRow(const int row_index);
//--- Проверка элементов в ячейках на скрытие
void CheckAndHideEdit(void);
void CheckAndHideCombobox(void);
//--- Возвращает индекс нажатой строки
int PressedRowIndex(void);
//--- Возвращает индекс столбца нажатой ячейки
int PressedCellColumnIndex(void);
//--- Проверяет, был ли задействован элемент в ячейке при нажатии
bool CheckCellElement(const int column_index,const int row_index,const bool double_click=false);
//--- Проверяет, было ли нажатие на кнопке в ячейке
bool CheckPressedButton(const int column_index,const int row_index,const bool double_click=false);
//--- Проверяет, было ли нажатие на чекбоксе в ячейке
bool CheckPressedCheckBox(const int column_index,const int row_index,const bool double_click=false);
//--- Проверяет, было ли нажатие на ячейке с полем ввода
bool CheckPressedEdit(const int column_index,const int row_index,const bool double_click=false);
//--- Проверяет, было ли нажатие на ячейке с комбо-боксом
bool CheckPressedCombobox(const int column_index,const int row_index,const bool double_click=false);
//---
private:
//--- Метод быстрой сортировки
void QuickSort(uint beg,uint end,uint column,const ENUM_CSORT_MODE mode=SORT_ASCEND);
//--- Проверка условия сортировки
bool CheckSortCondition(uint column_index,uint row_index,const string check_value,const bool direction);
//--- Поменять значения в указанных ячейках местами
void Swap(uint r1,uint r2);
//--- Рассчитывает размеры таблицы
void CalculateTableSize(void);
//--- Рассчитывают полный размер таблицы по оси X и Y
void CalculateTableXSize(void);
void CalculateTableYSize(void);
//--- Рассчитывают видимый размер таблицы по оси X и Y
void CalculateTableVisibleXSize(void);
void CalculateTableVisibleYSize(void);
//--- Изменить основные размеры таблицы
void ChangeMainSize(const int x_size,const int y_size);
//--- Изменить размеры таблицы
void ChangeTableSize(void);
//--- Изменить размеры полос прокрутки
void ChangeScrollsSize(void);
//--- Определение индексов видимой области таблицы
void VisibleTableIndexes(void);
//---
private:
//--- Возвращает текст
string Text(const int column_index,const int row_index);
//--- Возвращает X-координату текста в указанном столбце
int TextX(const int column_index,const bool headers=false);
//--- Возвращает способ выравнивания текста в указанном столбце
uint TextAlign(const int column_index,const uint anchor);
//--- Возвращает цвет текста ячейки
uint TextColor(const int column_index,const int row_index);
//--- Возвращает цвет фона ячейки
uint BackColor(const int column_index,const int row_index);
//--- Возвращает текущий цвет фона заголовка
uint HeaderColorCurrent(const bool is_header_focus);
//--- Возвращает текущий цвет фона строки
uint RowColorCurrent(const int column_index,const int row_index,const bool is_row_focus);
//--- Рисует элемент
void Draw(void);
//--- Рисует таблицу с учётом последних внесённых изменений
void DrawTable(const bool only_visible=false);
//--- Рисует заголовки таблицы
void DrawTableHeaders(void);
//--- Рисует заголовки
void DrawHeaders(void);
//--- Рисует сетку заголовков таблицы
void DrawHeadersGrid(void);
//--- Рисует признак возможности сортировки таблицы
void DrawSignSortedData(void);
//--- Рисует текст заголовков таблицы
void DrawHeadersText(void);
//--- Рисует фон строк таблицы
void DrawRows(void);
//--- Рисует выделенную строку
void DrawSelectedRow(void);
//--- Рисует сетку
void DrawGrid(void);
//--- Рисует все изображения таблицы
void DrawImages(void);
//--- Рисует изображение в указанной ячейке
void DrawImage(const int column_index,const int row_index);
//--- Рисует текст
void DrawText(void);
//--- Перерисовывает указанную ячейку таблицы
void RedrawCell(const int column_index,const int row_index);
//--- Рисует указанный ряд таблицы по указанному режиму
void DrawRow(int &indexes[],const int item_index,const int prev_item_index,const bool is_user=true);
//--- Перерисовывает указанный ряд таблицы по указанному режиму
void RedrawRow(const bool is_selected_row=false);
//---
private:
//--- Проверка фокуса на заголовках
void CheckHeaderFocus(void);
//--- Проверка фокуса на строках таблицы
int CheckRowFocus(void);
//--- Проверка фокуса на границах заголовков для изменения их ширины
void CheckColumnResizeFocus(void);
//--- Изменяет ширину захваченного столбца
void ChangeColumnWidth(void);
//--- Проверяет размер переданного массива и возвращает скорректированное значение
template<typename T>
int CheckArraySize(const T &array[]);
//--- Проверить выход из диапазона столбцов
bool CheckOutOfColumnRange(const uint column_index);
//--- Проверить выход из диапазона столбцов и строк
virtual bool CheckOutOfRange(const uint column_index,const uint row_index);
//--- Расчёт с учётом последних изменений и изменение размеров таблицы
void RecalculateAndResizeTable(const bool redraw=false);
//--- Инициализация указанного столбца значениями по умолчанию
void ColumnInitialize(const uint column_index);
//--- Инициализация указанной ячейки значениями по умолчанию
void CellInitialize(const uint column_index,const uint row_index);
//--- Делает копию указанного столбца (source) в новое место (dest.)
void ColumnCopy(const uint destination,const uint source);
//--- Делает копию указанной ячейки (source) в новое место (dest.)
void CellCopy(const uint column_dest,const uint row_dest,const uint column_source,const uint row_source);
//--- Копирует данные изображения из одного массива в другой
void ImageCopy(CImage &destination[],CImage &source[],const int index);
//---
private:
//--- Изменяет цвет объектов таблицы
void ChangeObjectsColor(void);
//--- Изменяет цвет заголовков при наведении курсора мыши
void ChangeHeadersColor(void);
//--- Изменение цвета строк при наведении курсора мыши
void ChangeRowsColor(void);
//--- Возвращает откорректированный текст по ширине столбца
string CorrectingText(const int column_index,const int row_index,const bool headers=false);
//--- Ускоренная перемотка таблицы
void FastSwitching(void);
//--- Изменить ширину по правому краю окна
virtual void ChangeWidthByRightWindowSide(void);
//--- Изменить высоту по нижнему краю окна
virtual void ChangeHeightByBottomWindowSide(void);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CTable::CTable(void) : m_rows_total(1),
m_columns_total(1),
m_edit_state(false),
m_combobox_state(false),
m_last_edit_row_index(WRONG_VALUE),
m_last_edit_column_index(WRONG_VALUE),
m_shift_x_step(10),
m_shift_x2_limit(0),
m_shift_y2_limit(0),
m_show_headers(false),
m_header_y_size(20),
m_cell_y_size(20),
m_default_text_align(ALIGN_CENTER),
m_default_width(100),
m_default_type_data(TYPE_STRING),
m_min_column_width(30),
m_grid_color(clrLightGray),
m_headers_color(C'255,244,213'),
m_headers_color_hover(C'229,241,251'),
m_headers_color_pressed(C'204,228,247'),
m_headers_text_color(clrBlack),
m_is_disabled_scrolls(false),
m_is_sort_mode(false),
m_last_sort_direction(SORT_ASCEND),
m_is_sorted_column_index(WRONG_VALUE),
m_sort_arrow_x_gap(10),
m_sort_arrow_y_gap(8),
m_cell_color(clrWhite),
m_cell_color_hover(C'229,243,255'),
m_prev_selected_item(WRONG_VALUE),
m_selected_item(WRONG_VALUE),
m_selected_item_text(""),
m_sep_x_offset(5),
m_lights_hover(false),
m_selectable_row(false),
m_is_without_deselect(false),
m_autoresize_columns(false),
m_auto_correct_columns_width_mode(false),
m_column_resize_mode(false),
m_column_resize_control(WRONG_VALUE),
m_column_resize_x_fixed(0),
m_column_resize_prev_width(0),
m_column_resize_prev_thumb(0),
m_item_index_focus(WRONG_VALUE),
m_prev_item_index_focus(WRONG_VALUE),
m_prev_header_index_focus(WRONG_VALUE),
m_selected_row_color(C'51,153,255'),
m_selected_row_text_color(clrWhite),
m_is_zebra_format_rows(clrNONE),
m_visible_table_from_index(WRONG_VALUE),
m_visible_table_to_index(WRONG_VALUE)
{
//--- Сохраним имя класса элемента в базовом классе
CElementBase::ClassName(CLASS_NAME);
//--- Цвет текста по умолчанию
m_label_color=clrBlack;
//--- Установим размер таблицы
TableSize(m_columns_total,m_rows_total);
//--- Инициализация структуры признака сортировки
::ArrayResize(m_sort_arrows,2);
m_sort_arrows[0].BmpPath("");
m_sort_arrows[1].BmpPath("");
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTable::~CTable(void)
{
}
//+------------------------------------------------------------------+
//| Обработчик событий |
//+------------------------------------------------------------------+
void CTable::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Обработка события перемещения курсора
if(id==CHARTEVENT_MOUSE_MOVE)
{
//--- Если полоса прокрутки в действии
if(m_scrollv.ScrollBarControl())
{
ShiftTable();
m_scrollv.Update(true);
return;
}
//--- Если полоса прокрутки в действии
if(m_scrollh.ScrollBarControl())
{
ShiftTable();
m_scrollh.Update(true);
return;
}
//--- Выйти, если полоса прокрутки активирована
if(m_scrollh.State() || m_scrollv.State())
return;
//--- Проверка фокуса над элементами
m_headers.MouseFocus(m_mouse.X()>m_headers.X() && m_mouse.X()<m_headers.X2() &&
m_mouse.Y()>m_headers.Y() && m_mouse.Y()<m_headers.Y2());
m_table.MouseFocus(m_mouse.X()>m_table.X() && m_mouse.X()<m_table.X2() &&
m_mouse.Y()>m_table.Y() && m_mouse.Y()<m_table.Y2());
//--- Изменение цвета объектов
ChangeObjectsColor();
//--- Изменить ширину захваченного столбца
ChangeColumnWidth();
return;
}
//--- Обработка события колёсика мыши
if(id==CHARTEVENT_MOUSE_WHEEL)
{
//--- Если курсор в таблице
if(m_table.MouseFocus())
{
//--- Получим текущую позицию полосы прокрутки
int pos=(m_scrollv.CurrentPos()-1<0)? 1 : m_scrollv.CurrentPos();
//--- Если колёсико мыши сместилось вниз
if(dparam<0)
VerticalScrolling(pos+1);
//--- Если колёсико мыши сместилось вверх
else if(dparam>0)
VerticalScrolling(pos-1);
//--- Обновить полосу прокрутки
m_scrollv.Update(true);
}
return;
}
//--- Обработка нажатия на объектах
if(id==CHARTEVENT_OBJECT_CLICK)
{
//--- Нажатие на заголовке
if(OnClickHeaders(sparam))
return;
//--- Нажатие на таблице
if(OnClickTable(sparam))
return;
//---
return;
}
//--- Обработка события окончания ввода
if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
{
if(OnEndEditCell((int)lparam))
return;
//---
return;
}
//--- Обработка события выбора пункта в списке
if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
{
if(OnClickComboboxItem((int)lparam))
return;
//---
return;
}
//--- Обработка события нажатия на кнопках полосы прокрутки
if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
{
//--- Выйти, если это нажатие на кнопке комбо-бокса
if(m_combobox.CheckElementName(sparam))
return;
//--- Выйти, если это нажатие не на кнопке полосы прокрутки
if(!m_scrollv.GetIncButtonPointer().CheckElementName(sparam))
return;
//--- Если было нажатие на кнопках вертикальной полосы прокрутки
if(m_scrollv.OnClickScrollInc((uint)lparam,(uint)dparam) ||
m_scrollv.OnClickScrollDec((uint)lparam,(uint)dparam))
{
//--- Сдвигает данные
ShiftTable();
m_scrollv.Update(true);
return;
}
//--- Если было нажатие на кнопках горизонтальной полосы прокрутки списка
if(m_scrollh.OnClickScrollInc((uint)lparam,(uint)dparam) ||
m_scrollh.OnClickScrollDec((uint)lparam,(uint)dparam))
{
//--- Сдвигает данные
ShiftTable();
m_scrollh.Update(true);
return;
}
}
//--- Изменение состояния левой кнопки мыши
if(id==CHARTEVENT_CUSTOM+ON_CHANGE_MOUSE_LEFT_BUTTON)
{
//--- Проверка поля ввода в ячейках на скрытие
CheckAndHideEdit();
//--- Проверка комбо-бокса в ячейках на скрытие
CheckAndHideCombobox();
//--- Выйти, если заголовки отключены
if(!m_show_headers)
return;
//--- Если левая кнопка мыши отжата
if(m_column_resize_control!=WRONG_VALUE && !m_mouse.LeftButtonState())
{
//--- Сбросить режим изменения ширины
m_column_resize_control=WRONG_VALUE;
//--- Перерисовать таблицу
DrawTable();
Update();
//--- Скрыть указатель
m_column_resize.Hide();
//--- Отправим сообщение на определение доступных элементов
::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),1,"");
//--- Отправим сообщение об изменении в графическом интерфейсе
::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
}
//--- Сбросить индекс последнего фокуса заголовка
m_prev_header_index_focus=WRONG_VALUE;
//--- Изменение цвета объектов
ChangeObjectsColor();
return;
}
//--- Обработка двойного нажатия левой кнопки мыши
if(id==CHARTEVENT_CUSTOM+ON_DOUBLE_CLICK)
{
//--- Выйти, если комбо-бокс есть и показан
if(m_combobox_state && m_combobox.IsVisible())
return;
//--- Нажатие на таблице
if(OnDoubleClickTable(sparam))
return;
//---
return;
}
}
//+------------------------------------------------------------------+
//| Таймер |
//+------------------------------------------------------------------+
void CTable::OnEventTimer(void)
{
//--- Ускоренная перемотка значений
FastSwitching();
}
//+------------------------------------------------------------------+
//| Создаёт нарисованную таблицу |
//+------------------------------------------------------------------+
bool CTable::CreateTable(const int x_gap,const int y_gap)
{
//--- Выйти, если нет указателя на главный элемент
if(!CElement::CheckMainPointer())
return(false);
//--- Инициализация свойств
InitializeProperties(x_gap,y_gap);
//--- Рассчитаем размеры таблицы
CalculateTableSize();
//--- Создание элемента
if(!CreateCanvas())
return(false);
if(!CreateTable())
return(false);
if(!CreateHeaders())
return(false);
if(!CreateScrollV())
return(false);
if(!CreateScrollH())
return(false);
if(!CreateEdit())
return(false);
if(!CreateCombobox())
return(false);
if(!CreateColumnResizePointer())
return(false);
//--- Изменить размеры таблицы
ChangeTableSize();
return(true);
}
//+------------------------------------------------------------------+
//| Изменение положения таблицы по оси X |
//+------------------------------------------------------------------+
void CTable::MovingX(const int x_gap)
{
m_x=CElement::CalculateX(x_gap);
CElementBase::XGap(x_gap);
//---
m_canvas.X(m_x);
m_canvas.XGap(CElement::CalculateXGap(m_x));
//---
int x=m_x+1;
m_table.X(x);
m_table.XGap(CElement::CalculateXGap(x));
//---
m_headers.X(x);
m_headers.XGap(CElement::CalculateXGap(x));
//---
CElement::Moving();
}
//+------------------------------------------------------------------+
//| Инициализация свойств |
//+------------------------------------------------------------------+
void CTable::InitializeProperties(const int x_gap,const int y_gap)
{
m_x =CElement::CalculateX(x_gap);
m_y =CElement::CalculateY(y_gap);
m_x_size =(m_x_size<1 || m_auto_xresize_mode)? (m_anchor_right_window_side)? m_main.X2()-m_x-m_auto_xresize_right_offset : m_main.X2()-m_x-m_auto_xresize_right_offset : m_x_size;
m_y_size =(m_y_size<1 || m_auto_yresize_mode)? (m_anchor_bottom_window_side)? m_main.Y2()-m_y-m_auto_yresize_bottom_offset : m_main.Y2()-m_y-m_auto_yresize_bottom_offset : m_y_size;
//--- Свойства по умолчанию
m_back_color =(m_back_color!=clrNONE)? m_back_color : clrWhite;
m_label_color =(m_label_color!=clrNONE)? m_label_color : clrBlack;
m_label_color_hover =(m_label_color_hover!=clrNONE)? m_label_color_hover : clrBlack;
m_label_color_pressed =(m_label_color_pressed!=clrNONE)? m_label_color_pressed : clrWhite;
m_border_color =(m_border_color!=clrNONE)? m_border_color : C'150,170,180';
m_icon_x_gap =(m_icon_x_gap>0)? m_icon_x_gap : 3;
m_icon_y_gap =(m_icon_y_gap>0)? m_icon_y_gap : 2;
m_label_x_gap =(m_label_x_gap>0)? m_label_x_gap : 5;
m_label_y_gap =(m_label_y_gap>0)? m_label_y_gap : 4;
//--- Отступы от крайней точки
CElementBase::XGap(x_gap);
CElementBase::YGap(y_gap);
}
//+------------------------------------------------------------------+
//| Создаёт фон для таблицы |
//+------------------------------------------------------------------+
bool CTable::CreateCanvas(void)
{
//--- Формирование имени объекта
string name=CElementBase::ElementName("table");
//--- Создание объекта
if(!CElement::CreateCanvas(name,m_x,m_y,m_x_size,m_y_size))
return(false);
//---
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт таблицу |
//+------------------------------------------------------------------+
bool CTable::CreateTable(void)
{
//--- Формирование имени объекта
string name=CElementBase::ProgramName()+"_"+"table_grid"+"_"+(string)CElementBase::Id();
//--- Координаты
int x =m_x+1;
int y =m_y+((m_show_headers)? m_header_y_size : 1);
//--- Создание объекта
::ResetLastError();
if(!m_table.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,m_table_x_size,m_header_y_size,COLOR_FORMAT_ARGB_NORMALIZE))
{
::Print(__FUNCTION__," > Не удалось создать холст для рисования таблицы: ",::GetLastError());
return(false);
}
//--- Получим указатель на базовый класс
if(!m_table.Attach(m_chart_id,name,COLOR_FORMAT_ARGB_NORMALIZE))
{
::Print(__FUNCTION__," > Не удалось присоединить холст для рисования к графику: ",::GetLastError());
return(false);
}
//--- Свойства
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_ZORDER,m_zorder+1);
::ObjectSetString(m_chart_id,m_table.ChartObjectName(),OBJPROP_TOOLTIP,"\n");
//--- Координаты
m_table.X(x);
m_table.Y(y);
//--- Сохраним размеры
m_table.XSize(m_table_visible_x_size);
m_table.YSize(m_table_visible_y_size);
//--- Отступы от крайней точки панели
m_table.XGap(CElement::CalculateXGap(x));
m_table.YGap(CElement::CalculateYGap(y));
//--- Установим размеры видимой области
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_XSIZE,m_table_visible_x_size);
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_YSIZE,m_table_visible_y_size);
//--- Зададим смещение фрейма внутри изображения по осям X и Y
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_XOFFSET,0);
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_YOFFSET,0);
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт заголовки таблицы |
//+------------------------------------------------------------------+
bool CTable::CreateHeaders(void)
{
//--- Выйти, если заголовки отключены
if(!m_show_headers)
return(true);
//--- Формирование имени объекта
string name=CElementBase::ProgramName()+"_"+"table_headers"+"_"+(string)CElementBase::Id();
//--- Координаты
int x =m_x+1;
int y =m_y+1;
//--- Определим картинки, как признак возможности сортировки таблицы
::ArrayResize(m_sort_arrows,2);
if(m_sort_arrows[0].ResourceIndex()==INT_MAX)
m_sort_arrows[0].ResourceIndex(RESOURCE_SPIN_INC);
if(m_sort_arrows[1].ResourceIndex()==INT_MAX)
m_sort_arrows[1].ResourceIndex(RESOURCE_SPIN_DEC);
//--- Сохранить картинки в массивы
for(int i=0; i<2; i++)
m_sort_arrows[i].ReadImageData(m_sort_arrows[i].ResourceIndex());
//--- Создание объекта
::ResetLastError();
if(!m_headers.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,m_table_x_size,m_header_y_size,COLOR_FORMAT_ARGB_NORMALIZE))
{
::Print(__FUNCTION__," > Не удалось создать холст для рисования заголовков таблицы: ",::GetLastError());
return(false);
}
//--- Получим указатель на базовый класс
if(!m_headers.Attach(m_chart_id,name,COLOR_FORMAT_ARGB_NORMALIZE))
{
::Print(__FUNCTION__," > Не удалось присоединить холст для рисования к графику: ",::GetLastError());
return(false);
}
//--- Свойства
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_ZORDER,m_zorder+1);
::ObjectSetString(m_chart_id,m_headers.ChartObjectName(),OBJPROP_TOOLTIP,"\n");
//--- Координаты
m_headers.X(x);
m_headers.Y(y);
//--- Сохраним размеры
m_headers.XSize(m_table_visible_x_size);
m_headers.YSize(m_header_y_size);
//--- Отступы от крайней точки панели
m_headers.XGap(CElement::CalculateXGap(x));
m_headers.YGap(CElement::CalculateYGap(y));
//--- Установим размеры видимой области
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_XSIZE,m_table_visible_x_size);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_YSIZE,m_header_y_size);
//--- Зададим смещение фрейма внутри изображения по осям X и Y
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_XOFFSET,0);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_YOFFSET,0);
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт вертикальный скролл |
//+------------------------------------------------------------------+
bool CTable::CreateScrollV(void)
{
//--- Сохранить указатель родителя
m_scrollv.MainPointer(this);
//--- Координаты
int x=16,y=1;
//--- Свойства
m_scrollv.Index(0);
m_scrollv.XSize(15);
m_scrollv.YSize(CElementBase::YSize()-2);
m_scrollv.AnchorRightWindowSide(true);
m_scrollv.Alpha(m_alpha);
//--- Расчёт количества шагов для смещения
uint rows_total =RowsTotal();
uint visible_rows_total =VisibleRowsTotal();
//--- Создание полосы прокрутки
if(!m_scrollv.CreateScroll(x,y,rows_total,visible_rows_total))
return(false);
//--- Скрыть, если сейчас не нужна
if(!m_scrollv.IsScroll() || m_is_disabled_scrolls)
m_scrollv.Hide();
//--- Добавить элемент в массив
CElement::AddToArray(m_scrollv);
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт горизонтальный скролл |
//+------------------------------------------------------------------+
bool CTable::CreateScrollH(void)
{
//--- Сохранить указатель на главный элемент
m_scrollh.MainPointer(this);
//--- Координаты
int x=1,y=16;
//--- Свойства
m_scrollh.Index(1);
m_scrollh.XSize(CElementBase::XSize()-2);
m_scrollh.YSize(15);
m_scrollh.AnchorBottomWindowSide(true);
m_scrollh.Alpha(m_alpha);
//--- Расчёт количества шагов для смещения
uint x_size_total =m_table_x_size/m_shift_x_step;
uint visible_x_size_total =m_table_visible_x_size/m_shift_x_step;
//--- Создание полосы прокрутки
if(!m_scrollh.CreateScroll(x,y,x_size_total,visible_x_size_total))
return(false);
//--- Скрыть, если сейчас не нужна
if(!m_scrollh.IsScroll() || m_is_disabled_scrolls)
m_scrollh.Hide();
//--- Добавить элемент в массив
CElement::AddToArray(m_scrollh);
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт поле ввода |
//+------------------------------------------------------------------+
bool CTable::CreateEdit(void)
{
//--- Если нет ячеек с полем ввода
if(!m_edit_state)
return(true);
//--- Сохраним указатель на главный элемент
m_edit.MainPointer(this);
//--- Координаты
int x=-1,y=0;
//--- Свойства
m_edit.Alpha(0);
m_edit.XSize(50);
m_edit.YSize(21);
m_edit.SetValue("");
m_edit.GetTextBoxPointer().XGap(1);
m_edit.GetTextBoxPointer().XSize(50);
m_edit.GetTextBoxPointer().TextYOffset(4);
m_edit.GetTextBoxPointer().AutoSelectionMode(true);
//--- Создадим элемент управления
if(!m_edit.CreateTextEdit("",x,y))
return(false);
//--- Скрыть
m_edit.Hide();
//--- Добавить элемент в массив
CElement::AddToArray(m_edit);
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт комбо-бокс |
//+------------------------------------------------------------------+
bool CTable::CreateCombobox(void)
{
//--- Если нет ячеек с комбо-боксом
if(!m_combobox_state)
return(true);
//--- Сохраним указатель на главный элемент
m_combobox.MainPointer(this);
//--- Координаты
int x=-1,y=0;
//--- Свойства
m_combobox.Alpha(0);
m_combobox.XSize(50);
m_combobox.YSize(21);
m_combobox.ItemsTotal(5);
m_combobox.GetButtonPointer().XGap(1);
m_combobox.GetButtonPointer().LabelYGap(4);
m_combobox.GetButtonPointer().IconYGap(3);
m_combobox.IsDropdown(CElementBase::IsDropdown());
//--- Получим указатель на список
CListView *lv=m_combobox.GetListViewPointer();
//--- Установим свойства списка
lv.YSize(93);
lv.LightsHover(true);
lv.GetScrollVPointer().Index(2);
//--- Занесём значения в список
for(int i=0; i<5; i++)
m_combobox.SetValue(i,(string)i);
//--- Выделим в списке первый пункт
m_combobox.SelectItem(0);
//--- Создадим элемент управления
if(!m_combobox.CreateComboBox("",x,y))
return(false);
//--- Скрыть
m_combobox.Hide();
//--- Добавить элемент в массив
CElement::AddToArray(m_combobox);
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт указатель курсора изменения ширины столбцов |
//+------------------------------------------------------------------+
bool CTable::CreateColumnResizePointer(void)
{
//--- Выйти, если режим изменения ширины столбцов отключен
if(!m_column_resize_mode)
{
m_column_resize.State(false);
m_column_resize.IsVisible(false);
return(true);
}
//--- Свойства
m_column_resize.XGap(13);
m_column_resize.YGap(14);
m_column_resize.XSize(20);
m_column_resize.YSize(20);
m_column_resize.Id(CElementBase::Id());
m_column_resize.Type(MP_X_RESIZE_RELATIVE);
//--- Создание элемента
if(!m_column_resize.CreatePointer(m_chart_id,m_subwin))
return(false);
//---
return(true);
}
//+------------------------------------------------------------------+
//| Возвращает количество видимых строк |
//+------------------------------------------------------------------+
int CTable::VisibleRowsTotal(void)
{
double visible_rows_total =m_table_visible_y_size/m_cell_y_size;
double check_y_size =visible_rows_total*m_cell_y_size;
visible_rows_total=(check_y_size<m_table_visible_y_size-1)? visible_rows_total-1 : visible_rows_total;
return((int)visible_rows_total);
}
//+------------------------------------------------------------------+
//| Возвращает количество картинок в указанной ячейке |
//+------------------------------------------------------------------+
int CTable::ImagesTotal(const uint column_index,const uint row_index)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return(WRONG_VALUE);
//--- Вернём размер массива картинок
return(::ArraySize(m_columns[column_index].m_rows[row_index].m_images));
}
//+------------------------------------------------------------------+
//| Минимальная ширина столбцов |
//+------------------------------------------------------------------+
void CTable::MinColumnWidth(const int width)
{
//--- Ширина столбца не менее 3-ёх пикселей
m_min_column_width=(width>3)? width : 3;
}
//+------------------------------------------------------------------+
//| Устанавливает размер таблицы |
//+------------------------------------------------------------------+
void CTable::TableSize(const int columns_total,const int rows_total,const bool init=true)
{
//--- Должно быть не менее одного столбца
m_columns_total=(columns_total<1)? 1 : columns_total;
//--- Должно быть не менее двух рядов
m_rows_total=(rows_total<1)? 1 : rows_total;
//--- Установить размер массивам рядов, столбцов и заголовков
::ArrayResize(m_rows,m_rows_total);
::ArrayResize(m_columns,m_columns_total);
//--- Выйти, если не нужна инициализация по умолчанию
if(!init)
return;
//--- Установить размер массивам таблицы
for(uint c=0; c<m_columns_total; c++)
{
::ArrayResize(m_columns[c].m_rows,m_rows_total);
//--- Инициализация свойств столбцов значениями по умолчанию
ColumnInitialize(c);
//--- Инициализация свойств ячеек
for(uint r=0; r<m_rows_total; r++)
CellInitialize(c,r);
}
}
//+------------------------------------------------------------------+
//| Реконструкция таблицы |
//+------------------------------------------------------------------+
void CTable::Rebuilding(const int columns_total,const int rows_total,const bool redraw=false)
{
m_selected_item =WRONG_VALUE;
m_prev_selected_item =WRONG_VALUE;
//--- Установить новый размер
TableSize(columns_total,rows_total);
//--- Рассчитать и установить новые размеры таблицы
RecalculateAndResizeTable(redraw);
}
//+------------------------------------------------------------------+
//| Добавляет столбец в таблицу по указанному индексу |
//+------------------------------------------------------------------+
void CTable::AddColumn(const int column_index,const bool redraw=false)
{
//--- Увеличим размер массива на один элемент
int array_size=(int)ColumnsTotal();
m_columns_total=array_size+1;
::ArrayResize(m_columns,m_columns_total);
//--- Установить размер массивам рядов
::ArrayResize(m_columns[array_size].m_rows,m_rows_total);
//--- Корректировка индекса в случае выхода из диапазона
int checked_column_index=(column_index>=(int)m_columns_total)?(int)m_columns_total-1 : column_index;
//--- Сместить другие столбцы (двигаемся от конца массива к индексу добавляемого столбца)
for(int c=array_size; c>=checked_column_index; c--)
{
//--- Сместить признак отсортированного массива
if(c==m_is_sorted_column_index && m_is_sorted_column_index!=WRONG_VALUE)
m_is_sorted_column_index++;
//--- Индекс предыдущего столбца
int prev_c=c-1;
//--- В новом столбце инициализация значениями по умолчанию
if(c==checked_column_index)
ColumnInitialize(c);
//--- Перемещаем данные из предыдущего столбца в текущий
else
ColumnCopy(c,prev_c);
//---
for(uint r=0; r<m_rows_total; r++)
{
//--- Инициализация ячеек нового столбца значениями по умолчанию
if(c==checked_column_index)
{
CellInitialize(c,r);
continue;
}
//--- Перемещаем данные из ячейки предыдущего столбца в ячейку текущего
CellCopy(c,r,prev_c,r);
}
}
//--- Рассчитать и установить новые размеры таблицы
RecalculateAndResizeTable(redraw);
}
//+------------------------------------------------------------------+
//| Удаляет столбец в таблице по указанному индексу |
//+------------------------------------------------------------------+
void CTable::DeleteColumn(const int column_index,const bool redraw=false)
{
//--- Получим размер массива столбцов
int array_size=(int)ColumnsTotal();
//--- Выйти, если остался только один столбец
if(array_size<2)
return;
//--- Корректировка индекса в случае выхода из диапазона
int checked_column_index=(column_index>=array_size)? array_size-1 : column_index;
//--- Сместить другие столбцы (двигаемся от указанного индекса до последнего столбца)
for(int c=checked_column_index; c<array_size-1; c++)
{
//--- Сместить признак отсортированного массива
if(c!=checked_column_index)
{
if(c==m_is_sorted_column_index && m_is_sorted_column_index!=WRONG_VALUE)
m_is_sorted_column_index--;
}
//--- Обнулить, если удалён отсортированный столбец
else
m_is_sorted_column_index=WRONG_VALUE;
//--- Индекс следующего столбца
int next_c=c+1;
//--- Перемещаем данные из следующего столбца в текущий
ColumnCopy(c,next_c);
//--- Перемещаем данные из ячеек следующего столбца в ячейки текущего
for(uint r=0; r<m_rows_total; r++)
CellCopy(c,r,next_c,r);
}
//--- Уменьшим массив столбцов на один элемент
m_columns_total=array_size-1;
::ArrayResize(m_columns,m_columns_total);
//--- Рассчитать и установить новые размеры таблицы
RecalculateAndResizeTable(redraw);
}
//+------------------------------------------------------------------+
//| Добавляет строку в таблицу по указанному индексу |
//+------------------------------------------------------------------+
void CTable::AddRow(const int row_index,const bool redraw=false)
{
//--- Увеличим размер массива на один элемент
int array_size=(int)RowsTotal();
m_rows_total=array_size+1;
//--- Установить размер массивам рядов
for(uint i=0; i<m_columns_total; i++)
{
::ArrayResize(m_rows,m_rows_total,100000);
::ArrayResize(m_columns[i].m_rows,m_rows_total,100000);
}
//--- Корректировка индекса в случае выхода из диапазона
int checked_row_index=(row_index>=(int)m_rows_total)?(int)m_rows_total-1 : row_index;
//--- Сместить другие строки (двигаемся от конца массива к индексу добавляемой строки)
for(int c=0; c<(int)m_columns_total; c++)
{
for(int r=array_size; r>=checked_row_index; r--)
{
//--- Инициализация ячейки новой строки значениями по умолчанию
if(r==checked_row_index)
{
CellInitialize(c,r);
continue;
}
//--- Индекс предыдущей строки
uint prev_r=r-1;
//--- Перемещаем данные из ячейки предыдущей строки в ячейку текущей
CellCopy(c,r,c,prev_r);
}
}
//--- Рассчитать и установить новые размеры таблицы
if(redraw)
RecalculateAndResizeTable(redraw);
}
//+------------------------------------------------------------------+
//| Удаляет строку в таблице по указанному индексу |
//+------------------------------------------------------------------+
void CTable::DeleteRow(const int row_index,const bool redraw=false)
{
//--- Получим размер массива строк
int array_size=(int)RowsTotal();
//--- Выйти, если осталась только одна строка
if(array_size<2)
return;
//--- Корректировка индекса в случае выхода из диапазона
int checked_row_index=(row_index>=(int)m_rows_total)?(int)m_rows_total-1 : row_index;
//--- Сместить другие строки (двигаемся от указанного индекса к последней строке)
for(int c=0; c<(int)m_columns_total; c++)
{
for(int r=checked_row_index; r<array_size-1; r++)
{
//--- Индекс следующей строки
uint next_r=r+1;
//--- Перемещаем данные из ячейки следующей строки в ячейку текущей
CellCopy(c,r,c,next_r);
}
}
//--- Уменьшим размер массива строк на один элемент
m_rows_total=array_size-1;
//--- Установить размер массивам рядов
for(uint i=0; i<m_columns_total; i++)
{
::ArrayResize(m_rows,m_rows_total);
::ArrayResize(m_columns[i].m_rows,m_rows_total);
}
//--- Рассчитать и установить новые размеры таблицы
RecalculateAndResizeTable(redraw);
}
//+------------------------------------------------------------------+
//| Удаляет все строки |
//+------------------------------------------------------------------+
void CTable::DeleteAllRows(const bool redraw=false)
{
//--- Установить размерность
TableSize(m_columns_total,1,false);
//--- Очистить ячейки
for(uint i=0; i<m_columns_total; i++)
{
m_columns[i].m_data_type=TYPE_STRING;
SetValue(i,0,"");
BackColor(i,0,clrWhite);
}
//--- Установить значения по умолчанию
m_selected_item_text ="";
m_selected_item =WRONG_VALUE;
m_last_sort_direction =SORT_ASCEND;
m_is_sorted_column_index =WRONG_VALUE;
//--- Рассчитать и установить новые размеры таблицы
RecalculateAndResizeTable(redraw);
}
//+------------------------------------------------------------------+
//| Очищает таблицу. Остаётся только один столбец и одна строка. |
//+------------------------------------------------------------------+
void CTable::Clear(const bool redraw=false)
{
//--- Установить минимальный размер 1x1
TableSize(1,1);
//--- Установить значения по умолчанию
m_selected_item_text ="";
m_selected_item =WRONG_VALUE;
m_last_sort_direction =SORT_ASCEND;
m_is_sorted_column_index =WRONG_VALUE;
//--- Рассчитать и установить новые размеры таблицы
RecalculateAndResizeTable(redraw);
}
//+------------------------------------------------------------------+
//| Заполняет массив заголовков по указанному индексу |
//+------------------------------------------------------------------+
void CTable::SetHeaderText(const uint column_index,const string value)
{
//--- Проверка на выход из диапазона столбцов
if(!CheckOutOfColumnRange(column_index))
return;
//--- Установить значение в массив
m_columns[column_index].m_header_text=value;
}
//+------------------------------------------------------------------+
//| Получение текста указанного заголовка |
//+------------------------------------------------------------------+
string CTable::GetHeaderText(const uint column_index)
{
//--- Проверка на выход из диапазона столбцов
if(!CheckOutOfColumnRange(column_index))
return("");
//--- Установить значение в массив
return(m_columns[column_index].m_header_text);
}
//+------------------------------------------------------------------+
//| Получение заголовков в переданный массив |
//+------------------------------------------------------------------+
uint CTable::GetHeadersText(string &headers[])
{
int columns_total=::ArraySize(m_columns);
::ArrayResize(headers,columns_total);
for(int c=0; c<columns_total; c++)
headers[c]=m_columns[c].m_header_text;
//--- Вернуть количество заголовков
return(columns_total);
}
//+------------------------------------------------------------------+
//| Заполняет массив режимом выравнивания текста |
//+------------------------------------------------------------------+
void CTable::TextAlign(const ENUM_ALIGN_MODE &array[])
{
int total=0;
//--- Выйти, если передан массив нулевого размера
if((total=CheckArraySize(array))==WRONG_VALUE)
return;
//--- Сохранить значения в структуре
for(int c=0; c<total; c++)
m_columns[c].m_text_align=array[c];
}
//+------------------------------------------------------------------+
//| Заполняет массив режимом выравнивания текста |
//+------------------------------------------------------------------+
void CTable::TextAlign(const uint column_index,const ENUM_ALIGN_MODE align)
{
//--- Проверка на выход из диапазона столбцов
if(!CheckOutOfColumnRange(column_index))
return;
//--- Установить способ выравнивания для указанного столбца
m_columns[column_index].m_text_align=align;
}
//+------------------------------------------------------------------+
//| Заполняет массив отступа текста в ячейке по оси X |
//+------------------------------------------------------------------+
void CTable::TextXOffset(const int &array[])
{
int total=0;
//--- Выйти, если передан массив нулевого размера
if((total=CheckArraySize(array))==WRONG_VALUE)
return;
//--- Сохранить значения в структуре
for(int c=0; c<total; c++)
m_columns[c].m_text_x_offset=array[c];
}
//+------------------------------------------------------------------+
//| Заполняет массив ширины столбцов |
//+------------------------------------------------------------------+
void CTable::ColumnsWidth(const int &array[])
{
int total=0;
//--- Выйти, если передан массив нулевого размера
if((total=CheckArraySize(array))==WRONG_VALUE)
return;
//--- Сохранить значения в структуре
for(int c=0; c<total; c++)
m_columns[c].m_width=array[c];
}
//+------------------------------------------------------------------+
//| Отступы картинок от краёв ячеек по оси X |
//+------------------------------------------------------------------+
void CTable::ImageXOffset(const int &array[])
{
int total=0;
//--- Выйти, если передан массив нулевого размера
if((total=CheckArraySize(array))==WRONG_VALUE)
return;
//--- Сохранить значения в структуре
for(int c=0; c<total; c++)
m_columns[c].m_image_x_offset=array[c];
}
//+------------------------------------------------------------------+
//| Отступы картинок от краёв ячеек по оси Y |
//+------------------------------------------------------------------+
void CTable::ImageYOffset(const int &array[])
{
int total=0;
//--- Выйти, если передан массив нулевого размера
if((total=CheckArraySize(array))==WRONG_VALUE)
return;
//--- Сохранить значения в структуре
for(int c=0; c<total; c++)
m_columns[c].m_image_y_offset=array[c];
}
//+------------------------------------------------------------------+
//| Установка типа данных в указанном столбце |
//+------------------------------------------------------------------+
void CTable::DataType(const uint column_index,const ENUM_DATATYPE type)
{
//--- Проверка на выход из диапазона столбцов
if(!CheckOutOfColumnRange(column_index))
return;
//--- Установить тип данных для указанного столбца
m_columns[column_index].m_data_type=type;
}
//+------------------------------------------------------------------+
//| Получение типа данных в указанном столбце |
//+------------------------------------------------------------------+
ENUM_DATATYPE CTable::DataType(const uint column_index)
{
//--- Проверка на выход из диапазона столбцов
if(!CheckOutOfColumnRange(column_index))
return(WRONG_VALUE);
//--- Вернуть тип данных для указанного столбца
return(m_columns[column_index].m_data_type);
}
//+------------------------------------------------------------------+
//| Устанавливает тип ячейки |
//+------------------------------------------------------------------+
void CTable::CellType(const uint column_index,const uint row_index,const ENUM_TYPE_CELL type)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return;
//--- Установить тип ячейки
m_columns[column_index].m_rows[row_index].m_type=type;
//--- Признак наличия поля ввода
if(type==CELL_EDIT && !m_edit_state)
m_edit_state=true;
//--- Признак наличия комбо-бокса
else if(type==CELL_COMBOBOX && !m_combobox_state)
m_combobox_state=true;
}
//+------------------------------------------------------------------+
//| Получение типа ячейки |
//+------------------------------------------------------------------+
ENUM_TYPE_CELL CTable::CellType(const uint column_index,const uint row_index)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return(WRONG_VALUE);
//--- Вернуть тип данных для указанного столбца
return(m_columns[column_index].m_rows[row_index].m_type);
}
//+------------------------------------------------------------------+
//| Устанавливает картинки в указанную ячейку |
//+------------------------------------------------------------------+
void CTable::SetImages(const uint column_index,const uint row_index,const string &bmp_file_path[])
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return;
//--- Выйти, если передан массив нулевого размера
int total=0;
if((total=::ArraySize(bmp_file_path))<1)
return;
//--- Установить новый размер массивам
::ArrayResize(m_columns[column_index].m_rows[row_index].m_images,total);
//---
for(int i=0; i<total; i++)
{
//--- По умолчанию выбрана первая картинка массива
m_columns[column_index].m_rows[row_index].m_selected_image=0;
//--- Записать переданную картинку в массив и запомнить её размеры
if(!m_columns[column_index].m_rows[row_index].m_images[i].ReadImageData(bmp_file_path[i]))
return;
}
}
//+------------------------------------------------------------------+
//| Устанавливает картинки в указанную ячейку |
//+------------------------------------------------------------------+
void CTable::SetImages(const uint column_index,const uint row_index,const uint &resource_index[])
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return;
//--- Выйти, если передан массив нулевого размера
int total=0;
if((total=::ArraySize(resource_index))<1)
return;
//--- Установить новый размер массивам
::ArrayResize(m_columns[column_index].m_rows[row_index].m_images,total);
//---
for(int i=0; i<total; i++)
{
//--- По умолчанию выбрана первая картинка массива
m_columns[column_index].m_rows[row_index].m_selected_image=0;
//--- Записать переданную картинку в массив и запомнить её размеры
if(!m_columns[column_index].m_rows[row_index].m_images[i].ReadImageData(resource_index[i]))
return;
}
}
//+------------------------------------------------------------------+
//| Изменяет картинку в указанной ячейке |
//+------------------------------------------------------------------+
void CTable::ChangeImage(const uint column_index,const uint row_index,const uint image_index,const bool redraw=false)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return;
//--- Получим количество картинок ячейки
int images_total=ImagesTotal(column_index,row_index);
//--- Выйти, если (1) картинок нет или (2) выходим из диапазона
if(images_total==WRONG_VALUE || image_index>=(uint)images_total)
return;
//--- Выйти, если указанная картинка совпадает с выбранной
if(image_index==m_columns[column_index].m_rows[row_index].m_selected_image)
return;
//--- Сохранить индекс выбранной картинки ячейки
m_columns[column_index].m_rows[row_index].m_selected_image=(int)image_index;
//--- Перерисовать ячейку, если указано
if(redraw)
RedrawCell(column_index,row_index);
}
//+------------------------------------------------------------------+
//| Возвращает индекс картинки в указанной ячейке |
//+------------------------------------------------------------------+
int CTable::SelectedImageIndex(const uint column_index,const uint row_index)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return(WRONG_VALUE);
//--- Вернуть значение
return(m_columns[column_index].m_rows[row_index].m_selected_image);
}
//+------------------------------------------------------------------+
//| Возвращает индекс выбранного пункта |
//| в списке комбо-бокса в указанной ячейке |
//+------------------------------------------------------------------+
int CTable::SelectedComboboxItemIndex(const uint column_index,const uint row_index)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return(WRONG_VALUE);
//--- Вернуть значение
return(m_columns[column_index].m_rows[row_index].m_selected_item);
}
//+------------------------------------------------------------------+
//| Устанавливает цвет текста в указанную ячейку таблицы |
//+------------------------------------------------------------------+
void CTable::TextColor(const uint column_index,const uint row_index,const color clr,const bool redraw=false)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return;
//--- Установить цвет текста в общий массив
m_columns[column_index].m_rows[row_index].m_text_color=clr;
//--- Перерисовать ячейку, если указано
if(redraw)
RedrawCell(column_index,row_index);
}
//+------------------------------------------------------------------+
//| Устанавливает цвет фона в указанную ячейку таблицы |
//+------------------------------------------------------------------+
void CTable::BackColor(const uint column_index,const uint row_index,const color clr,const bool redraw=false)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return;
//--- Установить цвет текста в общий массив
m_columns[column_index].m_rows[row_index].m_back_color=clr;
//--- Перерисовать ячейку, если указано
if(redraw)
RedrawCell(column_index,row_index);
}
//+------------------------------------------------------------------+
//| Возвращает цвет фона из указанной ячейки таблицы |
//+------------------------------------------------------------------+
color CTable::BackColor(const uint column_index,const uint row_index)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return(clrWhite);
//---
return(m_columns[column_index].m_rows[row_index].m_back_color);
}
//+------------------------------------------------------------------+
//| Заполняет массив по указанным индексам |
//+------------------------------------------------------------------+
void CTable::SetValue(const uint column_index,const uint row_index,const string value="",const uint digits=0,const bool redraw=false)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return;
//--- Установить значение в массив:
// Строковое
if(m_columns[column_index].m_data_type==TYPE_STRING)
m_columns[column_index].m_rows[row_index].m_full_text=value;
//--- Вещественное
else if(m_columns[column_index].m_data_type==TYPE_DOUBLE)
{
m_columns[column_index].m_rows[row_index].m_digits=digits;
double type_value=::StringToDouble(value);
m_columns[column_index].m_rows[row_index].m_full_text=::DoubleToString(type_value,digits);
}
//--- Время
else if(m_columns[column_index].m_data_type==TYPE_DATETIME)
{
datetime type_value=::StringToTime(value);
m_columns[column_index].m_rows[row_index].m_full_text=::TimeToString(type_value);
}
//--- Любой другой тип будет установлен, как строка
else
m_columns[column_index].m_rows[row_index].m_full_text=value;
//--- Скорректировать и сохранить текст, если не помещается в ячейке
m_columns[column_index].m_rows[row_index].m_short_text=CorrectingText(column_index,row_index);
//--- Перерисовать ячейку, если указано
if(redraw)
RedrawCell(column_index,row_index);
}
//+------------------------------------------------------------------+
//| Возвращает значение по указанным индексам |
//+------------------------------------------------------------------+
string CTable::GetValue(const uint column_index,const uint row_index)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return("");
//--- Вернуть значение
return(m_columns[column_index].m_rows[row_index].m_full_text);
}
//+------------------------------------------------------------------+
//| Выделение указанной строки в таблице |
//+------------------------------------------------------------------+
void CTable::SelectRow(const int row_index)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(0,(uint)row_index))
return;
//--- Если такая строка уже выделена
if(m_selected_item==row_index)
return;
//--- Текущий и предыдущий индексы строк
m_prev_selected_item =(m_selected_item==WRONG_VALUE)? row_index : m_selected_item;
m_selected_item =row_index;
//--- Массив для значений в определённой последовательности
int indexes[2];
//--- Если здесь в первый раз
if(m_prev_selected_item==WRONG_VALUE)
indexes[0]=m_selected_item;
else
{
indexes[0] =(m_selected_item>m_prev_selected_item)? m_prev_selected_item : m_selected_item;
indexes[1] =(m_selected_item>m_prev_selected_item)? m_selected_item : m_prev_selected_item;
}
//--- Рисует указанный ряд таблицы по указанному режиму
DrawRow(indexes,m_selected_item,m_prev_selected_item,false);
//--- Получить индексы на границах видимой области
VisibleTableIndexes();
//--- Переместить полосу прокрутки на указанную строку
if(row_index==0)
{
VerticalScrolling(0);
}
else if((uint)row_index>=m_rows_total-1)
{
VerticalScrolling(WRONG_VALUE);
}
else if(row_index<(int)m_visible_table_from_index)
{
VerticalScrolling(m_scrollv.CurrentPos()-1);
}
else if(row_index>=(int)m_visible_table_to_index-1)
{
VerticalScrolling(m_scrollv.CurrentPos()+1);
}
}
//+------------------------------------------------------------------+
//| Добавить список значений в комбо-бокс |
//+------------------------------------------------------------------+
void CTable::AddValueList(const uint column_index,const uint row_index,const string &array[],const uint selected_item=0)
{
//--- Проверка на выход из диапазона
if(!CheckOutOfRange(column_index,row_index))
return;
//--- Установим размер списку указанной ячейки
uint total=::ArraySize(array);
::ArrayResize(m_columns[column_index].m_rows[row_index].m_value_list,total);
//--- Сохраним переданные значения
::ArrayCopy(m_columns[column_index].m_rows[row_index].m_value_list,array);
//--- Проверка индекса выбранного пункта в списке
uint check_item_index=(selected_item>=total)? total-1 : selected_item;
//--- Сохранить выбранный пункт в списке
m_columns[column_index].m_rows[row_index].m_selected_item=(int)check_item_index;
//--- Сохранить текст выбранного пункта в ячейке
m_columns[column_index].m_rows[row_index].m_full_text=array[check_item_index];
}
//+------------------------------------------------------------------+
//| Горизонтальная прокрутка поля ввода |
//+------------------------------------------------------------------+
void CTable::HorizontalScrolling(const int pos=WRONG_VALUE)
{
//--- Для определения позиции ползунка
int index=0;
//--- Индекс последней позиции
int last_pos_index=int(m_table_x_size-m_table_visible_x_size);
//--- Корректировка в случае выхода из диапазона
if(pos<0)
index=last_pos_index;
else
index=(pos>last_pos_index)? last_pos_index : pos;
//--- Сдвигаем ползунок полосы прокрутки
m_scrollh.MovingThumb(index);
//--- Сдвигаем поле ввода
ShiftTable();
}
//+------------------------------------------------------------------+
//| Вертикальная прокрутка поля ввода |
//+------------------------------------------------------------------+
void CTable::VerticalScrolling(const int pos=WRONG_VALUE)
{
//--- Для определения позиции ползунка
int index=0;
//--- Индекс последней позиции
int last_pos_index=int(m_table_y_size-m_table_visible_y_size);
//--- Корректировка в случае выхода из диапазона
if(pos<0)
index=last_pos_index;
else
index=(pos>last_pos_index)? last_pos_index : pos;
//--- Сдвигаем ползунок полосы прокрутки
m_scrollv.MovingThumb(index);
//--- Сдвигаем поле ввода
ShiftTable();
}
//+------------------------------------------------------------------+
//| Сдвигает таблицу относительно полос прокрутки |
//+------------------------------------------------------------------+
void CTable::ShiftTable(void)
{
//--- Получим текущие позиции ползунков горизонтальной и вертикальной полос прокрутки
int h_offset =m_scrollh.CurrentPos()*m_shift_x_step;
int v_offset =m_scrollv.CurrentPos()*m_cell_y_size;
//--- Рассчитаем отступы для смещения
int x_offset =(h_offset<1)? 0 : (h_offset>=m_shift_x2_limit)? m_shift_x2_limit-2 : h_offset;
int y_offset =(v_offset<1)? 0 : (v_offset>=m_shift_y2_limit)? m_shift_y2_limit : v_offset;
//--- Расчёт положения данных относительно ползунков полос прокрутки
long x =(m_table_x_size>m_table_visible_x_size)? x_offset : 0;
long y =(m_table_y_size>m_table_visible_y_size)? y_offset : 0;
//--- Смещение таблицы
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_XOFFSET,x);
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_YOFFSET,y);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_XOFFSET,x);
}
//+------------------------------------------------------------------+
//| Сортировать данные по указанному столбцу |
//+------------------------------------------------------------------+
void CTable::SortData(const uint column_index=0,const int direction=WRONG_VALUE)
{
//--- Выйти, если выходим за пределы таблицы
if(column_index>=m_columns_total)
return;
//--- Индекс, с которого нужно начать сортировку
uint first_index=0;
//--- Последний индекс
uint last_index=m_rows_total-1;
//--- Без контроля направления пользователем
if(direction==WRONG_VALUE)
{
//--- В первый раз будет отсортировано по возрастанию, а затем каждый раз в противоположном направлении
if(m_is_sorted_column_index==WRONG_VALUE || column_index!=m_is_sorted_column_index || m_last_sort_direction==SORT_DESCEND)
m_last_sort_direction=SORT_ASCEND;
else
m_last_sort_direction=SORT_DESCEND;
}
else
{
m_last_sort_direction=(ENUM_CSORT_MODE)direction;
}
//--- Запомним индекс последнего отсортированного столбца данных
m_is_sorted_column_index=(int)column_index;
//--- Сортировка
QuickSort(first_index,last_index,column_index,m_last_sort_direction);
}
//+------------------------------------------------------------------+
//| Авто-изменение ширины столбцов |
//+------------------------------------------------------------------+
void CTable::AutoResizeColumns(void)
{
if(!m_autoresize_columns)
return;
//---
int table_x_size =m_x_size-2;
uint last_column_index =m_columns_total-1;
//--- Сумма ширины всех столбцов
int sum_width=0;
for(uint c=0; c<m_columns_total; c++)
sum_width+=m_columns[c].m_width;
//--- Корректируем ширину последнего столбца
if(m_rows_total>(uint)VisibleRowsTotal())
{
if(sum_width==table_x_size)
m_columns[last_column_index].m_width=m_columns[last_column_index].m_width-m_scrollv.XSize();
}
else
{
if(sum_width!=table_x_size)
{
if(sum_width<table_x_size)
{
int difference=table_x_size-sum_width;
m_columns[last_column_index].m_width=m_columns[last_column_index].m_width+difference;
}
else
{
int difference=sum_width-(table_x_size);
m_columns[last_column_index].m_width=m_columns[last_column_index].m_width-difference;
}
}
}
}
//+------------------------------------------------------------------+
//| Авто-коррекция ширины столбцов по тексту |
//+------------------------------------------------------------------+
void CTable::AutoCorrectWidthColumns(void)
{
if(!m_auto_correct_columns_width_mode || m_column_resize_mode)
return;
//--- Определяем максимальную ширину для каждого столбца
uint max_width[];
::ArrayResize(max_width,m_columns_total);
for(uint c=0; c<m_columns_total; c++)
{
uint width =0;
uint height =0;
::TextGetSize(GetHeaderText(c),width,height);
max_width[c]=width;
//---
for(uint r=0; r<m_rows_total; r++)
{
string text=m_columns[c].m_rows[r].m_full_text;
::TextGetSize(text,width,height);
max_width[c]=(uint)::fmax((double)width,(double)max_width[c]);
}
//---
m_columns[c].m_width=(int)max_width[c]+25;
}
}
//+------------------------------------------------------------------+
//| Обновление таблицы |
//+------------------------------------------------------------------+
void CTable::Update(const bool redraw=false)
{
//--- Перерисовать таблицу, если указано
if(redraw)
{
//--- Авторазмер столбцов
AutoCorrectWidthColumns();
AutoResizeColumns();
//--- Установить новый размер фона таблицы
ChangeMainSize(m_x_size,m_y_size);
//--- Рассчитать размеры таблицы
CalculateTableSize();
//--- Установить новый размер таблице
ChangeTableSize();
//--- Перерисовать таблицу
DrawTable();
//--- Обновить таблицу
m_canvas.Update();
m_table.Update();
//--- Обновить заголовки, если они включены
if(m_show_headers)
m_headers.Update();
//--- Обновить полосы прокрутки
m_scrollv.Update(true);
m_scrollh.Update(true);
return;
}
//--- Авторазмер столбцов
AutoCorrectWidthColumns();
AutoResizeColumns();
//--- Обновить таблицу
m_canvas.Update();
m_table.Update();
//--- Обновить заголовки, если они включены
if(m_show_headers)
m_headers.Update();
}
//+------------------------------------------------------------------+
//| Обработка нажатия на заголовке |
//+------------------------------------------------------------------+
bool CTable::OnClickHeaders(const string clicked_object)
{
//--- Выйти, если (1) режим сортировки отключен или (2) в процессе изменения ширины столбца
if(!m_is_sort_mode || m_column_resize_control!=WRONG_VALUE)
return(false);
//--- Выйти, если в ячейках есть поля ввода или комбо-боксы в ячейках
if(m_edit_state && m_combobox_state)
return(false);
//--- Выйдем, если полоса прокрутки в активном режиме
if(m_scrollv.State() || m_scrollh.State())
return(false);
//--- Выйдем, если чужое имя объекта
if(m_headers.ChartObjectName()!=clicked_object)
return(false);
//--- Для определения индекса столбца
uint column_index=0;
//--- Получим относительную X-координату под курсором мыши
int x=m_mouse.RelativeX(m_headers);
//--- Определим заголовок, на котором нажали
for(uint c=0; c<m_columns_total; c++)
{
//--- Если нашли заголовок, запомним его индекс
if(x>=m_columns[c].m_x && x<=m_columns[c].m_x2)
{
column_index=c;
break;
}
}
//--- Сортировка данных по указанному столбцу
SortData(column_index);
//--- Отправим сообщение об этом
::EventChartCustom(m_chart_id,ON_SORT_DATA,CElementBase::Id(),m_is_sorted_column_index,::EnumToString(DataType(column_index)));
return(true);
}
//+------------------------------------------------------------------+
//| Обработка выделения ряда |
//+------------------------------------------------------------------+
bool CTable::OnSelectRow(const int row_index)
{
//--- Если нажали на уже выделенном ряде
if(row_index==m_selected_item)
{
//--- Снять выделение, если нет запрета
if(!m_is_without_deselect)
{
m_prev_selected_item =m_selected_item;
m_selected_item =WRONG_VALUE;
m_selected_item_text ="";
}
return(true);
}
//--- Сохраним индекс ряда и строку первой ячейки
m_prev_selected_item =(m_selected_item==WRONG_VALUE)? row_index : m_selected_item;
m_selected_item =row_index;
m_selected_item_text =m_columns[0].m_rows[row_index].m_full_text;
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на таблице |
//+------------------------------------------------------------------+
bool CTable::OnClickTable(const string clicked_object)
{
//--- Выйти, если в процессе изменения ширины столбца
if(m_column_resize_control!=WRONG_VALUE)
return(false);
//--- Выйдем, если полоса прокрутки в активном режиме
if(m_scrollv.State() || m_scrollh.State())
return(false);
//--- Выйдем, если чужое имя объекта
if(m_table.ChartObjectName()!=clicked_object)
return(false);
//--- Определим ряд, на котором нажали
int r=PressedRowIndex();
//--- Определим ячейку, на которой нажали
int c=PressedCellColumnIndex();
//--- Проверим был ли задействован элемент в ячейке
bool is_cell_element=CheckCellElement(c,r);
//--- Если (1) включен режим выделения строки и (2) не задействован элемент в ячейке
if(m_selectable_row && !is_cell_element)
{
OnSelectRow(r);
//--- Изменить цвет
RedrawRow(true);
m_table.Update();
//--- Отправим сообщение об этом
::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElementBase::Id(),m_selected_item,string(c)+"_"+string(r));
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Обработка двойного нажатия на таблице |
//+------------------------------------------------------------------+
bool CTable::OnDoubleClickTable(const string clicked_object)
{
//--- Выйти, если таблица вне фокуса
if(!m_table.MouseFocus())
return(false);
//--- Определим ряд, на котором нажали
int r=PressedRowIndex();
//--- Определим ячейку, на которой нажали
int c=PressedCellColumnIndex();
//--- Проверим был ли задействован элемент в ячейке и вернём результат
return(CheckCellElement(c,r,true));
}
//+------------------------------------------------------------------+
//| Обработка окончания ввода значения в ячейку |
//+------------------------------------------------------------------+
bool CTable::OnEndEditCell(const int id)
{
//--- Выйти, если (1) идентификаторы не совпадают или (2) ячеек с полями ввода нет
if(id!=CElementBase::Id() || !m_edit_state)
return(false);
//--- Установить новое значение в ячейку таблицы
SetValue(m_last_edit_column_index,m_last_edit_row_index,m_edit.GetValue(),0,true);
Update();
//--- Деактивировать и скрыть поле ввода
m_edit.GetTextBoxPointer().DeactivateTextBox();
m_edit.Hide();
m_chart.Redraw();
return(true);
}
//+------------------------------------------------------------------+
//| Обработка выбора пункта в комбо-боксе ячейки |
//+------------------------------------------------------------------+
bool CTable::OnClickComboboxItem(const int id)
{
//--- Выйти, если (1) идентификаторы не совпадают или (2) ячеек с комбо-боксом нет
if(id!=CElementBase::Id() || !m_combobox_state)
return(false);
//--- Индексы последней редактируемой ячейки
int c=m_last_edit_column_index;
int r=m_last_edit_row_index;
//--- Запомним в ячейке индекс выбранного пункта
m_columns[c].m_rows[r].m_selected_item=m_combobox.GetListViewPointer().SelectedItemIndex();
//--- Установить новое значение в ячейку таблицы
SetValue(c,r,m_combobox.GetValue(),0,true);
Update();
return(true);
}
//+------------------------------------------------------------------+
//| Проверка поля ввода в ячейках на скрытие |
//+------------------------------------------------------------------+
void CTable::CheckAndHideEdit(void)
{
//--- Выйти, если (1) поля ввода нет или (2) оно скрыто
if(!m_edit_state || !m_edit.IsVisible())
return;
//--- Проверим фокус
m_edit.GetTextBoxPointer().CheckMouseFocus();
//--- Деактивировать и скрыть поле ввода, если оно (1) вне фокуса и (2) кнопка мыши нажата
if(!m_edit.GetTextBoxPointer().MouseFocus() && m_mouse.LeftButtonState())
{
m_edit.GetTextBoxPointer().DeactivateTextBox();
m_edit.Hide();
m_chart.Redraw();
}
}
//+------------------------------------------------------------------+
//| Проверка комбо-бокса в ячейках на скрытие |
//+------------------------------------------------------------------+
void CTable::CheckAndHideCombobox(void)
{
//--- Выйти, если (1) комбо-бокса нет или (2) оно скрыто
if(!m_combobox_state || !m_combobox.IsVisible())
return;
//--- Скрыть комбо-бокс, если он вне фокуса и кнопка мыши нажата
if(!m_combobox.GetButtonPointer().MouseFocus() && m_mouse.LeftButtonState())
{
m_combobox.Hide();
m_chart.Redraw();
}
}
//+------------------------------------------------------------------+
//| Возвращает индекс нажатой строки |
//+------------------------------------------------------------------+
int CTable::PressedRowIndex(void)
{
int index=0;
//--- Получим относительную Y-координату под курсором мыши
int y=m_mouse.RelativeY(m_table);
//--- Определим ряд, на котором нажали
for(uint r=0; r<m_rows_total; r++)
{
//--- Если нажатие было не на этом ряде, перейти к следующему
if(!(y>=m_rows[r].m_y && y<=m_rows[r].m_y2))
continue;
//---
index=(int)r;
break;
}
//--- Вернуть индекс
return(index);
}
//+------------------------------------------------------------------+
//| Возвращает индекс столбца нажатой ячейки |
//+------------------------------------------------------------------+
int CTable::PressedCellColumnIndex(void)
{
int index=0;
//--- Получим относительную X-координату под курсором мыши
int x=m_mouse.RelativeX(m_table);
//--- Определим ячейку, на которой нажали
for(uint c=0; c<m_columns_total; c++)
{
//--- Если на этой ячейке нажали, запомним индекс столбца
if(x>=m_columns[c].m_x && x<=m_columns[c].m_x2)
{
index=(int)c;
break;
}
}
//--- Вернуть индекс столбца
return(index);
}
//+------------------------------------------------------------------+
//| Проверяет, был ли при нажатии задействован элемент в ячейке |
//+------------------------------------------------------------------+
bool CTable::CheckCellElement(const int column_index,const int row_index,const bool double_click=false)
{
//--- Выйти, если в ячейке нет элемента управления
if(m_columns[column_index].m_rows[row_index].m_type==CELL_SIMPLE)
return(false);
//---
switch(m_columns[column_index].m_rows[row_index].m_type)
{
//--- Если это ячейка-кнопка
case CELL_BUTTON :
{
if(!CheckPressedButton(column_index,row_index,double_click))
return(false);
//---
break;
}
//--- Если это ячейка-чекбокс
case CELL_CHECKBOX :
{
if(!CheckPressedCheckBox(column_index,row_index,double_click))
return(false);
//---
break;
}
//--- Если это ячейка с полем ввода
case CELL_EDIT :
{
if(!CheckPressedEdit(column_index,row_index,double_click))
return(false);
//---
break;
}
//--- Если это ячейка с комбо-боксом
case CELL_COMBOBOX :
{
if(!CheckPressedCombobox(column_index,row_index,double_click))
return(false);
//---
break;
}
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Проверяет, было ли нажатие на кнопке в ячейке |
//+------------------------------------------------------------------+
bool CTable::CheckPressedButton(const int column_index,const int row_index,const bool double_click=false)
{
//--- Выйти, если нет картинок в ячейке
if(ImagesTotal(column_index,row_index)<1)
{
::Print(__FUNCTION__," > Установите минимум одну картинку для ячейки-кнопки!");
return(false);
}
//--- Получим относительные координаты под курсором мыши
int x=m_mouse.RelativeX(m_table);
//--- Получим правую границу картинки
int image_x =int(m_columns[column_index].m_x+m_columns[column_index].m_image_x_offset);
int image_x2 =int(image_x+m_columns[column_index].m_rows[row_index].m_images[0].Width());
//--- Выйти, если нажали не на картинке
if(x>image_x2)
return(false);
else
{
//--- Если это не двойной клик, отправим сообщение
if(!double_click)
{
int image_index=m_columns[column_index].m_rows[row_index].m_selected_image;
::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElementBase::Id(),image_index,"cell"+string(column_index)+"_"+string(row_index));
}
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Проверяет, было ли нажатие на чекбоксе в ячейке |
//+------------------------------------------------------------------+
bool CTable::CheckPressedCheckBox(const int column_index,const int row_index,const bool double_click=false)
{
//--- Выйти, если нет картинок в ячейке
if(ImagesTotal(column_index,row_index)<2)
{
::Print(__FUNCTION__," > Установите минимум две картинки для ячейки-чекбокса!");
return(false);
}
//--- Получим относительные координаты под курсором мыши
int x=m_mouse.RelativeX(m_table);
//--- Получим правую границу картинки
int image_x =int(m_columns[column_index].m_x+m_icon_x_gap);
int image_x2 =int(image_x+m_columns[column_index].m_rows[row_index].m_images[0].Width());
//--- Выйти, если (1) нажали не на картинке и (2) это не двойной клик
if(x>image_x2 && !double_click)
return(false);
else
{
//--- Текущий индекс выбранной картинки
int image_i=m_columns[column_index].m_rows[row_index].m_selected_image;
//--- Определим следующий индекс для картинки
int next_i=(image_i<ImagesTotal(column_index,row_index)-1)?++image_i : 0;
//--- Выбрать следующую картинку
//if(m_selectable_row)
// {
// ChangeImage(column_index,row_index,next_i);
// RedrawRow(true);
// }
//else
ChangeImage(column_index,row_index,next_i,true);
//--- Обновить таблицу
m_table.Update();
//--- Отправим сообщение об этом
::EventChartCustom(m_chart_id,ON_CLICK_CHECKBOX,CElementBase::Id(),next_i,string(column_index)+"_"+string(row_index));
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Проверяет, было ли нажатие на поле ввода в ячейке |
//+------------------------------------------------------------------+
bool CTable::CheckPressedEdit(const int column_index,const int row_index,const bool double_click=false)
{
//--- Выйти, если это не двойной клик
if(!double_click)
return(false);
//--- Сохранить индексы
m_last_edit_row_index =row_index;
m_last_edit_column_index =column_index;
//--- Сдвиг по двум осям
int x_offset =(int)::ObjectGetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_XOFFSET);
int y_offset =(int)::ObjectGetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_YOFFSET);
//--- Установить новые координаты
m_edit.XGap(m_columns[column_index].m_x-x_offset);
m_edit.YGap(m_rows[row_index].m_y+((m_show_headers)? m_header_y_size : 0)-y_offset);
//--- Размеры
int x_size =m_columns[column_index].m_x2-m_columns[column_index].m_x+1;
int y_size =m_cell_y_size+1;
//--- Установить размер
m_edit.GetTextBoxPointer().ChangeSize(x_size,y_size);
//--- Установить значение из ячейки таблицы
m_edit.SetValue(m_columns[column_index].m_rows[row_index].m_full_text);
//--- Активировать поле ввода
m_edit.GetTextBoxPointer().ActivateTextBox();
//--- Установить фокус
m_edit.GetTextBoxPointer().MouseFocus(true);
//--- Показать поле ввода
m_edit.Reset();
//--- Перерисовать график
m_chart.Redraw();
return(true);
}
//+------------------------------------------------------------------+
//| Проверяет, было ли нажатие на комбо-боксе в ячейке |
//+------------------------------------------------------------------+
bool CTable::CheckPressedCombobox(const int column_index,const int row_index,const bool double_click=false)
{
//--- Выйти, если это не двойной клик
if(!double_click)
return(false);
//--- Сохранить индексы
m_last_edit_row_index =row_index;
m_last_edit_column_index =column_index;
//--- Сдвиг по двум осям
int x_offset =(int)::ObjectGetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_XOFFSET);
int y_offset =(int)::ObjectGetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_YOFFSET);
//--- Установить новые координаты
m_combobox.XGap(m_columns[column_index].m_x-x_offset);
m_combobox.YGap(m_rows[row_index].m_y+((m_show_headers)? m_header_y_size : 0)-y_offset);
//--- Установить размер кнопке
int x_size =m_columns[column_index].m_x2-m_columns[column_index].m_x+1;
int y_size =m_cell_y_size+1;
m_combobox.GetButtonPointer().ChangeSize(x_size,y_size);
//--- Установить размер списку
y_size=m_combobox.GetListViewPointer().YSize();
m_combobox.GetListViewPointer().ChangeSize(x_size,y_size);
//--- Установить размер списка ячейки
int total=::ArraySize(m_columns[column_index].m_rows[row_index].m_value_list);
m_combobox.GetListViewPointer().Rebuilding(total);
//--- Установить список из ячейки
for(int i=0; i<total; i++)
m_combobox.GetListViewPointer().SetValue(i,m_columns[column_index].m_rows[row_index].m_value_list[i]);
//--- Установить пункт из ячейки
int index=m_columns[column_index].m_rows[row_index].m_selected_item;
m_combobox.SelectItem(index);
//--- Обновить элемент
m_combobox.GetButtonPointer().MouseFocus(true);
m_combobox.GetButtonPointer().Update(true);
m_combobox.GetListViewPointer().Update(true);
//--- Показать поле ввода
m_combobox.Reset();
//--- Перерисовать график
m_chart.Redraw();
//--- Отправим сообщение об изменении в графическом интерфейсе
::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
return(true);
}
//+------------------------------------------------------------------+
//| Алгоритм быстрой сортировки |
//+------------------------------------------------------------------+
void CTable::QuickSort(uint beg,uint end,uint column,const ENUM_CSORT_MODE mode=SORT_ASCEND)
{
uint r1 =beg;
uint r2 =end;
uint c =column;
string temp =NULL;
string value =NULL;
uint data_total =m_rows_total-1;
//--- Выполнять алгоритм, пока левый индекс меньше самого крайнего правого индекса
while(r1<end)
{
//--- Получим значение из середины ряда
value=m_columns[c].m_rows[(beg+end)>>1].m_full_text;
//--- Выполнять алгоритм, пока левый индекс меньше найденного правого индекса
while(r1<r2)
{
//--- Смещать индекс вправо, пока находим значение по указанному условию
while(CheckSortCondition(c,r1,value,(mode==SORT_ASCEND)? false : true))
{
//--- Контроль выхода за границы массива
if(r1==data_total)
break;
r1++;
}
//--- Смещать индекс влево, пока находим значение по указанному условию
while(CheckSortCondition(c,r2,value,(mode==SORT_ASCEND)? true : false))
{
//--- Контроль выхода за границы массива
if(r2==0)
break;
r2--;
}
//--- Если левый индекс ещё не больше правого
if(r1<=r2)
{
//--- Поменять значения местами
Swap(r1,r2);
//--- Если дошли до предела слева
if(r2==0)
{
r1++;
break;
}
//---
r1++;
r2--;
}
}
//--- Рекурсивное продолжение алгоритма, пока не дойдём до начала диапазона
if(beg<r2)
QuickSort(beg,r2,c,mode);
//--- Cужение диапазона для следующей итерации
beg=r1;
r2=end;
}
}
//+------------------------------------------------------------------+
//| Сравнение значений по указанному условию сортировки |
//+------------------------------------------------------------------+
//| direction: true (>), false (<) |
//+------------------------------------------------------------------+
bool CTable::CheckSortCondition(uint column_index,uint row_index,const string check_value,const bool direction)
{
bool condition=false;
//---
switch(m_columns[column_index].m_data_type)
{
case TYPE_STRING :
{
string v1=m_columns[column_index].m_rows[row_index].m_full_text;
string v2=check_value;
condition=(direction)? v1>v2 : v1<v2;
break;
}
//---
case TYPE_DOUBLE :
{
double v1=double(m_columns[column_index].m_rows[row_index].m_full_text);
double v2=double(check_value);
condition=(direction)? v1>v2 : v1<v2;
break;
}
//---
case TYPE_DATETIME :
{
datetime v1=::StringToTime(m_columns[column_index].m_rows[row_index].m_full_text);
datetime v2=::StringToTime(check_value);
condition=(direction)? v1>v2 : v1<v2;
break;
}
//---
default :
{
long v1=(long)m_columns[column_index].m_rows[row_index].m_full_text;
long v2=(long)check_value;
condition=(direction)? v1>v2 : v1<v2;
break;
}
}
//---
return(condition);
}
//+------------------------------------------------------------------+
//| Меняет элементы местами |
//+------------------------------------------------------------------+
void CTable::Swap(uint r1,uint r2)
{
//--- Пройдёмся в цикле по всем столбцам
for(uint c=0; c<m_columns_total; c++)
{
//--- Меняем местами полный текст
string temp_text =m_columns[c].m_rows[r1].m_full_text;
m_columns[c].m_rows[r1].m_full_text =m_columns[c].m_rows[r2].m_full_text;
m_columns[c].m_rows[r2].m_full_text =temp_text;
//--- Меняем местами краткий текст
temp_text =m_columns[c].m_rows[r1].m_short_text;
m_columns[c].m_rows[r1].m_short_text =m_columns[c].m_rows[r2].m_short_text;
m_columns[c].m_rows[r2].m_short_text =temp_text;
//--- Меняем местами кол-во знаков после запятой
uint temp_digits =m_columns[c].m_rows[r1].m_digits;
m_columns[c].m_rows[r1].m_digits =m_columns[c].m_rows[r2].m_digits;
m_columns[c].m_rows[r2].m_digits =temp_digits;
//--- Меняем местами цвет текста
color temp_text_color =m_columns[c].m_rows[r1].m_text_color;
m_columns[c].m_rows[r1].m_text_color =m_columns[c].m_rows[r2].m_text_color;
m_columns[c].m_rows[r2].m_text_color =temp_text_color;
//--- Меняем местами цвет фона
color temp_back_color =m_columns[c].m_rows[r1].m_back_color;
m_columns[c].m_rows[r1].m_back_color =m_columns[c].m_rows[r2].m_back_color;
m_columns[c].m_rows[r2].m_back_color =temp_back_color;
//--- Меняем местами индекс выбранной картинки
int temp_selected_image =m_columns[c].m_rows[r1].m_selected_image;
m_columns[c].m_rows[r1].m_selected_image =m_columns[c].m_rows[r2].m_selected_image;
m_columns[c].m_rows[r2].m_selected_image =temp_selected_image;
//--- Проверим, есть ли картинки в ячейках
int r1_images_total=::ArraySize(m_columns[c].m_rows[r1].m_images);
int r2_images_total=::ArraySize(m_columns[c].m_rows[r2].m_images);
//--- Переходим к следующему столбцу, если в обеих ячейках нет картинок
if(r1_images_total<1 && r2_images_total<1)
continue;
//--- Меняем местами картинки
CImage r1_temp_images[];
//---
::ArrayResize(r1_temp_images,r1_images_total);
for(int i=0; i<r1_images_total; i++)
ImageCopy(r1_temp_images,m_columns[c].m_rows[r1].m_images,i);
//---
::ArrayResize(m_columns[c].m_rows[r1].m_images,r2_images_total);
for(int i=0; i<r2_images_total; i++)
ImageCopy(m_columns[c].m_rows[r1].m_images,m_columns[c].m_rows[r2].m_images,i);
//---
::ArrayResize(m_columns[c].m_rows[r2].m_images,r1_images_total);
for(int i=0; i<r1_images_total; i++)
ImageCopy(m_columns[c].m_rows[r2].m_images,r1_temp_images,i);
}
}
//+------------------------------------------------------------------+
//| Рисует элемент |
//+------------------------------------------------------------------+
void CTable::Draw(void)
{
DrawTable();
}
//+------------------------------------------------------------------+
//| Рисует таблицу |
//+------------------------------------------------------------------+
void CTable::DrawTable(const bool only_visible=false)
{
//--- Если не указано перерисовать только видимую часть таблицы
if(!only_visible)
{
//--- Установим индексы строк всей таблицы от самого начала и до конца
m_visible_table_from_index =0;
m_visible_table_to_index =m_rows_total;
}
//--- Получим индексы строк видимой части таблицы
else
VisibleTableIndexes();
//--- Нарисовать фон
CElement::DrawBackground();
//--- Нарисовать рамку
CElement::DrawBorder();
//--- Нарисовать сетку
DrawGrid();
//--- Нарисовать фон строк таблицы
DrawRows();
//--- Нарисовать выделенную строку
DrawSelectedRow();
//--- Нарисовать картинку
DrawImages();
//--- Нарисовать текст
DrawText();
//--- Обновить заголовки, если они включены
if(m_show_headers)
DrawTableHeaders();
}
//+------------------------------------------------------------------+
//| Перерисовывает указанную ячейку таблицы |
//+------------------------------------------------------------------+
void CTable::RedrawCell(const int column_index,const int row_index)
{
//--- Координаты
int x1=m_columns[column_index].m_x+1;
int x2=m_columns[column_index].m_x2-1;
int y1=m_rows[row_index].m_y+1;
int y2=m_rows[row_index].m_y2-1;
//--- Для расчёта координат
int x=0,y=0;
//--- Для проверки фокуса
bool is_row_focus=false;
//--- Если включен режим подсветки строк таблицы
if(m_lights_hover)
{
//--- (1) Получим относительную Y-координату курсора мыши и (2) фокус на указанной строке таблицы
y=m_mouse.RelativeY(m_table);
is_row_focus=(y>m_rows[row_index].m_y && y<=m_rows[row_index].m_y2);
}
//--- Нарисовать фон ячейки
m_table.FillRectangle(x1,y1,x2,y2,RowColorCurrent(column_index,row_index,is_row_focus));
//--- Рисуем картинку, если (1) она есть в этой ячейке и (2) в этом столбце выравнивание текста по левому краю
if(ImagesTotal(column_index,row_index)>0 && m_columns[column_index].m_text_align==ALIGN_LEFT)
CTable::DrawImage(column_index,row_index);
//--- Получим способ выравнивания текста
uint text_align=TextAlign(column_index,TA_TOP);
//--- Рисуем текст
for(uint c=0; c<m_columns_total; c++)
{
//--- Получим X-координату текста
x=TextX(c);
//--- Остановим цикл
if(c==column_index)
break;
}
//--- (1) Рассчитать Y-координату и (2) нарисовать текст
y=y1+m_label_y_gap-1;
m_table.TextOut(x,y,m_columns[column_index].m_rows[row_index].m_short_text,TextColor(column_index,row_index),text_align);
}
//+------------------------------------------------------------------+
//| Рисует указанный ряд таблицы по указанному режиму |
//+------------------------------------------------------------------+
void CTable::DrawRow(int &indexes[],const int item_index,const int prev_item_index,const bool is_user=true)
{
int x1=0,x2=m_table_x_size-2;
int y1[2]={0},y2[2]={0};
//--- Количество строк и столбцов для рисования
uint rows_total =0;
uint columns_total =m_columns_total-1;
//--- Если это программный метод выделения строки
if(!is_user)
rows_total=(prev_item_index!=WRONG_VALUE && item_index!=prev_item_index)? 2 : 1;
else
rows_total=(item_index!=WRONG_VALUE && prev_item_index!=WRONG_VALUE && item_index!=prev_item_index)? 2 : 1;
//--- Рисуем фон строк
for(uint r=0; r<rows_total; r++)
{
//--- Расчёт координат верхней и нижней границ строки
y1[r] =m_rows[indexes[r]].m_y+1;
y2[r] =m_rows[indexes[r]].m_y2-1;
//--- Определим фокус на строке относительно режима подсветки
bool is_item_focus=false;
if(!m_lights_hover)
is_item_focus=(indexes[r]==item_index && item_index!=WRONG_VALUE);
else
is_item_focus=(item_index==WRONG_VALUE)?(indexes[r]==prev_item_index) :(indexes[r]==item_index);
//--- Нарисовать фон ячейки
for(uint c=0; c<m_columns_total; c++)
{
x1=m_columns[c].m_x+((c>0)? 1 : 0);
x2=m_columns[c].m_x2-1;
m_table.FillRectangle(x1,y1[r],x2,y2[r],RowColorCurrent(c,indexes[r],(is_user)? is_item_focus : false));
}
}
//--- Рисуем границы
for(uint r=0; r<rows_total; r++)
{
for(uint c=0; c<columns_total; c++)
m_table.Line(m_columns[c].m_x2,y1[r],m_columns[c].m_x2,y2[r],::ColorToARGB(m_grid_color));
}
//--- Рисуем картинки
for(uint r=0; r<rows_total; r++)
{
for(uint c=0; c<m_columns_total; c++)
{
//--- Рисуем картинку, если (1) она есть в этой ячейке и (2) в этом столбце выравнивание текста по левому краю
if(ImagesTotal(c,indexes[r])>0 && m_columns[c].m_text_align==ALIGN_LEFT)
CTable::DrawImage(c,indexes[r]);
}
}
//--- Для расчёта координат
int x=0,y=0;
//--- Способ выравнивания текста
uint text_align=0;
//--- Рисуем текст
for(uint c=0; c<m_columns_total; c++)
{
//--- Получим (1) X-координату текста и (2) способ выравнивания текста
x =TextX(c);
text_align =TextAlign(c,TA_TOP);
//---
for(uint r=0; r<rows_total; r++)
{
//--- (1) Рассчитать координату и (2) нарисовать текст
y=m_rows[indexes[r]].m_y+m_label_y_gap;
m_table.TextOut(x,y,m_columns[c].m_rows[indexes[r]].m_short_text,TextColor(c,indexes[r]),text_align);
}
}
}
//+------------------------------------------------------------------+
//| Перерисовывает указанный ряд таблицы по указанному режиму |
//+------------------------------------------------------------------+
void CTable::RedrawRow(const bool is_selected_row=false)
{
//--- Текущий и предыдущий индексы строк
int item_index =WRONG_VALUE;
int prev_item_index =WRONG_VALUE;
//--- Инициализиция индексов строк относительного указанного режима
if(is_selected_row)
{
item_index =m_selected_item;
prev_item_index =m_prev_selected_item;
}
else
{
item_index =m_item_index_focus;
prev_item_index =m_prev_item_index_focus;
}
//--- Выйти, если индексы не определены
if(prev_item_index==WRONG_VALUE && item_index==WRONG_VALUE)
return;
//--- Массив для значений в определённой последовательности
int indexes[2];
//--- Если (1) курсор мыши сдвинулся вниз или (2) в первый раз здесь
if(item_index>m_prev_item_index_focus || item_index==WRONG_VALUE)
{
indexes[0] =(item_index==WRONG_VALUE || prev_item_index!=WRONG_VALUE)? prev_item_index : item_index;
indexes[1] =item_index;
}
//--- Если курсор мыши сдвинулся вверх
else
{
indexes[0] =item_index;
indexes[1] =prev_item_index;
}
//--- Рисует указанный ряд таблицы по указанному режиму
DrawRow(indexes,item_index,prev_item_index);
}
//+------------------------------------------------------------------+
//| Рисует фон рядов таблицы |
//+------------------------------------------------------------------+
void CTable::DrawRows(void)
{
//--- Координаты курсора мыши
int y=0;
//--- Координаты заголовков
int x1=0,x2=m_table_x_size-2;
int y1=0,y2=0;
bool is_row_focus=false;
//--- Получим относительную X-координату под курсором мыши
y=m_mouse.RelativeY(m_table);
//--- Рисование рядов
for(uint r=m_visible_table_from_index; r<m_visible_table_to_index; r++)
{
//--- Расчёт координат границ ряда с сохранением значений
m_rows[r].m_y =(int)(r*m_cell_y_size);
m_rows[r].m_y2 =m_rows[r].m_y+m_cell_y_size;
y1 =m_rows[r].m_y+((r>0)? 1 : 0);
y2 =m_rows[r].m_y2-1;
//--- Проверим фокус
is_row_focus=(m_lights_hover)?(y>y1 && y<y2) : false;
//--- Нарисовать фон ячейки
for(uint c=0; c<m_columns_total; c++)
{
x1 =m_columns[c].m_x+((c>0)? 1 : 0);
x2 =m_columns[c].m_x2-1;
m_table.FillRectangle(x1,y1,x2,y2,RowColorCurrent(c,r,is_row_focus));
}
}
}
//+------------------------------------------------------------------+
//| Рисует выделенный ряд |
//+------------------------------------------------------------------+
void CTable::DrawSelectedRow(void)
{
//--- Выйти, если нет выделенного ряда
if(m_selected_item==WRONG_VALUE || !m_selectable_row)
return;
//--- Зададим начальные координаты для проверки условия
int y_offset=m_selected_item*m_cell_y_size;
//--- Координаты
int x1=0;
int x2=0;
int y1=y_offset+1;
int y2=y_offset+m_cell_y_size-1;
//--- Нарисовать фон ячеек
for(uint c=0; c<m_columns_total; c++)
{
x1 =m_columns[c].m_x+((c>0)? 1 : 0);
x2 =m_columns[c].m_x2-1;
m_table.FillRectangle(x1,y1,x2,y2,::ColorToARGB(m_selected_row_color,m_alpha));
}
}
//+------------------------------------------------------------------+
//| Рисует сетку |
//+------------------------------------------------------------------+
void CTable::DrawGrid(void)
{
//--- Цвет сетки
uint clr=::ColorToARGB(m_grid_color);
//--- Размер холста для рисования
int x_size=m_table_x_size;
int y_size=m_table_y_size-1;
//--- Координаты
int x1=0,x2=0,y1=0,y2=0;
//--- Горизонтальные линии
uint first_index=(m_show_headers)? 0 : 1;
x1=0; y1=0; x2=x_size; y2=0;
for(uint r=first_index; r<m_rows_total; r++)
{
//--- Расчёт координат границ ряда с сохранением значений
m_rows[r].m_y =y1 =(int)(r*m_cell_y_size);
m_rows[r].m_y2 =y2 =y1+m_cell_y_size;
m_table.Line(x1,m_rows[r].m_y,x2,m_rows[r].m_y,clr);
}
//--- Вертикальные линии
x1=0; y1=0; x2=0; y2=y_size;
for(uint i=0; i<m_columns_total; i++)
{
m_columns[i].m_x2=x2=x1+=m_columns[i].m_width;
m_table.Line(x1,y1,x2,y2,clr);
//--- Сохраним X-координату столбца
if(i>0)
{
uint prev_i=i-1;
m_columns[i].m_x=m_columns[prev_i].m_x+m_columns[prev_i].m_width;
}
}
}
//+------------------------------------------------------------------+
//| Рисует все изображения таблицы |
//+------------------------------------------------------------------+
void CTable::DrawImages(void)
{
//--- Для расчёта координат
int x=0,y=0;
//--- Столбцы
for(int c=0; c<(int)m_columns_total; c++)
{
//--- Если выравнивание текста не по левому краю, перейти к следующему столбцу
if(m_columns[c].m_text_align!=ALIGN_LEFT)
continue;
//--- Строки
for(int r=(int)m_visible_table_from_index; r<(int)m_visible_table_to_index; r++)
{
//--- Перейти к следующему, если в этой ячейке нет картинок
if(ImagesTotal(c,r)<1)
continue;
//--- Выбранная картинка в ячейке (по умолчанию выбрана первая [0])
int selected_image=m_columns[c].m_rows[r].m_selected_image;
//--- Перейти к следующему, если массив пикселей пуст
if(m_columns[c].m_rows[r].m_images[selected_image].DataTotal()<1)
continue;
//--- Нарисовать картинку
CTable::DrawImage(c,r);
}
}
}
//+------------------------------------------------------------------+
//| Рисует изображение в указанной ячейке |
//+------------------------------------------------------------------+
void CTable::DrawImage(const int column_index,const int row_index)
{
//--- Расчёт координат
int x =m_columns[column_index].m_x+m_columns[column_index].m_image_x_offset;
int y =m_rows[row_index].m_y+m_columns[column_index].m_image_y_offset;
//--- Выбранная картинка в ячейке и её размеры
int selected_image =m_columns[column_index].m_rows[row_index].m_selected_image;
uint image_height =m_columns[column_index].m_rows[row_index].m_images[selected_image].Height();
uint image_width =m_columns[column_index].m_rows[row_index].m_images[selected_image].Width();
//--- Рисуем
for(uint ly=0,i=0; ly<image_height; ly++)
{
for(uint lx=0; lx<image_width; lx++,i++)
{
//--- Если нет цвета, перейти к следующему пикселю
if(m_columns[column_index].m_rows[row_index].m_images[selected_image].Data(i)<1)
continue;
//--- Получаем цвет нижнего слоя (фона ячейки) и цвет указанного пикселя картинки
uint background =(row_index==m_selected_item)? m_selected_row_color : m_canvas.PixelGet(x+lx,y+ly);
uint pixel_color =m_columns[column_index].m_rows[row_index].m_images[selected_image].Data(i);
//--- Смешиваем цвета
uint foreground=::ColorToARGB(m_clr.BlendColors(background,pixel_color));
//--- Рисуем пиксель наслаиваемого изображения
m_table.PixelSet(x+lx,y+ly,foreground);
}
}
}
//+------------------------------------------------------------------+
//| Рисует текст |
//+------------------------------------------------------------------+
void CTable::DrawText(void)
{
//--- Для расчёта координат и отступов
int x=0,y=0;
uint text_align=0;
//--- Свойства шрифта
m_table.FontSet(CElement::Font(),-CElement::FontSize()*10,FW_NORMAL);
//--- Столбцы
for(uint c=0; c<m_columns_total; c++)
{
//--- Получим X-координату текста
x=TextX(c);
//--- Получим способ выравнивания текста
text_align=TextAlign(c,TA_TOP);
//--- Ряды
for(uint r=m_visible_table_from_index; r<m_visible_table_to_index; r++)
{
//--- Рассчитаем Y-координату
y=m_rows[r].m_y+m_label_y_gap;
//--- Нарисовать текст
m_table.TextOut(x,y,Text(c,r),TextColor(c,r),text_align);
}
//--- Обнулить координату Y для следующего цикла
y=0;
}
}
//+------------------------------------------------------------------+
//| Рисует заголовки таблицы |
//+------------------------------------------------------------------+
void CTable::DrawTableHeaders(void)
{
//--- Нарисовать заголовки
DrawHeaders();
//--- Нарисовать сетку
DrawHeadersGrid();
//--- Нарисовать текст заголовков
DrawHeadersText();
//--- Нарисовать признак возможности сортировки таблицы
DrawSignSortedData();
}
//+------------------------------------------------------------------+
//| Рисует фон заголовков |
//+------------------------------------------------------------------+
void CTable::DrawHeaders(void)
{
//--- Если не в фокусе, сбросить цвет заголовков
if(!m_headers.MouseFocus() && m_column_resize_control==WRONG_VALUE)
{
m_headers.Erase(::ColorToARGB(m_headers_color,m_alpha));
return;
}
//--- Для проверки фокуса над заголовками
bool is_header_focus=false;
//--- Координаты курсора мыши
int x=0;
//--- Координаты
int y1=0,y2=m_header_y_size;
//--- Получим относительную X-координату под курсором мыши
if(::CheckPointer(m_mouse)!=POINTER_INVALID)
x=m_mouse.RelativeX(m_headers);
//--- Очистить фон заголовков
m_headers.Erase(::ColorToARGB(clrNONE,m_alpha));
//--- Отступ с учётом режима изменения ширины столбцов
int sep_x_offset=(m_column_resize_mode)? m_sep_x_offset : 0;
//--- Рисуем фон заголовков
for(uint i=0; i<m_columns_total; i++)
{
//--- Проверим фокус
if(is_header_focus=x>m_columns[i].m_x+((i!=0)? sep_x_offset : 0) && x<=m_columns[i].m_x2+sep_x_offset)
m_prev_header_index_focus=(int)i;
//--- Определим цвет заголовка
uint clr=(i==m_column_resize_control)? ::ColorToARGB(m_headers_color_hover,m_alpha) : HeaderColorCurrent(is_header_focus);
//--- Нарисовать фон заголовка
m_headers.FillRectangle(m_columns[i].m_x,y1,m_columns[i].m_x2,y2,clr);
}
}
//+------------------------------------------------------------------+
//| Рисует сетку заголовков таблицы |
//+------------------------------------------------------------------+
void CTable::DrawHeadersGrid(void)
{
//--- Цвет сетки
uint clr=::ColorToARGB(m_grid_color);
//--- Координаты
int x1=0,x2=0,y1=0,y2=0;
x2=m_table_x_size-1;
y2=m_header_y_size-1;
//--- Нарисовать рамку
m_headers.Line(x1,y2,x2,y2,clr);
//--- Разделительные линии
x2=x1=m_columns[0].m_width;
for(uint i=1; i<m_columns_total; i++)
m_headers.Line(m_columns[i].m_x,y1,m_columns[i].m_x,y2,clr);
}
//+------------------------------------------------------------------+
//| Рисует признак возможности сортировки таблицы |
//+------------------------------------------------------------------+
void CTable::DrawSignSortedData(void)
{
//--- Выйти, если (1) сортировка отключена или (2) ещё не была осуществлена
if(!m_is_sort_mode || m_is_sorted_column_index==WRONG_VALUE)
return;
//--- Выйти, если в ячейках есть поля ввода или комбо-боксы в ячейках
if(m_edit_state && m_combobox_state)
return;
//--- Выйти, если выход за пределы массива
if(m_is_sorted_column_index>=::ArraySize(m_columns))
return;
//--- Расчёт координат
int x =m_columns[m_is_sorted_column_index].m_x2-m_sort_arrow_x_gap;
int y =m_sort_arrow_y_gap;
//--- Выбранная картинка по направлению сортировки
int image_index=(m_last_sort_direction==SORT_ASCEND)? 0 : 1;
//--- Рисуем
for(uint ly=0,i=0; ly<m_sort_arrows[image_index].Height(); ly++)
{
for(uint lx=0; lx<m_sort_arrows[image_index].Width(); lx++,i++)
{
//--- Если нет цвета, перейти к следующему пикселю
if(m_sort_arrows[image_index].Data(i)<1)
continue;
//--- Получаем цвет нижнего слоя (фона заголовка) и цвет указанного пикселя картинки
uint background =m_headers.PixelGet(x+lx,y+ly);
uint pixel_color =m_sort_arrows[image_index].Data(i);
//--- Смешиваем цвета
uint foreground=::ColorToARGB(m_clr.BlendColors(background,pixel_color));
//--- Рисуем пиксель наслаиваемого изображения
m_headers.PixelSet(x+lx,y+ly,foreground);
}
}
}
//+------------------------------------------------------------------+
//| Рисует текст заголовков таблицы |
//+------------------------------------------------------------------+
void CTable::DrawHeadersText(void)
{
//--- Для расчёта координат и отступов
int x=0,y=m_header_y_size/2;
int column_offset =0;
uint text_align =0;
//--- Цвет текста
uint clr=::ColorToARGB(m_headers_text_color);
//--- Свойства шрифта
m_headers.FontSet(CElement::Font(),-CElement::FontSize()*10,FW_NORMAL);
//--- Нарисовать текст
for(uint c=0; c<m_columns_total; c++)
{
//--- Получим X-координату текста
x=TextX(c,true);
//--- Получим способ выравнивания текста
text_align=TextAlign(c,TA_VCENTER);
//--- Нарисовать название столбца
m_headers.TextOut(x,y,CorrectingText(c,0,true),clr,text_align);
}
}
//+------------------------------------------------------------------+
//| Изменение цвета объектов таблицы |
//+------------------------------------------------------------------+
void CTable::ChangeObjectsColor(void)
{
//--- Отслеживаем изменение цвета, только если не в режиме изменения ширины столбца
if(m_column_resize_control!=WRONG_VALUE)
return;
//--- Изменение цвета заголовков
ChangeHeadersColor();
//--- Изменение цвета рядов при наведении курсора мыши
ChangeRowsColor();
}
//+------------------------------------------------------------------+
//| Изменение цвета заголовков при наведении курсора мыши |
//+------------------------------------------------------------------+
void CTable::ChangeHeadersColor(void)
{
//--- Выйти, если заголовки отключены
if(!m_show_headers)
return;
//--- Если указатель курсора активирован
if(m_column_resize_control==WRONG_VALUE &&
m_column_resize.IsVisible() && m_mouse.LeftButtonState())
{
//--- Запомним индекс захваченного столбца
m_column_resize_control=m_prev_header_index_focus;
//--- Отправим сообщение на определение доступных элементов
::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),0,"");
//--- Отправим сообщение об изменении в графическом интерфейсе
::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
return;
}
//--- Если не в фокусе
if(!m_headers.MouseFocus())
{
//--- Если ещё не отмечено, что не в фокусе
if(m_prev_header_index_focus!=WRONG_VALUE)
{
//--- Сбросить фокус
m_prev_header_index_focus=WRONG_VALUE;
//--- Изменить цвет
DrawTableHeaders();
m_headers.Update();
}
}
//--- Если в фокусе
else
{
//--- Проверить фокус над заголовками
CheckHeaderFocus();
//--- Если нет фокуса
if(m_prev_header_index_focus==WRONG_VALUE)
{
//--- Изменить цвет
DrawTableHeaders();
m_headers.Update();
}
}
}
//+------------------------------------------------------------------+
//| Изменение цвета рядов при наведении курсора мыши |
//+------------------------------------------------------------------+
void CTable::ChangeRowsColor(void)
{
//--- Выйти, если отключена подсветка строк при наведении
if(!m_lights_hover)
return;
//--- Если не в фокусе
if(!m_table.MouseFocus())
{
//--- Если ещё не отмечено, что не в фокусе
if(m_prev_item_index_focus!=WRONG_VALUE)
{
m_item_index_focus=WRONG_VALUE;
//--- Изменить цвет
RedrawRow();
m_table.Update();
//--- Сбросить фокус
m_prev_item_index_focus=WRONG_VALUE;
}
}
//--- Если в фокусе
else
{
//--- Проверить фокус над строками
if(m_item_index_focus==WRONG_VALUE)
{
//--- Получим индекс строки в фокусе
m_item_index_focus=CheckRowFocus();
//--- Изменить цвет строки
RedrawRow();
m_table.Update();
//--- Сохранить как предыдущий индекс в фокусе
m_prev_item_index_focus=m_item_index_focus;
return;
}
//--- Получим относительную Y-координату под курсором мыши
int y=m_mouse.RelativeY(m_table);
//--- Проверка фокуса
bool condition=(y>m_rows[m_item_index_focus].m_y && y<=m_rows[m_item_index_focus].m_y2);
//--- Если фокус изменился
if(!condition)
{
//--- Получим индекс строки в фокусе
m_item_index_focus=CheckRowFocus();
//--- Изменить цвет строки
RedrawRow();
m_table.Update();
//--- Сохранить как предыдущий индекс в фокусе
m_prev_item_index_focus=m_item_index_focus;
}
}
}
//+------------------------------------------------------------------+
//| Проверка фокуса на заголовке |
//+------------------------------------------------------------------+
void CTable::CheckHeaderFocus(void)
{
//--- Выйти, если (1) заголовки отключены или (2) начали изменение ширины столбца
if(!m_show_headers || m_column_resize_control!=WRONG_VALUE)
return;
//--- Получим относительную X-координату под курсором мыши
int x=m_mouse.RelativeX(m_headers);
//--- Отступ с учётом режима изменения ширины столбцов
int sep_x_offset=(m_column_resize_mode)? m_sep_x_offset : 0;
//--- Ищем фокус
for(uint i=0; i<m_columns_total; i++)
{
//--- Если фокус заголовка изменился
if((x>m_columns[i].m_x+sep_x_offset && x<=m_columns[i].m_x2+sep_x_offset) && m_prev_header_index_focus!=i)
{
m_prev_header_index_focus=WRONG_VALUE;
break;
}
}
}
//+------------------------------------------------------------------+
//| Определение индексов видимой области таблицы |
//+------------------------------------------------------------------+
void CTable::VisibleTableIndexes(void)
{
//--- Определяем границы с учётом смещения видимой области таблицы
int yoffset1 =(int)::ObjectGetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_YOFFSET);
int yoffset2 =yoffset1+m_table_visible_y_size;
//--- Определяем первый и последний индексы видимой области таблицы
m_visible_table_from_index =int(double(yoffset1/m_cell_y_size));
m_visible_table_to_index =int(double(yoffset2/m_cell_y_size));
//--- Нижний индекс на один больше, если не выходим из диапазона
m_visible_table_to_index=(m_visible_table_to_index+1>m_rows_total)? m_rows_total : m_visible_table_to_index+1;
}
//+------------------------------------------------------------------+
//| Проверка фокуса на рядах таблицы |
//+------------------------------------------------------------------+
int CTable::CheckRowFocus(void)
{
int item_index_focus=WRONG_VALUE;
//--- Получим относительную Y-координату под курсором мыши
int y=m_mouse.RelativeY(m_table);
///--- Получим индексы локальной области таблицы
VisibleTableIndexes();
//--- Ищем фокус
for(uint i=m_visible_table_from_index; i<m_visible_table_to_index; i++)
{
//--- Если фокус строки изменился
if(y>m_rows[i].m_y && y<=m_rows[i].m_y2)
{
item_index_focus=(int)i;
break;
}
}
//--- Вернём индекс строки в фокусе
return(item_index_focus);
}
//+------------------------------------------------------------------+
//| Проверка фокуса на границах заголовков для изменения их ширины |
//+------------------------------------------------------------------+
void CTable::CheckColumnResizeFocus(void)
{
//--- Выйти, если режим изменения ширины столбцов отключен
if(!m_column_resize_mode || m_auto_correct_columns_width_mode)
return;
//--- Выйти, если начали изменение ширины столбца
if(m_column_resize_control!=WRONG_VALUE)
{
//--- Обновить координаты указателя
m_column_resize.Moving(m_mouse.X(),m_mouse.Y());
return;
}
//--- Для проверки фокуса над границами заголовков
bool is_focus=false;
//--- Если курсор мыши в области заголовков
if(m_headers.MouseFocus())
{
//--- Получим относительную X-координату под курсором мыши
int x=m_mouse.RelativeX(m_headers);
//--- Ищем фокус
for(uint i=0; i<m_columns_total; i++)
{
if(is_focus=x>m_columns[i].m_x2-m_sep_x_offset && x<=m_columns[i].m_x2+m_sep_x_offset)
break;
}
//--- Если есть фокус
if(is_focus)
{
//--- Обновить координаты указателя и сделать его видимым
m_column_resize.Moving(m_mouse.X(),m_mouse.Y());
m_column_resize.Reset();
m_chart.Redraw();
}
else
{
m_column_resize.Hide();
}
}
//--- Скрыть указатель, если нет фокуса
else if(!is_focus)
m_column_resize.Hide();
}
//+------------------------------------------------------------------+
//| Изменяет ширину захваченного столбца |
//+------------------------------------------------------------------+
void CTable::ChangeColumnWidth(void)
{
//--- Выйти, если заголовки отключены
if(!m_show_headers)
return;
//--- Проверка фокуса на границах заголовков
CheckColumnResizeFocus();
//--- Если закончили, сбросим значения
if(m_column_resize_control==WRONG_VALUE)
{
m_column_resize_x_fixed =0;
m_column_resize_prev_width =0;
m_column_resize_prev_thumb =0;
return;
}
//--- Получим относительную X-координату под курсором мыши
int x=m_mouse.RelativeX(m_headers);
//--- Если только начали изменение ширины столбца
if(m_column_resize_x_fixed<1)
{
//--- Запомним текущую X-координату и ширину столбца
m_column_resize_x_fixed =x;
m_column_resize_prev_width =m_columns[m_column_resize_control].m_width;
m_column_resize_prev_thumb =m_scrollh.CurrentPos();
}
//--- Рассчитаем новую ширину для столбца
int new_width=m_column_resize_prev_width+(x-m_column_resize_x_fixed);
//--- Оставить без изменений, если меньше установленного ограничения
if(new_width<m_min_column_width)
return;
//--- Сохраним новую ширину столбца
m_columns[m_column_resize_control].m_width=new_width;
//--- Рассчитать размеры таблицы
CalculateTableSize();
//--- Установить новый размер таблице
ChangeTableSize();
//--- Корректируем ползунок полосы прокрутки, если его позиция изменилась
if(m_scrollh.CurrentPos()!=m_column_resize_prev_thumb)
{
m_scrollh.MovingThumb(m_column_resize_prev_thumb);
//--- Корректировка таблицы относительно полос прокрутки
ShiftTable();
}
//--- Нарисуем таблицу
DrawTable(true);
Update();
if(m_scrollh.IsScroll())
m_scrollh.Update(true);
if(m_scrollv.IsScroll())
m_scrollv.Update(true);
}
//+------------------------------------------------------------------+
//| Проверить выход из диапазона столбцов |
//+------------------------------------------------------------------+
template<typename T>
int CTable::CheckArraySize(const T &array[])
{
int total=0;
int array_size=::ArraySize(array);
//--- Выйти, если передан массив нулевого размера
if(array_size<1)
return(WRONG_VALUE);
//--- Скорректировать значение для предотвращения выхода из диапазона массива
total=(array_size<(int)m_columns_total)? array_size :(int)m_columns_total;
//--- Вернуть скорректированный размер массива
return(total);
}
//+------------------------------------------------------------------+
//| Проверить выход из диапазона столбцов |
//+------------------------------------------------------------------+
bool CTable::CheckOutOfColumnRange(const uint column_index)
{
//--- Проверка на выход из диапазона столбцов
uint csize=::ArraySize(m_columns);
if(csize<1 || column_index>=csize)
return(false);
//---
return(true);
}
//+------------------------------------------------------------------+
//| Проверить выход из диапазона столбцов и рядов |
//+------------------------------------------------------------------+
bool CTable::CheckOutOfRange(const uint column_index,const uint row_index)
{
//--- Проверка на выход из диапазона столбцов
uint csize=::ArraySize(m_columns);
if(csize<1 || column_index>=csize)
return(false);
//--- Проверка на выход из диапазона рядов
uint rsize=::ArraySize(m_columns[column_index].m_rows);
if(rsize<1 || row_index>=rsize)
return(false);
//---
return(true);
}
//+------------------------------------------------------------------+
//| Расчёт с учётом последних изменений и изменение размеров таблицы |
//+------------------------------------------------------------------+
void CTable::RecalculateAndResizeTable(const bool redraw=false)
{
//--- Рассчитать размеры таблицы
CalculateTableSize();
//--- Установить новый размер таблице
ChangeTableSize();
//--- Обновить таблицу
Update(redraw);
//---
if(RowsTotal()>(uint)VisibleRowsTotal())
::EventChartCustom(m_chart_id,ON_CHANGE_GUI,0,0.0,"");
}
//+------------------------------------------------------------------+
//| Инициализация указанного столбца значениями по умолчанию |
//+------------------------------------------------------------------+
void CTable::ColumnInitialize(const uint column_index)
{
//--- Инициализация свойств столбцов значениями по умолчанию
m_columns[column_index].m_x =0;
m_columns[column_index].m_x2 =0;
m_columns[column_index].m_width =m_default_width;
m_columns[column_index].m_data_type =m_default_type_data;
m_columns[column_index].m_text_align =m_default_text_align;
m_columns[column_index].m_text_x_offset =m_label_x_gap;
m_columns[column_index].m_image_x_offset =m_icon_x_gap;
m_columns[column_index].m_image_y_offset =m_icon_y_gap;
m_columns[column_index].m_header_text ="";
}
//+------------------------------------------------------------------+
//| Инициализация указанной ячейки значениями по умолчанию |
//+------------------------------------------------------------------+
void CTable::CellInitialize(const uint column_index,const uint row_index)
{
m_columns[column_index].m_rows[row_index].m_full_text ="";
m_columns[column_index].m_rows[row_index].m_short_text ="";
m_columns[column_index].m_rows[row_index].m_selected_image =0;
m_columns[column_index].m_rows[row_index].m_text_color =m_label_color;
m_columns[column_index].m_rows[row_index].m_back_color =m_back_color;
m_columns[column_index].m_rows[row_index].m_digits =0;
m_columns[column_index].m_rows[row_index].m_type =CELL_SIMPLE;
//--- По умолчанию у ячейки нет картинок
::ArrayFree(m_columns[column_index].m_rows[row_index].m_images);
}
//+------------------------------------------------------------------+
//| Делает копию указанного столбца (source) в новое место (dest.) |
//+------------------------------------------------------------------+
void CTable::ColumnCopy(const uint destination,const uint source)
{
m_columns[destination].m_header_text =m_columns[source].m_header_text;
m_columns[destination].m_width =m_columns[source].m_width;
m_columns[destination].m_data_type =m_columns[source].m_data_type;
m_columns[destination].m_text_align =m_columns[source].m_text_align;
m_columns[destination].m_text_x_offset =m_columns[source].m_text_x_offset;
m_columns[destination].m_image_x_offset =m_columns[source].m_image_x_offset;
m_columns[destination].m_image_y_offset =m_columns[source].m_image_y_offset;
}
//+------------------------------------------------------------------+
//| Делает копию указанной ячейки (source) в новое место (dest.) |
//+------------------------------------------------------------------+
void CTable::CellCopy(const uint column_dest,const uint row_dest,const uint column_source,const uint row_source)
{
m_columns[column_dest].m_rows[row_dest].m_type =m_columns[column_source].m_rows[row_source].m_type;
m_columns[column_dest].m_rows[row_dest].m_digits =m_columns[column_source].m_rows[row_source].m_digits;
m_columns[column_dest].m_rows[row_dest].m_full_text =m_columns[column_source].m_rows[row_source].m_full_text;
m_columns[column_dest].m_rows[row_dest].m_short_text =m_columns[column_source].m_rows[row_source].m_short_text;
m_columns[column_dest].m_rows[row_dest].m_text_color =m_columns[column_source].m_rows[row_source].m_text_color;
m_columns[column_dest].m_rows[row_dest].m_back_color =m_columns[column_source].m_rows[row_source].m_back_color;
m_columns[column_dest].m_rows[row_dest].m_selected_image =m_columns[column_source].m_rows[row_source].m_selected_image;
//--- Копируем размер массива из источника в приёмник
int images_total=::ArraySize(m_columns[column_source].m_rows[row_source].m_images);
::ArrayResize(m_columns[column_dest].m_rows[row_dest].m_images,images_total);
//---
for(int i=0; i<images_total; i++)
{
//--- Копировать, если есть картинки
if(m_columns[column_source].m_rows[row_source].m_images[i].DataTotal()<1)
continue;
//--- Делаем копию картинки
ImageCopy(m_columns[column_dest].m_rows[row_dest].m_images,m_columns[column_source].m_rows[row_source].m_images,i);
}
}
//+------------------------------------------------------------------+
//| Копирует данные изображения из одного массива в другой |
//+------------------------------------------------------------------+
void CTable::ImageCopy(CImage &destination[],CImage &source[],const int index)
{
//--- Копируем пиксели картинки
destination[index].CopyImageData(source[index]);
//--- Копируем свойства картинки
destination[index].Width(source[index].Width());
destination[index].Height(source[index].Height());
destination[index].BmpPath(source[index].BmpPath());
}
//+------------------------------------------------------------------+
//| Возвращает текст |
//+------------------------------------------------------------------+
string CTable::Text(const int column_index,const int row_index)
{
string text="";
//--- Корректируем текст, если не в режиме изменения ширины столбца
if(m_column_resize_control==WRONG_VALUE)
text=CorrectingText(column_index,row_index);
//--- Если же в режиме изменения ширины столбца, то...
else
{
//--- ...корректируем текст только для того столбца, ширину которого изменяем
if(column_index==m_column_resize_control)
text=CorrectingText(column_index,row_index);
//--- Для всех остальных используем уже ранее откорректированный текст
else
text=m_columns[column_index].m_rows[row_index].m_short_text;
}
//--- Вернём текст
return(text);
}
//+------------------------------------------------------------------+
//| Возвращает X-координату текста в указанном столбце |
//+------------------------------------------------------------------+
int CTable::TextX(const int column_index,const bool headers=false)
{
int x=0;
//--- Выравнивание текста в ячейках по установленному режиму для каждого столбца
switch(m_columns[column_index].m_text_align)
{
//--- По центру
case ALIGN_CENTER :
x=m_columns[column_index].m_x+(m_columns[column_index].m_width/2);
break;
//--- Cправа
case ALIGN_RIGHT :
{
int x_offset=0;
//---
if(headers)
{
bool condition=(m_is_sorted_column_index!=WRONG_VALUE && m_is_sorted_column_index==column_index);
x_offset=(condition)? m_label_x_gap+m_sort_arrow_x_gap : m_label_x_gap;
}
else
x_offset=m_columns[column_index].m_text_x_offset;
//---
x=m_columns[column_index].m_x2-x_offset;
break;
}
//--- Слева
case ALIGN_LEFT :
x=m_columns[column_index].m_x+((headers)? m_label_x_gap : m_columns[column_index].m_text_x_offset);
break;
}
//--- Вернуть способ выравнивания
return(x);
}
//+------------------------------------------------------------------+
//| Возвращает способ выравнивания текста в указанном столбце |
//+------------------------------------------------------------------+
uint CTable::TextAlign(const int column_index,const uint anchor)
{
uint text_align=0;
//--- Выравнивание текста для текущего столбца
switch(m_columns[column_index].m_text_align)
{
case ALIGN_CENTER :
text_align=TA_CENTER|anchor;
break;
case ALIGN_RIGHT :
text_align=TA_RIGHT|anchor;
break;
case ALIGN_LEFT :
text_align=TA_LEFT|anchor;
break;
}
//--- Вернуть способ выравнивания
return(text_align);
}
//+------------------------------------------------------------------+
//| Возвращает цвет текста ячейки |
//+------------------------------------------------------------------+
uint CTable::TextColor(const int column_index,const int row_index)
{
uint clr=(row_index==m_selected_item)? m_selected_row_text_color : m_columns[column_index].m_rows[row_index].m_text_color;
//--- Вернуть цвет заголовка
return(::ColorToARGB(clr));
}
//+------------------------------------------------------------------+
//| Возвращает цвет фона ячейки |
//+------------------------------------------------------------------+
uint CTable::BackColor(const int column_index,const int row_index)
{
uint clr=(row_index==m_selected_item)? m_selected_row_color : m_columns[column_index].m_rows[row_index].m_back_color;
//--- Вернуть цвет заголовка
return(::ColorToARGB(clr));
}
//+------------------------------------------------------------------+
//| Возвращает текущий цвет фона заголовка |
//+------------------------------------------------------------------+
uint CTable::HeaderColorCurrent(const bool is_header_focus)
{
uint clr=clrNONE;
//--- Если нет фокуса
if(!is_header_focus || !m_headers.MouseFocus())
clr=m_headers_color;
else
{
//--- Если левая кнопка мыши нажата и не в процессе изменения ширины столбца
bool condition=(m_mouse.LeftButtonState() && m_column_resize_control==WRONG_VALUE);
clr=(condition)? m_headers_color_pressed : m_headers_color_hover;
}
//--- Вернуть цвет заголовка
return(::ColorToARGB(clr,m_alpha));
}
//+------------------------------------------------------------------+
//| Возвращает текущий цвет фона строки |
//+------------------------------------------------------------------+
uint CTable::RowColorCurrent(const int column_index,const int row_index,const bool is_row_focus)
{
//--- Если выделенная строка
if(row_index==m_selected_item)
return(::ColorToARGB(m_selected_row_color,m_alpha));
//--- Цвет ряда
uint clr=m_cell_color;
//--- Если (1) нет фокуса или (2) в процессе изменения ширины столбца или (3) форма заблокирована
bool condition=(!is_row_focus || !m_table.MouseFocus() || m_column_resize_control!=WRONG_VALUE || m_main.CElementBase::IsLocked());
//--- Если включен режим форматирования в стиле "Зебра"
if(m_is_zebra_format_rows!=clrNONE)
{
if(condition)
clr=(row_index%2!=0)? m_is_zebra_format_rows : m_cell_color;
else
clr=m_cell_color_hover;
}
else
{
clr=(condition)? m_columns[column_index].m_rows[row_index].m_back_color : m_cell_color_hover;
}
//--- Вернуть цвет
return(::ColorToARGB(clr,m_alpha));
}
//+------------------------------------------------------------------+
//| Возвращает откорректированный текст по ширине столбца |
//+------------------------------------------------------------------+
string CTable::CorrectingText(const int column_index,const int row_index,const bool headers=false)
{
//--- Получим текущий текст
string corrected_text=(headers)? m_columns[column_index].m_header_text : m_columns[column_index].m_rows[row_index].m_full_text;
//--- Отступы от краёв ячейки по оси X
int x_offset=0;
//---
if(headers)
x_offset=(m_is_sorted_column_index==WRONG_VALUE)? m_label_x_gap*2 : m_label_x_gap+m_sort_arrow_x_gap;
else
x_offset=m_label_x_gap+m_columns[column_index].m_text_x_offset;
//--- Получим указатель на объект холста
CRectCanvas *obj=(headers)? ::GetPointer(m_headers) : ::GetPointer(m_table);
//--- Получим ширину текста
int full_text_width=obj.TextWidth(corrected_text);
//--- Пространство для строки
int space_width=m_columns[column_index].m_width-x_offset;
//--- Если помещаемся в ячейку, сохраним скорректированный текст в отдельный массив и вернём его
if(full_text_width<=space_width)
{
//--- Если это не заголовки, сохраним откорректированный текст
if(!headers)
m_columns[column_index].m_rows[row_index].m_short_text=corrected_text;
//---
return(corrected_text);
}
//--- Если текст не помещается в ячейку, нужно скорректировать его (обрезать лишние символы и добавить многоточие)
else
{
//--- Для работы со строкой
string temp_text="";
//--- Получим длину строки
int total=::StringLen(corrected_text);
//--- Будем удалять у строки по одному символу, пока не достигнем нужной ширины текста
for(int i=total-1; i>=0; i--)
{
//--- Удалим один символ
temp_text=::StringSubstr(corrected_text,0,i);
//--- Если ничего не осталось, оставим пустую строку
if(temp_text=="")
{
corrected_text="";
break;
}
//--- Добавим многоточие перед проверкой
int text_width=obj.TextWidth(temp_text+"...");
//--- Если помещаемся в ячейку
if(text_width<space_width)
{
//--- Сохраняем текст и останавливаем цикл
corrected_text=temp_text+"...";
break;
}
}
}
//--- Если это не заголовки, сохраним откорректированный текст
if(!headers)
m_columns[column_index].m_rows[row_index].m_short_text=corrected_text;
//--- Вернём скорректированный текст
return(corrected_text);
}
//+------------------------------------------------------------------+
//| Перемещение элемента |
//+------------------------------------------------------------------+
void CTable::Moving(const bool only_visible=true)
{
//--- Выйти, если элемент скрыт
if(only_visible)
if(!CElementBase::IsVisible())
return;
//--- Если привязка справа
if(m_anchor_right_window_side)
{
//--- Сохранение координат в полях элемента
CElementBase::X(m_main.X2()-XGap());
//--- Сохранение координат в полях объектов
m_table.X(m_main.X2()-m_table.XGap());
m_headers.X(m_main.X2()-m_headers.XGap());
}
else
{
CElementBase::X(m_main.X()+XGap());
m_table.X(m_main.X()+m_table.XGap());
m_headers.X(m_main.X()+m_headers.XGap());
}
//--- Если привязка снизу
if(m_anchor_bottom_window_side)
{
CElementBase::Y(m_main.Y2()-YGap());
m_table.Y(m_main.Y2()-m_table.YGap());
m_headers.Y(m_main.Y2()-m_headers.YGap());
}
else
{
CElementBase::Y(m_main.Y()+YGap());
m_table.Y(m_main.Y()+m_table.YGap());
m_headers.Y(m_main.Y()+m_headers.YGap());
}
//--- Обновление координат графических объектов
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_XDISTANCE,m_table.X());
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_YDISTANCE,m_table.Y());
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_XDISTANCE,m_headers.X());
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_YDISTANCE,m_headers.Y());
//--- Переместить остальные элементы
CElement::Moving(only_visible);
}
//+------------------------------------------------------------------+
//| Показывает элемент |
//+------------------------------------------------------------------+
void CTable::Show(void)
{
//--- Выйти, если элемент уже видим
if(CElementBase::IsVisible())
return;
//--- Состояние видимости
CElementBase::IsVisible(true);
//--- Перемещение элемента
Moving();
//--- Сделать видимыми все объекты
::ObjectSetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
//---
if(!m_is_disabled_scrolls)
{
if(m_scrollv.IsScroll())
m_scrollv.Show();
if(m_scrollh.IsScroll())
m_scrollh.Show();
}
}
//+------------------------------------------------------------------+
//| Скрывает элемент |
//+------------------------------------------------------------------+
void CTable::Hide(void)
{
//--- Выйти, если элемент уже скрыт
if(!CElementBase::IsVisible())
return;
//--- Скрыть все объекты
::ObjectSetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
m_scrollv.Hide();
m_scrollh.Hide();
//--- Состояние видимости
CElementBase::IsVisible(false);
}
//+------------------------------------------------------------------+
//| Удаление |
//+------------------------------------------------------------------+
void CTable::Delete(void)
{
//--- Удаление графических объектов
m_table.Destroy();
m_canvas.Destroy();
m_headers.Destroy();
m_column_resize.Delete();
//--- Освобождение массивов элемента
for(uint c=0; c<m_columns_total; c++)
{
for(uint r=0; r<m_rows_total; r++)
{
for(int i=0; i<ImagesTotal(c,r); i++)
m_columns[c].m_rows[r].m_images[i].DeleteImageData();
//---
::ArrayFree(m_columns[c].m_rows[r].m_images);
}
}
//---
for(uint c=0; c<m_columns_total; c++)
::ArrayFree(m_columns[c].m_rows);
//---
uint total=ArraySize(m_sort_arrows);
for(uint i=0; i<total; i++)
m_sort_arrows[i].DeleteImageData();
//---
::ArrayFree(m_rows);
::ArrayFree(m_columns);
::ArrayFree(m_sort_arrows);
//--- Инициализация переменных значениями по умолчанию
CElementBase::IsVisible(true);
m_is_sorted_column_index=WRONG_VALUE;
}
//+------------------------------------------------------------------+
//| Установка приоритетов |
//+------------------------------------------------------------------+
void CTable::SetZorders(void)
{
CElement::SetZorders();
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_ZORDER,m_zorder+1);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_ZORDER,m_zorder+1);
}
//+------------------------------------------------------------------+
//| Сброс приоритетов |
//+------------------------------------------------------------------+
void CTable::ResetZorders(void)
{
CElement::ResetZorders();
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_ZORDER,WRONG_VALUE);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_ZORDER,WRONG_VALUE);
}
//+------------------------------------------------------------------+
//| Ускоренная промотка полосы прокрутки |
//+------------------------------------------------------------------+
void CTable::FastSwitching(void)
{
//--- Выйдем, если нет фокуса на списке
if(!CElementBase::MouseFocus())
return;
//--- Вернём счётчик к первоначальному значению, если кнопка мыши отжата
if(!m_mouse.LeftButtonState() || m_scrollv.State() || m_scrollh.State())
m_timer_counter=SPIN_DELAY_MSC;
//--- Если же кнопка мыши нажата
else
{
//--- Увеличим счётчик на установленный интервал
m_timer_counter+=TIMER_STEP_MSC;
//--- Выйдем, если меньше нуля
if(m_timer_counter<0)
return;
//---
bool scroll_v=false,scroll_h=false;
//--- Если прокрутка вверх
if(m_scrollv.GetIncButtonPointer().MouseFocus())
{
m_scrollv.OnClickScrollInc((uint)Id(),0);
scroll_v=true;
}
//--- Если прокрутка вниз
else if(m_scrollv.GetDecButtonPointer().MouseFocus())
{
m_scrollv.OnClickScrollDec((uint)Id(),1);
scroll_v=true;
}
//--- Если прокрутка влево
else if(m_scrollh.GetIncButtonPointer().MouseFocus())
{
m_scrollh.OnClickScrollInc((uint)Id(),2);
scroll_h=true;
}
//--- Если прокрутка вправо
else if(m_scrollh.GetDecButtonPointer().MouseFocus())
{
m_scrollh.OnClickScrollDec((uint)Id(),3);
scroll_h=true;
}
//--- Выйти, если ни одна кнопка не нажата
if(!scroll_v && !scroll_h)
return;
//--- Смещает таблицу
ShiftTable();
//--- Обновить полосы прокрутки
if(scroll_v) m_scrollv.Update(true);
if(scroll_h) m_scrollh.Update(true);
}
}
//+------------------------------------------------------------------+
//| Рассчитывает размеры таблицы |
//+------------------------------------------------------------------+
void CTable::CalculateTableSize(void)
{
//--- Рассчитаем общую ширину и высоту таблицы
CalculateTableXSize();
CalculateTableYSize();
//--- Рассчитывает видимые размеры таблицы (два раза на случай, когда нужно отобразить обе полосы прокрутки)
for(int i=0; i<2; i++)
{
CalculateTableVisibleXSize();
CalculateTableVisibleYSize();
}
}
//+------------------------------------------------------------------+
//| Рассчитывает полный размер таблицы по оси X |
//+------------------------------------------------------------------+
void CTable::CalculateTableXSize(void)
{
//--- Рассчитаем общую ширину таблицы
m_table_x_size=0;
for(uint c=0; c<m_columns_total; c++)
m_table_x_size=m_table_x_size+m_columns[c].m_width;
}
//+------------------------------------------------------------------+
//| Рассчитывает полный размер таблицы по оси Y |
//+------------------------------------------------------------------+
void CTable::CalculateTableYSize(void)
{
//--- Рассчитаем общую высоту таблицы
m_table_y_size=(int)(m_cell_y_size*m_rows_total)+1;
}
//+------------------------------------------------------------------+
//| Рассчитывает видимый размер таблицы по оси X |
//+------------------------------------------------------------------+
void CTable::CalculateTableVisibleXSize(void)
{
if(m_is_disabled_scrolls)
{
m_table_visible_x_size=m_table_x_size-1;
return;
}
//--- Ширина таблицы с учётом наличия вертикальной полосы прокрутки
int x_size=(m_table_y_size>m_table_visible_y_size) ? m_x_size-m_scrollh.ScrollWidth()-2 : m_x_size-2;
//--- Зададим ширину фрейма для показа фрагмента изображения (видимой части таблицы таблицы)
m_table_visible_x_size=x_size;
//--- Корректировка размера видимой части по оси X
m_table_visible_x_size=(m_table_visible_x_size>=m_table_x_size)? m_table_x_size : m_table_visible_x_size;
//--- Сохраним ограничение по смещению
m_shift_x2_limit=m_table_x_size-m_table_visible_x_size;
}
//+------------------------------------------------------------------+
//| Рассчитывает видимый размер таблицы по оси Y |
//+------------------------------------------------------------------+
void CTable::CalculateTableVisibleYSize(void)
{
if(m_is_disabled_scrolls)
{
m_table_visible_y_size=m_table_y_size-1;
return;
}
//--- Расчёт количества шагов для смещения
uint x_size_total =m_table_x_size/m_shift_x_step;
uint visible_x_size_total =m_table_visible_x_size/m_shift_x_step;
//--- Если есть заголовки и гориз. полоса прокрутки, то скорректировать размер элемента по оси Y
int header_y_size=(m_show_headers)? m_header_y_size : 2;
int y_size=(x_size_total>visible_x_size_total) ? m_y_size-header_y_size-m_scrollv.ScrollWidth()-2 : m_y_size-header_y_size-2;
//--- Зададим высоту фрейма для показа фрагмента изображения (видимой части таблицы таблицы)
m_table_visible_y_size=y_size;
//--- Корректировка размера видимой части по оси Y
m_table_visible_y_size=(m_table_visible_y_size>=m_table_y_size)? m_table_y_size : m_table_visible_y_size;
//--- Сохраним ограничение по смещению
m_shift_y2_limit=m_table_y_size-m_table_visible_y_size;
}
//+------------------------------------------------------------------+
//| Изменить основные размеры таблицы |
//+------------------------------------------------------------------+
void CTable::ChangeMainSize(const int x_size,const int y_size)
{
//--- Установить новый размер фону таблицы
CElementBase::XSize(x_size);
CElementBase::YSize(y_size);
m_canvas.XSize(x_size);
m_canvas.YSize(y_size);
m_canvas.Resize(x_size,y_size);
}
//+------------------------------------------------------------------+
//| Изменить размеры таблицы |
//+------------------------------------------------------------------+
void CTable::ChangeTableSize(void)
{
//--- Установить новый размер таблице
m_table.XSize(m_table_visible_x_size);
m_table.YSize(m_table_visible_y_size);
m_headers.XSize(m_table_visible_x_size);
m_headers.YSize(m_header_y_size);
m_table.Resize(m_table_x_size,m_table_y_size);
m_headers.Resize(m_table_x_size,m_header_y_size);
//--- Установим размеры видимой области
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_XSIZE,m_table_visible_x_size);
::ObjectSetInteger(m_chart_id,m_table.ChartObjectName(),OBJPROP_YSIZE,m_table_visible_y_size);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_XSIZE,m_table_visible_x_size);
::ObjectSetInteger(m_chart_id,m_headers.ChartObjectName(),OBJPROP_YSIZE,m_header_y_size);
//--- Изменить размеры полос прокрутки
ChangeScrollsSize();
//--- Корректировка данных
ShiftTable();
}
//+------------------------------------------------------------------+
//| Изменить размеры полос прокрутки |
//+------------------------------------------------------------------+
void CTable::ChangeScrollsSize(void)
{
//--- Расчёт количества шагов для смещения
uint x_size_total =m_table_x_size/m_shift_x_step;
uint visible_x_size_total =m_table_visible_x_size/m_shift_x_step;
uint y_size_total =RowsTotal();
uint visible_y_size_total =VisibleRowsTotal();
//--- Рассчитать размеры полос прокрутки
m_scrollh.Reinit(x_size_total,visible_x_size_total);
m_scrollv.Reinit(y_size_total,visible_y_size_total);
//--- Если горизонтальная полоса прокрутки не нужна
if(!m_scrollh.IsScroll())
{
//--- Скрыть горизонтальную полосу прокрутки
m_scrollh.Hide();
//--- Рассчитать и изменить высоту вертикальной полосы прокрутки
int y_size=CElementBase::YSize()-2;
m_scrollv.ChangeYSize(y_size);
}
else
{
//--- Показать горизонтальную полосу прокрутки
if(CElementBase::IsVisible() && !m_is_disabled_scrolls)
m_scrollh.Show();
//--- Рассчитать и изменить высоту вертикальной полосы прокрутки
int y_size=CElementBase::YSize()-m_scrollh.ScrollWidth()-2;
m_scrollv.ChangeYSize(y_size);
}
//--- Если вертикальная полоса прокрутки не нужна
if(!m_scrollv.IsScroll())
{
//--- Скрыть вертикальную полосу прокрутки
m_scrollv.Hide();
//--- Изменить ширину горизонтальной полосы прокрутки
int x_size=CElementBase::XSize()-1;
m_scrollh.ChangeXSize(x_size);
}
else
{
//--- Показать вертикальную полосу прокрутки
if(CElementBase::IsVisible() && !m_is_disabled_scrolls)
m_scrollv.Show();
//--- Рассчитать и изменить ширину горизонтальной полосы прокрутки
int x_size=CElementBase::XSize()-m_scrollv.ScrollWidth()-1;
m_scrollh.ChangeXSize(x_size);
}
}
//+------------------------------------------------------------------+
//| Изменить ширину по правому краю формы |
//+------------------------------------------------------------------+
void CTable::ChangeWidthByRightWindowSide(void)
{
//--- Выйти, если включен режим фиксации к правому краю формы
if(m_anchor_right_window_side)
return;
//--- Размеры
int x_size =m_main.X2()-m_canvas.X()-m_auto_xresize_right_offset;
int y_size =(m_auto_yresize_mode)? m_main.Y2()-m_canvas.Y()-m_auto_yresize_bottom_offset : m_y_size;
//--- Выйти, если размер меньше указанного
if(x_size<100)
return;
//--- Установить новый размер фона таблицы
ChangeMainSize(x_size,y_size);
//--- Рассчитать размеры таблицы
CalculateTableSize();
//--- Установить новый размер таблице
ChangeTableSize();
//--- Нарисуем таблицу
DrawTable();
if(m_scrollh.IsScroll())
m_scrollh.Update(true);
if(m_scrollv.IsScroll())
m_scrollv.Update(true);
}
//+------------------------------------------------------------------+
//| Изменить высоту по нижнему краю окна |
//+------------------------------------------------------------------+
void CTable::ChangeHeightByBottomWindowSide(void)
{
//--- Выйти, если включен режим фиксации к нижнему краю формы
if(m_anchor_bottom_window_side)
return;
//--- Размеры
int x_size =(m_auto_xresize_mode)? m_main.X2()-m_canvas.X()-m_auto_xresize_right_offset : m_x_size;
int y_size =m_main.Y2()-m_canvas.Y()-m_auto_yresize_bottom_offset;
//--- Выйти, если размер меньше указанного
if(y_size<60)
return;
//--- Установить новый размер фона таблицы
ChangeMainSize(x_size,y_size);
//--- Рассчитать размеры таблицы
CalculateTableSize();
//--- Установить новый размер таблице
ChangeTableSize();
//--- Нарисуем таблицу
DrawTable();
if(m_scrollh.IsScroll())
m_scrollh.Update(true);
if(m_scrollv.IsScroll())
m_scrollv.Update(true);
//--- Обновить
Update(true);
}
//+------------------------------------------------------------------+