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

3568 lines
305 KiB
MQL5

//+------------------------------------------------------------------+
//| TextBox.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Scrolls.mqh"
#include "..\Keys.mqh"
#include "..\Element.mqh"
#include "..\TimeCounter.mqh"
#include <Charts\Chart.mqh>
//+------------------------------------------------------------------+
//| Класс для создания многострочного текстового поля |
//+------------------------------------------------------------------+
class CTextBox : public CElement
{
private:
//--- Экземпляр класса для работы с клавиатурой
CKeys m_keys;
//--- Объект для работы со счётчиком таймера
CTimeCounter m_counter;
//--- Объекты для создания элемента
CRectCanvas m_textbox;
CScrollV m_scrollv;
CScrollH m_scrollh;
//--- Символы и их свойства
struct StringOptions
{
string m_symbol[]; // Символы
int m_width[]; // Ширина символов
bool m_end_of_line; // Признак окончания строки
};
StringOptions m_lines[];
//--- Общий размер и размер видимой части элемента
int m_area_x_size;
int m_area_y_size;
int m_area_visible_x_size;
int m_area_visible_y_size;
//--- Цвет фона и символов выделенного текста
color m_selected_back_color;
color m_selected_text_color;
//--- Начальные и конечные индексы строк и символов (выделенного текста)
int m_selected_line_from;
int m_selected_line_to;
int m_selected_symbol_from;
int m_selected_symbol_to;
//--- Цвет текста по умолчанию
color m_default_text_color;
//--- Текст по умолчанию
string m_default_text;
//--- Переменная для работы со строкой
string m_temp_input_string;
//--- Отступы для текста от краёв поля ввода
int m_text_x_offset;
int m_text_y_offset;
//--- Текущие координаты текстового курсора
int m_text_cursor_x;
int m_text_cursor_y;
//--- Текущая позиция текстового курсора
uint m_text_cursor_x_pos;
uint m_text_cursor_y_pos;
//--- Для расчёта границ видимой части поля ввода
int m_x_limit;
int m_y_limit;
int m_x2_limit;
int m_y2_limit;
//--- Размер шага для смещения по горизонтали
int m_shift_x_step;
//--- Ограничения на смещение
int m_shift_x2_limit;
int m_shift_y2_limit;
//--- Многострочный режим
bool m_multi_line_mode;
//--- Режим "Перенос по словам"
bool m_word_wrap_mode;
//--- Режим "Только для чтения"
bool m_read_only_mode;
//--- Режим авто выделения текста
bool m_auto_selection_mode;
//--- Состояние поля ввода
bool m_text_edit_state;
//--- Счётчик таймера для перемотки списка
int m_timer_counter;
//---
public:
CTextBox(void);
~CTextBox(void);
//--- Методы для создания элемента
bool CreateTextBox(const int x_gap,const int y_gap);
//---
private:
void InitializeProperties(const int x_gap,const int y_gap);
bool CreateCanvas(void);
bool CreateTextBox(void);
bool CreateScrollV(void);
bool CreateScrollH(void);
//---
public:
//--- Возвращает указатели на полосы прокрутки
CScrollV *GetScrollVPointer(void) { return(::GetPointer(m_scrollv)); }
CScrollH *GetScrollHPointer(void) { return(::GetPointer(m_scrollh)); }
//--- Цвет фона и символов выделенного текста
void SelectedBackColor(const color clr) { m_selected_back_color=clr; }
void SelectedTextColor(const color clr) { m_selected_text_color=clr; }
//--- (1) Текст по умолчанию и (2) цвет текста по умолчанию
void DefaultText(const string text) { m_default_text=text; }
void DefaultTextColor(const color clr) { m_default_text_color=clr; }
//--- (1) Многострочный режим, (2) режим "Перенос по словам"
void MultiLineMode(const bool mode) { m_multi_line_mode=mode; }
bool MultiLineMode(void) const { return(m_multi_line_mode); }
void WordWrapMode(const bool mode) { m_word_wrap_mode=mode; }
//--- (1) Режим "Только для чтения", (2) состояние поля ввода, (3) режим для автоматического выделения текста
bool ReadOnlyMode(void) const { return(m_read_only_mode); }
void ReadOnlyMode(const bool mode) { m_read_only_mode=mode; }
bool TextEditState(void) const { return(m_text_edit_state); }
void AutoSelectionMode(const bool state) { m_auto_selection_mode=state; }
//--- (1) Отступы для текста от краёв поля ввода, (2) режим выравнивания текста
void TextXOffset(const int x_offset) { m_text_x_offset=x_offset; }
void TextYOffset(const int y_offset) { m_text_y_offset=y_offset; }
//--- Возвращает индекс (1) строки, (2) символа, на котором находится текстовый курсор,
// (3) количество строк, (4) количество видимых строк
uint TextCursorLine(void) { return(m_text_cursor_y_pos); }
uint TextCursorColumn(void) { return(m_text_cursor_x_pos); }
uint LinesTotal(void) { return(::ArraySize(m_lines)); }
uint VisibleLinesTotal(void);
//--- Количество символов в указанной строке
uint ColumnsTotal(const uint line_index);
//--- Информация текстового курсора (строка/количество строк, столбец/количество столбцов)
string TextCursorInfo(void);
//--- Добавляет строку
void AddLine(const string added_text="");
//--- Добавляет текст в указанную строку
void AddText(const uint line_index,const string added_text);
//--- Возвращает текст из указанной строки
string GetValue(const uint line_index=0);
//--- Очищает текстовое поле ввода
void ClearTextBox(void);
//--- Прокрутка таблицы: (1) вертикальная и (2) горизонтальная
void VerticalScrolling(const int pos=WRONG_VALUE);
void HorizontalScrolling(const int pos=WRONG_VALUE);
//--- Смещение данных относительно позиций полос прокрутки
void ShiftData(void);
//--- Корректировка размеров поля ввода
void CorrectSize(void);
//--- Активация поля ввода
void ActivateTextBox(void);
//--- Дезактивирует поле ввода
void DeactivateTextBox(void);
//--- Изменение размеров
void ChangeSize(const uint x_size,const uint y_size);
//---
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 Draw(void);
//--- Обновление элемента
virtual void Update(const bool redraw=false);
//---
private:
//--- Обработка нажатия на элемент
bool OnClickTextBox(const string clicked_object);
//--- Обработка нажатия на клавише
bool OnPressedKey(const long key_code);
//--- Обработка нажатия на клавише "Backspace"
bool OnPressedKeyBackspace(const long key_code);
//--- Обработка нажатия на клавише "Enter"
bool OnPressedKeyEnter(const long key_code);
//--- Обработка нажатия на клавише "Left"
bool OnPressedKeyLeft(const long key_code);
//--- Обработка нажатия на клавише "Right"
bool OnPressedKeyRight(const long key_code);
//--- Обработка нажатия на клавише "Up"
bool OnPressedKeyUp(const long key_code);
//--- Обработка нажатия на клавише "Down"
bool OnPressedKeyDown(const long key_code);
//--- Обработка нажатия на клавише "Home"
bool OnPressedKeyHome(const long key_code);
//--- Обработка нажатия на клавише "End"
bool OnPressedKeyEnd(const long key_code);
//--- Обработка нажатия на клавише Ctrl + Left
bool OnPressedKeyCtrlAndLeft(const long key_code);
//--- Обработка нажатия на клавише Ctrl + Right
bool OnPressedKeyCtrlAndRight(const long key_code);
//--- Обработка одновременного нажатия клавиш Ctrl + Home
bool OnPressedKeyCtrlAndHome(const long key_code);
//--- Обработка одновременного нажатия клавиш Ctrl + End
bool OnPressedKeyCtrlAndEnd(const long key_code);
//--- Обработка нажатия на клавише Shift + Left
bool OnPressedKeyShiftAndLeft(const long key_code);
//--- Обработка нажатия на клавише Shift + Right
bool OnPressedKeyShiftAndRight(const long key_code);
//--- Обработка нажатия на клавише Shift + Up
bool OnPressedKeyShiftAndUp(const long key_code);
//--- Обработка нажатия на клавише Shift + Down
bool OnPressedKeyShiftAndDown(const long key_code);
//--- Обработка нажатия на клавише Shift + Home
bool OnPressedKeyShiftAndHome(const long key_code);
//--- Обработка нажатия на клавише Shift + End
bool OnPressedKeyShiftAndEnd(const long key_code);
//--- Обработка нажатия на клавише Ctrl + Shift + Left
bool OnPressedKeyCtrlShiftAndLeft(const long key_code);
//--- Обработка нажатия на клавише Ctrl + Shift + Right
bool OnPressedKeyCtrlShiftAndRight(const long key_code);
//--- Обработка нажатия на клавише Ctrl + Shift + Home
bool OnPressedKeyCtrlShiftAndHome(const long key_code);
//--- Обработка нажатия на клавише Ctrl + Shift + End
bool OnPressedKeyCtrlShiftAndEnd(const long key_code);
//---
private:
//--- Устанавливает (1) начальные и (2) конечные индексы для выделения текста
void SetStartSelectedTextIndexes(void);
void SetEndSelectedTextIndexes(void);
//--- Выделить весь текст
void SelectAllText(void);
//--- Сбросить выделенный текст
void ResetSelectedText(void);
//--- Ускоренная перемотка поля ввода
void FastSwitching(void);
//--- Вывод текста на холст
void TextOut(void);
//--- Рисует рамку
virtual void DrawBorder(void);
//--- Рисует текстовый курсор
void DrawCursor(void);
//--- Отображает текст и мигающий курсор
void DrawTextAndCursor(const bool show_state=false);
//--- Возвращает текущий цвет фона
uint AreaColorCurrent(void);
//--- Возвращает текущий цвет текста
uint TextColorCurrent(void);
//--- Возвращает текущий цвет рамки
uint BorderColorCurrent(void);
//--- Изменение цвета объектов
void ChangeObjectsColor(void);
//--- Собирает строку из символов
string CollectString(const uint line_index,const uint symbols_total=0);
//--- Добавляет символ и его свойства в массивы структуры
void AddSymbol(const string key_symbol);
//--- Удаляет символ
void DeleteSymbol(void);
//--- Удаляет (1) выделенный текст, (2) на одной строке, (3) на нескольких строках
bool DeleteSelectedText(void);
void DeleteTextOnOneLine(void);
void DeleteTextOnMultipleLines(void);
//
void InsertToString(const uint line_index, const uint start_pos, const string text);
//--- Возвращает высоту строки
uint LineHeight(void);
//--- Возвращает ширину строки от указанного символа в пикселях
uint LineWidth(const uint symbol_index,const uint line_index);
//--- Возвращает максимальную ширину строки
uint MaxLineWidth(void);
//--- Смещает строки на одну позицию вверх
void ShiftOnePositionUp(void);
//--- Смещает строки на одну позицию вниз
void ShiftOnePositionDown(void);
//--- Проверка наличия выделенного текста
bool CheckSelectedText(const uint line_index,const uint symbol_index);
//--- Проверка на наличие обязательной первой строки
uint CheckFirstLine(void);
//--- Устанавливает новый размер массивам свойств указанной строки
void ArraysResize(const uint line_index,const uint new_size);
//--- Делает копию указанной (source) строки в новое место (destination)
void LineCopy(const uint destination,const uint source);
//--- Очищает указанную строку
void ClearLine(const uint line_index);
//--- Перемещение текстового курсора в указанном направлении
void MoveTextCursor(const ENUM_MOVE_TEXT_CURSOR direction);
void MoveTextCursor(const ENUM_MOVE_TEXT_CURSOR direction,const bool with_highlighted_text);
//--- Перемещение текстового курсора влево
void MoveTextCursorToLeft(const bool to_next_word=false);
//--- Перемещение текстового курсора вправо
void MoveTextCursorToRight(const bool to_next_word=false);
//--- Перемещение текстового курсора на одну строку вверх
void MoveTextCursorToUp(void);
//--- Перемещение текстового курсора на одну строку вниз
void MoveTextCursorToDown(void);
//--- Устанавливает курсор по указанным позициям
void SetTextCursor(const uint x_pos,const uint y_pos);
//--- Устанавливает курсор по указанным позициям по курсору мыши
void SetTextCursorByMouseCursor(void);
//--- Корректировка текстового курсора по оси X
void CorrectingTextCursorXPos(const int x_pos=WRONG_VALUE);
//--- Расчёт координат для текстового курсора
void CalculateTextCursorX(void);
void CalculateTextCursorY(void);
//--- Расчёт границ поля ввода
void CalculateBoundaries(void);
void CalculateXBoundaries(void);
void CalculateYBoundaries(void);
//--- Расчёт X-позиции ползунка полосы прокрутки в левой границе поля ввода
int CalculateScrollThumbX(void);
//--- Расчёт X-позиции ползунка полосы прокрутки в правой границе поля ввода
int CalculateScrollThumbX2(void);
//--- Расчёт X-позиции ползунка полосы прокрутки
int CalculateScrollPosX(const bool to_right=false);
//--- Расчёт Y-позиции ползунка полосы прокрутки в верхней границе поля ввода
int CalculateScrollThumbY(void);
//--- Расчёт Y-позиции ползунка полосы прокрутки в нижней границе поля ввода
int CalculateScrollThumbY2(void);
//--- Расчёт Y-позиции ползунка полосы прокрутки
int CalculateScrollPosY(const bool to_down=false);
//--- Корректировка горизонтальной полосы прокрутки
void CorrectingHorizontalScrollThumb(void);
//--- Корректировка вертикальной полосы прокрутки
void CorrectingVerticalScrollThumb(void);
//--- Рассчитывает размеры текстого поля ввода
void CalculateTextBoxSize(void);
bool CalculateTextBoxXSize(void);
bool CalculateTextBoxYSize(void);
//--- Изменить основные размеры элемента
void ChangeMainSize(const int x_size,const int y_size);
//--- Изменить размеры поля ввода
void ChangeTextBoxSize(const bool x_offset=false,const bool y_offset=false);
//--- Изменить размеры полос прокрутки
void ChangeScrollsSize(void);
//--- Перенос по словам
void WordWrap(void);
//--- Возвращает индексы первых видимых символа и пробела
bool CheckForOverflow(const uint line_index,int &symbol_index,int &space_index);
//--- Количество слов в указанной строке
uint WordsTotal(const uint line_index);
//--- Возвращает количество переносимых символов
bool WrapSymbolsTotal(const uint line_index,uint &wrap_symbols_total);
//--- Возвращает индекс символа пробела по его номеру
uint SymbolIndexBySpaceNumber(const uint line_index,const uint space_index);
//--- Перемещает строки
void MoveLines(const uint from_index,const uint to_index,const uint count,const bool to_down=true);
//--- Перемещение символов в указанной строке
void MoveSymbols(const uint line_index,const uint from_pos,const uint to_pos,const bool to_left=true);
//--- Добавление текста в указанную строку
void AddToString(const uint line_index,const string text);
//--- Копирует в переданный массив символы для переноса на другую строку
void CopyWrapSymbols(const uint line_index,const uint start_pos,const uint symbols_total,string &array[]);
//--- Вставляет символы из переданного массива в указанную строку
void PasteWrapSymbols(const uint line_index,const uint start_pos,string &array[]);
//--- Перенос текста на следующую строку
void WrapTextToNewLine(const uint curr_line_index,const uint symbol_index,const bool by_pressed_enter=false);
//--- Перенос текста из указанной строки в предыдущую
void WrapTextToPrevLine(const uint next_line_index,const uint wrap_symbols_total,const bool is_all_text=false);
//--- Изменить ширину по правому краю окна
virtual void ChangeWidthByRightWindowSide(void);
//--- Изменить высоту по нижнему краю окна
virtual void ChangeHeightByBottomWindowSide(void);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CTextBox::CTextBox(void) : m_selected_text_color(clrWhite),
m_selected_back_color(C'51,153,255'),
m_selected_line_from(WRONG_VALUE),
m_selected_line_to(WRONG_VALUE),
m_selected_symbol_from(WRONG_VALUE),
m_selected_symbol_to(WRONG_VALUE),
m_default_text_color(clrTomato),
m_default_text(""),
m_temp_input_string(""),
m_text_x_offset(5),
m_text_y_offset(4),
m_multi_line_mode(false),
m_word_wrap_mode(false),
m_read_only_mode(false),
m_auto_selection_mode(false),
m_text_edit_state(false),
m_text_cursor_x_pos(0),
m_text_cursor_y_pos(0),
m_shift_x_step(10),
m_shift_x2_limit(0),
m_shift_y2_limit(0)
{
//--- Сохраним имя класса элемента в базовом классе
CElementBase::ClassName(CLASS_NAME);
//--- Исходные координаты текстового курсора
m_text_cursor_x=m_text_x_offset;
m_text_cursor_y=m_text_y_offset;
//--- Установка параметров для счётчика таймера
m_counter.SetParameters(16,200);
//--- Обязательная первая строка многострочного поля ввода
::ArrayResize(m_lines,1);
//--- Установим признак окончания строки
m_lines[0].m_end_of_line=true;
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTextBox::~CTextBox(void)
{
}
//+------------------------------------------------------------------+
//| Обработчик событий графика |
//+------------------------------------------------------------------+
void CTextBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Обработка события перемещения курсора
if(id==CHARTEVENT_MOUSE_MOVE)
{
//--- Проверка статуса полос прокрутки
bool is_scroll_state=m_scrollv.ScrollBarControl() || ((m_multi_line_mode)? m_scrollh.ScrollBarControl() : false);
//---
if(m_text_edit_state)
{
//--- Если (1) не в фокусе и (2) левая кнопка мыши нажата и (3) не в режиме перемещения полос прокрутки
if(!CElementBase::MouseFocus() && m_mouse.LeftButtonState() && !is_scroll_state)
{
//--- Отправим сообщение об окончании ввода строки в поле ввода, если поле было активно
string str=(m_multi_line_mode)? TextCursorInfo() : "";
::EventChartCustom(m_chart_id,ON_END_EDIT,CElementBase::Id(),CElementBase::Index(),str);
//--- Дезактивируем поле ввода
DeactivateTextBox();
//--- Обновить
Update(true);
}
}
//--- Изменение цвета объектов
ChangeObjectsColor();
//--- Выйти, если многострочный режим отключен
if(!m_multi_line_mode)
return;
//--- Если полоса прокрутки в действии
if(is_scroll_state)
{
//--- Сдвинуть данные относительно полос прокрутки
ShiftData();
//--- Дезактивируем поле ввода
DeactivateTextBox();
//--- Обновить
Update(true);
//--- Обновить полосу прокрутки в действии
if(m_scrollh.State()) m_scrollh.Update(true);
if(m_scrollv.State()) m_scrollv.Update(true);
}
//--- Если одна из кнопок полос прокрутки нажата
if(m_mouse.LeftButtonState() &&
(m_scrollv.ScrollIncState() || m_scrollv.ScrollDecState() ||
m_scrollh.ScrollIncState() || m_scrollh.ScrollDecState()))
{
//--- Дезактивируем поле ввода
DeactivateTextBox();
//--- Обновить
Update(true);
}
//---
return;
}
//--- Обработка события нажатия левой кнопки мыши на объекте
if(id==CHARTEVENT_OBJECT_CLICK)
{
//--- Нажатие на поле ввода
if(OnClickTextBox(sparam))
return;
//---
return;
}
//--- Обработка события нажатия на кнопках полосы прокрутки
if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
{
//--- Если было нажатие на кнопках полосы прокрутки списка
if(m_scrollv.OnClickScrollInc((uint)lparam,(uint)dparam) ||
m_scrollv.OnClickScrollDec((uint)lparam,(uint)dparam))
{
//--- Сдвигает данные
ShiftData();
m_scrollv.Update(true);
return;
}
//--- Если было нажатие на кнопках полосы прокрутки списка
if(m_scrollh.OnClickScrollInc((uint)lparam,(uint)dparam) ||
m_scrollh.OnClickScrollDec((uint)lparam,(uint)dparam))
{
//--- Сдвигает данные
ShiftData();
m_scrollh.Update(true);
return;
}
}
//--- Обработка нажатия кнопки на клавиатуре
if(id==CHARTEVENT_KEYDOWN)
{
//--- Выйти, если поле ввода не активировано
if(!m_text_edit_state)
return;
//--- Нажатие на символьной клавише
if(OnPressedKey(lparam))
return;
//--- Нажатие на клавише "Backspace"
if(OnPressedKeyBackspace(lparam))
return;
//--- Нажатие на клавише "Enter"
if(OnPressedKeyEnter(lparam))
return;
//--- Нажатие на клавише "Left"
if(OnPressedKeyLeft(lparam))
return;
//--- Нажатие на клавише "Right"
if(OnPressedKeyRight(lparam))
return;
//--- Нажатие на клавише "Up"
if(OnPressedKeyUp(lparam))
return;
//--- Нажатие на клавише "Down"
if(OnPressedKeyDown(lparam))
return;
//--- Нажатие на клавише "Home"
if(OnPressedKeyHome(lparam))
return;
//--- Нажатие на клавише "End"
if(OnPressedKeyEnd(lparam))
return;
//--- Одновременное нажатие клавиш Ctrl + Left
if(OnPressedKeyCtrlAndLeft(lparam))
return;
//--- Одновременное нажатие клавиш Ctrl + Right
if(OnPressedKeyCtrlAndRight(lparam))
return;
//--- Одновременное нажатие клавиш Ctrl + Home
if(OnPressedKeyCtrlAndHome(lparam))
return;
//--- Одновременное нажатие клавиш Ctrl + End
if(OnPressedKeyCtrlAndEnd(lparam))
return;
//--- Одновременное нажатие клавиш Shift + Left
if(OnPressedKeyShiftAndLeft(lparam))
return;
//--- Одновременное нажатие клавиш Shift + Right
if(OnPressedKeyShiftAndRight(lparam))
return;
//--- Одновременное нажатие клавиш Shift + Up
if(OnPressedKeyShiftAndUp(lparam))
return;
//--- Одновременное нажатие клавиш Shift + Down
if(OnPressedKeyShiftAndDown(lparam))
return;
//--- Одновременное нажатие клавиш Shift + Home
if(OnPressedKeyShiftAndHome(lparam))
return;
//--- Одновременное нажатие клавиш Shift + End
if(OnPressedKeyShiftAndEnd(lparam))
return;
//--- Одновременное нажатие клавиш Ctrl + Shift + Left
if(OnPressedKeyCtrlShiftAndLeft(lparam))
return;
//--- Одновременное нажатие клавиш Ctrl + Shift + Right
if(OnPressedKeyCtrlShiftAndRight(lparam))
return;
//--- Одновременное нажатие клавиш Ctrl + Shift + Home
if(OnPressedKeyCtrlShiftAndHome(lparam))
return;
//--- Одновременное нажатие клавиш Ctrl + Shift + End
if(OnPressedKeyCtrlShiftAndEnd(lparam))
return;
//---
return;
}
}
//+------------------------------------------------------------------+
//| Таймер |
//+------------------------------------------------------------------+
void CTextBox::OnEventTimer(void)
{
//--- Ускоренная перемотка значений
FastSwitching();
//--- Пауза между обновлением текстового курсора
if(m_counter.CheckTimeCounter())
{
//--- Обновим текстовый курсор, если элемент видим и поле ввода активировано
if(CElementBase::IsVisible() && m_text_edit_state)
DrawTextAndCursor();
}
}
//+------------------------------------------------------------------+
//| Создаёт элемент "Текстовое поле ввода" |
//+------------------------------------------------------------------+
bool CTextBox::CreateTextBox(const int x_gap,const int y_gap)
{
//--- Выйти, если нет указателя на главный элемент
if(!CElement::CheckMainPointer())
return(false);
//--- Инициализация свойств
InitializeProperties(x_gap,y_gap);
//--- Рассчитаем размеры текстового поля ввода
CalculateTextBoxSize();
//--- Создание элемента
if(!CreateCanvas())
return(false);
if(!CreateTextBox())
return(false);
if(!CreateScrollV())
return(false);
if(!CreateScrollH())
return(false);
//--- Изменить размеры текстового поля ввода
ChangeTextBoxSize();
//--- В режиме переноса слов нужно повторно пересчитать и установить размеры
if(m_word_wrap_mode)
{
CalculateTextBoxSize();
ChangeTextBoxSize();
}
//---
return(true);
}
//+------------------------------------------------------------------+
//| Инициализация свойств |
//+------------------------------------------------------------------+
void CTextBox::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<0 || m_auto_xresize_mode)? m_main.X2()-CElementBase::X()-m_auto_xresize_right_offset : m_x_size;
m_y_size =(m_y_size<0 || m_auto_yresize_mode)? m_main.Y2()-CElementBase::Y()-m_auto_yresize_bottom_offset : m_y_size;
//--- Цвета по умолчанию
m_back_color =(m_back_color!=clrNONE)? m_back_color : clrWhite;
m_back_color_locked =(m_back_color_locked!=clrNONE)? m_back_color_locked : clrWhiteSmoke;
m_border_color =(m_border_color!=clrNONE)? m_border_color : clrGray;
m_border_color_hover =(m_border_color_hover!=clrNONE)? m_border_color_hover : clrBlack;
m_border_color_locked =(m_border_color_locked!=clrNONE)? m_border_color_locked : clrSilver;
m_border_color_pressed =(m_border_color_pressed!=clrNONE)? m_border_color_pressed : clrCornflowerBlue;
m_label_color =(m_label_color!=clrNONE)? m_label_color : clrBlack;
m_label_color_locked =(m_label_color_locked!=clrNONE)? m_label_color_locked : clrSilver;
//--- Отступы от крайней точки
CElementBase::XGap(x_gap);
CElementBase::YGap(y_gap);
}
//+------------------------------------------------------------------+
//| Создаёт холст для рисования фона |
//+------------------------------------------------------------------+
bool CTextBox::CreateCanvas(void)
{
//--- Формирование имени объекта
string name=CElementBase::ElementName("textbox");
//--- Создание объекта
if(!CElement::CreateCanvas(name,m_x,m_y,m_x_size,m_y_size))
return(false);
//--- Проверка на наличие обязательной первой строки
CheckFirstLine();
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт холст для рисования поля ввода |
//+------------------------------------------------------------------+
bool CTextBox::CreateTextBox(void)
{
//--- Формирование имени объекта
string name="";
if(m_index==WRONG_VALUE)
name=m_program_name+"_"+"textbox_edit"+"_"+(string)m_id;
else
name=m_program_name+"_"+"textbox_edit"+"_"+(string)m_index+"__"+(string)m_id;
//--- Координаты
int x =m_x+1;
int y =m_y+1;
//--- Размер
int x_size =m_area_x_size-2;
int y_size =m_area_y_size-2;
//--- Создание объекта
::ResetLastError();
if(!m_textbox.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,x_size,y_size,COLOR_FORMAT_ARGB_NORMALIZE))
{
::Print(__FUNCTION__," > Не удалось создать холст для рисования поля ввода: ",::GetLastError());
return(false);
}
//--- Получим указатель на базовый класс
if(!m_textbox.Attach(m_chart_id,name,COLOR_FORMAT_ARGB_NORMALIZE))
{
::Print(__FUNCTION__," > Не удалось присоединить холст для рисования к графику: ",::GetLastError());
return(false);
}
//--- Свойства
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_ZORDER,m_zorder+1);
::ObjectSetString(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_TOOLTIP,"\n");
//--- Координаты
m_textbox.X(x);
m_textbox.Y(y);
//--- Отступы от крайней точки панели
m_textbox.XGap(CElement::CalculateXGap(x));
m_textbox.YGap(CElement::CalculateYGap(y));
//--- Установим размеры видимой области
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XSIZE,m_area_visible_x_size);
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YSIZE,m_area_visible_y_size);
//--- Зададим смещение фрейма внутри изображения по осям X и Y
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XOFFSET,0);
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YOFFSET,0);
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт вертикальный скролл |
//+------------------------------------------------------------------+
bool CTextBox::CreateScrollV(void)
{
//--- Сохранить указатель родительского элемента
m_scrollv.MainPointer(this);
//--- Если отключен многострочный режим
if(!m_multi_line_mode)
{
//--- Инициализация вертикальной полосы прокрутки
m_scrollv.Reinit(m_area_y_size,m_area_visible_y_size);
//--- Сохранить указатель родительского элемента
m_scrollv.GetIncButtonPointer().MainPointer(m_scrollv);
m_scrollv.GetDecButtonPointer().MainPointer(m_scrollv);
return(true);
}
//--- Координаты
int x =m_scrollv.ScrollWidth()+1;
int y =1;
//--- Установим свойства
m_scrollv.Index(0);
m_scrollv.IsDropdown(CElementBase::IsDropdown());
m_scrollv.XSize(m_scrollv.ScrollWidth());
m_scrollv.YSize(m_y_size-m_scrollv.ScrollWidth()-1);
m_scrollv.AnchorRightWindowSide(true);
//--- Расчёт количества шагов для смещения
uint lines_total =LinesTotal()+1;
uint visible_lines_total =VisibleLinesTotal();
//--- Создание полосы прокрутки
if(!m_scrollv.CreateScroll(x,y,lines_total,visible_lines_total))
return(false);
//--- Добавить элемент в массив
CElement::AddToArray(m_scrollv);
return(true);
}
//+------------------------------------------------------------------+
//| Создаёт горизонтальный скролл |
//+------------------------------------------------------------------+
bool CTextBox::CreateScrollH(void)
{
//--- Сохранить указатель родительского элемента
m_scrollh.MainPointer(this);
//--- Если отключен многострочный режим
if(!m_multi_line_mode)
{
//--- Инициализация горизонтальной полосы прокрутки
m_scrollh.Reinit(m_area_x_size,m_area_visible_x_size);
//--- Сохранить указатель главного элемента
m_scrollh.GetIncButtonPointer().MainPointer(m_scrollh);
m_scrollh.GetDecButtonPointer().MainPointer(m_scrollh);
return(true);
}
//--- Координаты
int x =1;
int y =m_scrollh.ScrollWidth()+1;
//--- Установим свойства
m_scrollh.Index(1);
m_scrollh.IsDropdown(CElementBase::IsDropdown());
m_scrollh.XSize(CElementBase::XSize()-m_scrollv.ScrollWidth()-1);
m_scrollh.YSize(m_scrollv.ScrollWidth());
m_scrollh.AnchorBottomWindowSide(true);
//--- Расчёт количества шагов для смещения
uint x_size_total =m_area_x_size/m_shift_x_step;
uint visible_x_size_total =m_area_visible_x_size/m_shift_x_step;
//--- Создание полосы прокрутки
if(!m_scrollh.CreateScroll(x,y,x_size_total,visible_x_size_total))
return(false);
//--- Добавить элемент в массив
CElement::AddToArray(m_scrollh);
return(true);
}
//+------------------------------------------------------------------+
//| Возвращает количество видимых строк |
//+------------------------------------------------------------------+
uint CTextBox::VisibleLinesTotal(void)
{
return((m_area_visible_y_size-(m_text_y_offset*2))/LineHeight());
}
//+------------------------------------------------------------------+
//| Возвращает количество символов в указанной строке |
//+------------------------------------------------------------------+
uint CTextBox::ColumnsTotal(const uint line_index)
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Предотвращение выхода из диапазона
uint check_index=(line_index<lines_total)? line_index : lines_total-1;
//--- Получим размер массива символов в строке
uint symbols_total=::ArraySize(m_lines[check_index].m_symbol);
//--- Вернуть количество символов
return(symbols_total);
}
//+------------------------------------------------------------------+
//| Информация текстового курсора |
//+------------------------------------------------------------------+
string CTextBox::TextCursorInfo(void)
{
//--- Компоненты для строки
string lines_total =(string)LinesTotal();
string columns_total =(string)ColumnsTotal(TextCursorLine());
string text_cursor_line =string(TextCursorLine()+1);
string text_cursor_column =string(TextCursorColumn()+1);
//--- Сформируем строку
string text_box_info="Ln "+text_cursor_line+"/"+lines_total+", "+"Col "+text_cursor_column+"/"+columns_total;
//--- Вернуть строку
return(text_box_info);
}
//+------------------------------------------------------------------+
//| Добавляет строку |
//+------------------------------------------------------------------+
void CTextBox::AddLine(const string added_text="")
{
//--- Выйти, если отключен многострочный режим
if(!m_multi_line_mode)
return;
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Резервный размер массива
int reserve_size=10000;
//--- Устанавливаем размер массивам структуры
::ArrayResize(m_lines,lines_total+1,reserve_size);
//--- Установим признак окончания строки
m_lines[lines_total].m_end_of_line=true;
//--- Установим шрифт
m_textbox.FontSet(CElement::Font(),-CElement::FontSize()*10,FW_NORMAL);
//--- Добавим текст в строку
AddToString(lines_total,added_text);
//--- Выйти, если у составного элемента указатель на главный элемент невалиден
if(::CheckPointer(m_scrollh.MainPointer())==POINTER_INVALID)
return;
//--- Рассчитать размеры поля ввода
CalculateTextBoxSize();
//--- Установить новый размер полю ввода
ChangeTextBoxSize();
//--- В режиме переноса слов нужно повторно пересчитать и установить размеры
if(m_word_wrap_mode)
{
CalculateTextBoxSize();
ChangeTextBoxSize();
}
//--- Перерисовать полосы прокрутки
//if(m_scrollh.IsScroll())
// m_scrollh.Update(true);
//if(m_scrollv.IsScroll())
// m_scrollv.Update(true);
}
//+------------------------------------------------------------------+
//| Добавляет текст в указанную строку |
//+------------------------------------------------------------------+
void CTextBox::AddText(const uint line_index,const string added_text)
{
//--- Выйти, если передана пустая строка
if(added_text=="")
return;
//--- Получим размер массива строк, с проверкой на наличие обязательной первой строки
uint lines_total=CheckFirstLine();
//--- Предотвращение выхода из диапазона
uint l=(line_index<lines_total)? line_index : lines_total-1;
//--- Корректировка индекса с учётом режима переноса по словам
if(m_word_wrap_mode)
{
for(uint i=0,j=0; i<lines_total; i++)
{
//--- Считаем строки по признаку окончания
if(m_lines[i].m_end_of_line)
{
//---
if(l==j || i+1>=lines_total)
{
l=i;
break;
}
//---
j++;
}
}
}
//---
uint insert_pos = (l == m_text_cursor_y_pos) ? m_text_cursor_x_pos : ::ArraySize(m_lines[l].m_symbol);
//--- Установим шрифт
m_textbox.FontSet(CElement::Font(),-CElement::FontSize()*10,FW_NORMAL);
//--- Добавим текст в строку
InsertToString(l,insert_pos,added_text);
}
//+------------------------------------------------------------------+
//| Возвращает текст из указанной строки |
//+------------------------------------------------------------------+
string CTextBox::GetValue(const uint line_index=0)
{
//--- Получим размер массива строк, с проверкой на наличие обязательной первой строки
uint lines_total=CheckFirstLine();
//--- Предотвращение выхода из диапазона
uint l=(line_index<lines_total)? line_index : lines_total-1;
//--- Вернуть текст
return(CollectString(l));
}
//+------------------------------------------------------------------+
//| Очищает текстовое поле ввода |
//+------------------------------------------------------------------+
void CTextBox::ClearTextBox(void)
{
//--- Удаляем все строки кроме первой
::ArrayResize(m_lines,1);
//--- Очищаем первую строку
ClearLine(0);
}
//+------------------------------------------------------------------+
//| Горизонтальная прокрутка поля ввода |
//+------------------------------------------------------------------+
void CTextBox::HorizontalScrolling(const int pos=WRONG_VALUE)
{
//--- Для определения позиции ползунка
int index=0;
//--- Индекс последней позиции
int last_pos_index=int(m_area_x_size-m_area_visible_x_size);
//--- Корректировка в случае выхода из диапазона
if(pos<0)
index=last_pos_index;
else
index=(pos>last_pos_index)? last_pos_index : pos;
//--- Сдвигаем ползунок полосы прокрутки
m_scrollh.MovingThumb(index);
//--- Сдвигаем поле ввода
ShiftData();
//--- Обновить полосу прокрутки
m_scrollh.Update();
}
//+------------------------------------------------------------------+
//| Вертикальная прокрутка поля ввода |
//+------------------------------------------------------------------+
void CTextBox::VerticalScrolling(const int pos=WRONG_VALUE)
{
//--- Для определения позиции ползунка
int index=0;
//--- Индекс последней позиции
int last_pos_index=int((m_area_y_size-m_area_visible_y_size)/(double)LineHeight());
//--- Корректировка в случае выхода из диапазона
if(pos<0)
index=last_pos_index;
else
index=(pos>last_pos_index)? last_pos_index : pos;
//--- Сдвигаем ползунок полосы прокрутки
m_scrollv.MovingThumb(index);
//--- Сдвигаем поле ввода
ShiftData();
//--- Обновить полосу прокрутки
m_scrollv.Update(true);
}
//+------------------------------------------------------------------+
//| Сдвигает данные относительно полос прокрутки |
//+------------------------------------------------------------------+
void CTextBox::ShiftData(void)
{
//--- Получим текущие позиции ползунков горизонтальной и вертикальной полос прокрутки
int h_offset =m_scrollh.CurrentPos()*m_shift_x_step;
int v_offset =m_text_y_offset+(m_scrollv.CurrentPos()*(int)LineHeight())-2;
//--- Рассчитаем отступы для смещения
int x_offset =(h_offset<1)? 0 : (h_offset>=m_shift_x2_limit)? m_shift_x2_limit : h_offset;
int y_offset =(v_offset<1)? 0 : (v_offset>=m_shift_y2_limit)? m_shift_y2_limit : v_offset;
//--- Расчёт положения данных относительно ползунков полос прокрутки
long x =(m_area_x_size>m_area_visible_x_size && !m_word_wrap_mode)? x_offset : 0;
long y =(m_area_y_size>m_area_visible_y_size)? y_offset : 0;
//--- Смещение данных
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XOFFSET,x);
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YOFFSET,y);
}
//+------------------------------------------------------------------+
//| Корректировка размеров поля ввода |
//+------------------------------------------------------------------+
void CTextBox::CorrectSize(void)
{
//--- Рассчитать размеры поля ввода
CalculateTextBoxSize();
//--- Установить новый размер полю ввода
ChangeTextBoxSize(true,true);
}
//+------------------------------------------------------------------+
//| Активация поля ввода |
//+------------------------------------------------------------------+
void CTextBox::ActivateTextBox(void)
{
OnClickTextBox(m_textbox.ChartObjectName());
}
//+------------------------------------------------------------------+
//| Изменение размеров |
//+------------------------------------------------------------------+
void CTextBox::ChangeSize(const uint x_size,const uint y_size)
{
//--- Установить новый размер
ChangeMainSize(x_size,y_size);
//--- Рассчитать размеры поля ввода
CalculateTextBoxSize();
//--- Установить новый размер полю ввода
ChangeTextBoxSize();
}
//+------------------------------------------------------------------+
//| Перемещение элемента |
//+------------------------------------------------------------------+
void CTextBox::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_textbox.X(m_main.X2()-m_textbox.XGap());
}
else
{
CElementBase::X(m_main.X()+XGap());
m_textbox.X(m_main.X()+m_textbox.XGap());
}
//--- Если привязка снизу
if(m_anchor_bottom_window_side)
{
CElementBase::Y(m_main.Y2()-YGap());
m_textbox.Y(m_main.Y2()-m_textbox.YGap());
}
else
{
CElementBase::Y(m_main.Y()+YGap());
m_textbox.Y(m_main.Y()+m_textbox.YGap());
}
//--- Обновление координат
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XDISTANCE,m_textbox.X());
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YDISTANCE,m_textbox.Y());
//--- Обновить положение объектов
CElement::Moving(only_visible);
}
//+------------------------------------------------------------------+
//| Показывает элемент |
//+------------------------------------------------------------------+
void CTextBox::Show(void)
{
//--- Выйти, если элемент уже видим
if(CElementBase::IsVisible())
return;
//--- Сделать видимыми все объекты
::ObjectSetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
//--- Состояние видимости
CElementBase::IsVisible(true);
//--- Обновить положение элемента
Moving();
//--- Показать полосы прокрутки
if(m_scrollv.IsScroll())
m_scrollv.Show();
if(m_scrollh.IsScroll())
m_scrollh.Show();
}
//+------------------------------------------------------------------+
//| Скрывает элемент |
//+------------------------------------------------------------------+
void CTextBox::Hide(void)
{
//--- Выйти, если элемент скрыт
if(!CElementBase::IsVisible())
return;
//--- Скрыть все объекты
::ObjectSetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
m_scrollv.Hide();
m_scrollh.Hide();
//--- Состояние видимости
CElementBase::IsVisible(false);
}
//+------------------------------------------------------------------+
//| Удаление |
//+------------------------------------------------------------------+
void CTextBox::Delete(void)
{
//--- Удаление объектов
m_canvas.Destroy();
m_textbox.Destroy();
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Освобождение массивов элемента
for(uint i=0; i<lines_total; i++)
{
::ArrayFree(m_lines[i].m_width);
::ArrayFree(m_lines[i].m_symbol);
}
//---
::ArrayFree(m_lines);
//--- Инициализация переменных значениями по умолчанию
m_text_edit_state=false;
CElementBase::IsVisible(true);
CElementBase::MouseFocus(false);
}
//+------------------------------------------------------------------+
//| Установка приоритетов |
//+------------------------------------------------------------------+
void CTextBox::SetZorders(void)
{
CElement::SetZorders();
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_ZORDER,m_zorder+1);
}
//+------------------------------------------------------------------+
//| Сброс приоритетов |
//+------------------------------------------------------------------+
void CTextBox::ResetZorders(void)
{
CElement::ResetZorders();
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_ZORDER,WRONG_VALUE);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на элемент |
//+------------------------------------------------------------------+
bool CTextBox::OnClickTextBox(const string clicked_object)
{
//--- Выйдем, если чужое имя объекта
if(m_textbox.ChartObjectName()!=clicked_object)
{
//--- Отправим сообщение об окончании ввода строки в поле ввода, если поле было активно
if(m_text_edit_state)
{
string str=(m_multi_line_mode)? TextCursorInfo() : "";
::EventChartCustom(m_chart_id,ON_END_EDIT,CElementBase::Id(),CElementBase::Index(),str);
}
//--- Дезактивировать поле ввода
DeactivateTextBox();
//--- Обновить
Update(true);
return(false);
}
//--- Выйти, если (1) включен режим "Только для чтения" или (2) элемент заблокирован
if(m_read_only_mode || CElementBase::IsLocked())
return(true);
//--- Выйдем, если полоса прокрутки в активном режиме
if(m_scrollv.State() || m_scrollh.State())
return(true);
//--- Если (1) включен режим авто выделения текста и (2) поле ввода было активировано только что
if(m_auto_selection_mode && !m_text_edit_state)
SelectAllText();
//--- Сброс выделения
else
ResetSelectedText();
//--- Отключим управление графиком
m_chart.SetInteger(CHART_KEYBOARD_CONTROL,false);
//--- Если (1) включен режим авто выделения текста и (2) поле ввода активировано
if(!m_auto_selection_mode || (m_auto_selection_mode && m_text_edit_state))
{
//--- Установить текстовый курсор по курсору мыши
SetTextCursorByMouseCursor();
}
//--- Если включен режим многострочного поля ввода, корректируем вертикальную полосу прокрутки
if(m_multi_line_mode)
CorrectingVerticalScrollThumb();
//--- Активировать поле ввода
m_text_edit_state=true;
//--- Обновить текст и курсор
DrawTextAndCursor(true);
//--- Изменить цвет рамки
DrawBorder();
m_canvas.Update();
//--- Отправим сообщение об этом
::EventChartCustom(m_chart_id,ON_CLICK_TEXT_BOX,CElementBase::Id(),CElementBase::Index(),TextCursorInfo());
return(true);
}
//+------------------------------------------------------------------+
//| Procesamiento de tecla presionada |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKey(const long key_code)
{
//--- Obtener el símbolo de la tecla
string pressed_key=m_keys.KeySymbol(key_code);
//--- Salir si no hay símbolo
if(pressed_key=="")
return(false);
//--- Si hay texto seleccionado, eliminarlo
DeleteSelectedText();
//--- Agregar el símbolo y sus propiedades
AddSymbol(pressed_key);
//--- Determinar si se necesita recálculo completo de dimensiones
bool needs_full_recalc=false;
//--- Obtener tamaño del array de símbolos y ancho de línea actual
int symbols_total =::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
int line_width = (int)LineWidth(symbols_total,m_text_cursor_y_pos);
const int xscrrol = m_scrollv.ScrollWidth();
//--- Si el modo de ajuste de palabras está activado
if(m_word_wrap_mode)
{
//--- Se necesita recálculo si la línea supera el área visible (causará wrap)
if((line_width+m_text_x_offset*2+xscrrol)>m_area_visible_x_size)
needs_full_recalc=true;
}
//--- Si el modo de ajuste de palabras está desactivado
else
{
//--- Se necesita recálculo si la línea supera el área total actual
// (necesitará expandir el canvas o ajustar scroll)
if((line_width+m_text_x_offset*2+xscrrol)>m_area_x_size)
needs_full_recalc=true;
}
//--- Si se necesita recálculo completo
if(needs_full_recalc)
{
//--- Calcular dimensiones del campo de entrada
CalculateTextBoxSize();
//--- Establecer nuevo tamaño al campo de entrada
ChangeTextBoxSize(true,true);
//--- Corregir barra de desplazamiento horizontal
CorrectingHorizontalScrollThumb();
//--- Si el modo de ajuste de palabras está activado, corregir barra vertical
if(m_word_wrap_mode)
CorrectingVerticalScrollThumb();
}
//--- Si no se necesita recálculo completo
else
{
//--- Obtener coordenada X del cursor
CalculateTextCursorX();
//--- Corregir barra de desplazamiento horizontal si el cursor salió del área visible
int x_offset=(int)::ObjectGetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XOFFSET);
if(m_text_cursor_x-x_offset>=m_area_visible_x_size-m_text_x_offset)
CorrectingHorizontalScrollThumb();
}
//--- Actualizar texto en el campo de entrada
DrawTextAndCursor(true);
//--- Enviar mensaje sobre esto
::EventChartCustom(m_chart_id,ON_MODIFY_TEXTBOX,CElementBase::Id(),CElementBase::Index(),pressed_key);
return(true);
}
/*
//+------------------------------------------------------------------+
//| Обработка нажатия специальных символов с Shift/Alt |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeySpecial(const long key_code)
{
//--- Obtener estado de modificadores
if(!m_keys.KeyShiftState())
return(false);
//--- Mapa de caracteres especiales
string special_char = "";
//--- Con Shift
if(is_shift && !is_alt)
{
switch((int)key_code)
{
case 188: special_char = "<"; break; // Shift + ,
case 190: special_char = ">"; break; // Shift + .
case 191: special_char = "?"; break; // Shift + /
case 186: special_char = ":"; break; // Shift + ;
case 222: special_char = "\""; break; // Shift + '
case 219: special_char = "{"; break; // Shift + [
case 221: special_char = "}"; break; // Shift + ]
case 220: special_char = "|"; break; // Shift + \
case 49: special_char = "!"; break; // Shift + 1
case 50: special_char = "@"; break; // Shift + 2
case 51: special_char = "#"; break; // Shift + 3
case 52: special_char = "$"; break; // Shift + 4
case 53: special_char = "%"; break; // Shift + 5
case 54: special_char = "^"; break; // Shift + 6
case 55: special_char = "&"; break; // Shift + 7
case 56: special_char = "*"; break; // Shift + 8
case 57: special_char = "("; break; // Shift + 9
case 48: special_char = ")"; break; // Shift + 0
case 189: special_char = "_"; break; // Shift + -
case 187: special_char = "+"; break; // Shift + =
case 192: special_char = "~"; break; // Shift + `
}
}
//--- Sin Shift (caracteres normales que Keys.mqh no captura)
else if(!is_shift && !is_alt)
{
switch((int)key_code)
{
case 188: special_char = ","; break;
case 190: special_char = "."; break;
case 191: special_char = "/"; break;
case 186: special_char = ";"; break;
case 222: special_char = "'"; break;
case 219: special_char = "["; break;
case 221: special_char = "]"; break;
case 220: special_char = "\\"; break;
case 189: special_char = "-"; break;
case 187: special_char = "="; break;
case 192: special_char = "`"; break;
}
}
//--- Si no encontramos carácter especial, salir
if(special_char == "")
return(false);
//--- Si hay texto seleccionado, eliminarlo
DeleteSelectedText();
//--- Agregar el carácter especial
AddSymbol(special_char);
//--- Recalcular dimensiones
CalculateTextBoxSize();
ChangeTextBoxSize(true, true);
//--- Corregir scrollbars
CorrectingHorizontalScrollThumb();
if(m_word_wrap_mode)
CorrectingVerticalScrollThumb();
//--- Redibujar
DrawTextAndCursor(true);
//--- Enviar evento de modificación
::EventChartCustom(m_chart_id, ON_MODIFY_TEXTBOX, CElementBase::Id(), CElementBase::Index(), special_char);
return(true);
} */
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише 'Backspace' |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyBackspace(const long key_code)
{
//--- Выйти, если это не клавиша 'Backspace'
if(key_code!=KEY_BACKSPACE)
return(false);
//--- Если есть выделенный текст, удалим его и выйдем
if(DeleteSelectedText())
return(true);
//--- Удалить символ, если позиция больше нуля
if(m_text_cursor_x_pos>0)
DeleteSymbol();
//--- Если нулевая позиция и не первая строка,
// удалить строку и сместить строки на одну позицию вверх
else if(m_text_cursor_y_pos>0)
ShiftOnePositionUp();
//--- Рассчитать размеры поля ввода
CalculateTextBoxSize();
//--- Установить новый размер полю ввода
ChangeTextBoxSize(true,true);
//--- Корректируем полосы прокрутки
CorrectingHorizontalScrollThumb();
CorrectingVerticalScrollThumb();
//--- Обновить текст в поле ввода
DrawTextAndCursor(true);
//--- Отправим сообщение об этом
//::EventChartCustom(m_chart_id,ON_MOVE_TEXT_CURSOR,CElementBase::Id(),CElementBase::Index(),TextCursorInfo());
// Modifuqe esto agrege un nuevo evento
::EventChartCustom(m_chart_id,ON_MODIFY_TEXTBOX,CElementBase::Id(),CElementBase::Index(),"backspace");
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише "Enter" |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyEnter(const long key_code)
{
//--- Выйти, если это не клавиша 'Enter'
if(key_code!=KEY_ENTER)
return(false);
//--- Если есть выделенный текст, удалим его
DeleteSelectedText();
//--- Если отключен многострочный режим
if(!m_multi_line_mode)
{
//--- Дезактивировать поле ввода
DeactivateTextBox();
//--- Обновить
Update(true);
//--- Отправим сообщение об этом
string str=(m_multi_line_mode)? TextCursorInfo() : "";
::EventChartCustom(m_chart_id,ON_END_EDIT,CElementBase::Id(),CElementBase::Index(),str);
return(false);
}
//--- Сместим строки на одну позицию вниз
ShiftOnePositionDown();
//--- Рассчитать размеры поля ввода
CalculateTextBoxSize();
//--- Установить новый размер полю ввода
ChangeTextBoxSize();
//--- Корректируем вертикальную полосу прокрутки
CorrectingVerticalScrollThumb();
//--- Переместить курсор в начало строки
SetTextCursor(0,m_text_cursor_y_pos);
//--- Переместить полосу прокрутки в начало
HorizontalScrolling(0);
//--- Обновить текст в поле ввода
DrawTextAndCursor(true);
//--- Отправим сообщение об этом
//::EventChartCustom(m_chart_id,ON_MOVE_TEXT_CURSOR,CElementBase::Id(),CElementBase::Index(),TextCursorInfo());
// Modifuqe esto agrege un nuevo evento
::EventChartCustom(m_chart_id,ON_MODIFY_TEXTBOX,CElementBase::Id(),CElementBase::Index(),"\n");
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише 'Left' |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyLeft(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Left' или (2) нажата клавиша 'Ctrl' или (3) клавиша 'Shift' нажата
if(key_code!=KEY_LEFT || m_keys.KeyCtrlState() || m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор влево на один символ
MoveTextCursor(TO_NEXT_LEFT_SYMBOL,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише 'Right' |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyRight(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Right' или (2) нажата клавиша 'Ctrl' или (3) клавиша 'Shift' нажата
if(key_code!=KEY_RIGHT || m_keys.KeyCtrlState() || m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор вправо на один символ
MoveTextCursor(TO_NEXT_RIGHT_SYMBOL,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише 'Up' |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyUp(const long key_code)
{
//--- Выйти, если отключен многострочный режим
if(!m_multi_line_mode)
return(false);
//--- Выйти, если (1) это не клавиша 'Up' или (2) клавиша 'Shift' нажата
if(key_code!=KEY_UP || m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор вверх на одну строку
MoveTextCursor(TO_NEXT_UP_LINE,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише 'Down' |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyDown(const long key_code)
{
//--- Выйти, если отключен многострочный режим
if(!m_multi_line_mode)
return(false);
//--- Выйти, если (1) это не клавиша 'Down' или (2) клавиша 'Shift' нажата
if(key_code!=KEY_DOWN || m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор вниз на одну строку
MoveTextCursor(TO_NEXT_DOWN_LINE,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише 'Home' |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyHome(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Home' или (2) клавиша 'Ctrl' нажата или (3) клавиша 'Shift' нажата
if(key_code!=KEY_HOME || m_keys.KeyCtrlState() || m_keys.KeyShiftState())
return(false);
//--- Переместить курсор в начало текущей строки
MoveTextCursor(TO_BEGIN_LINE,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише 'End' |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyEnd(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'End' или (2) клавиша 'Ctrl' нажата или (3) клавиша 'Shift' нажата
if(key_code!=KEY_END || m_keys.KeyCtrlState() || m_keys.KeyShiftState())
return(false);
//--- Переместить курсор в конец текущей строки
MoveTextCursor(TO_END_LINE,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка одновременного нажатия клавиш Ctrl + Left |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyCtrlAndLeft(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Left' и (2) клавиша 'Ctrl' не нажата или (3) клавиша 'Shift' нажата
if(!(key_code==KEY_LEFT && m_keys.KeyCtrlState()) || m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор влево на одно слово
MoveTextCursor(TO_NEXT_LEFT_WORD,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка одновременного нажатия клавиш Ctrl + Right |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyCtrlAndRight(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Right' и (2) клавиша 'Ctrl' не нажата или (3) клавиша 'Shift' нажата
if(!(key_code==KEY_RIGHT && m_keys.KeyCtrlState()) || m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор вправо на одно слово
MoveTextCursor(TO_NEXT_RIGHT_WORD,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка одновременного нажатия клавиш Ctrl + Home |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyCtrlAndHome(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Home' и (2) клавиша 'Ctrl' не нажата или (3) клавиша 'Shift' нажата
if(!(key_code==KEY_HOME && m_keys.KeyCtrlState()) || m_keys.KeyShiftState())
return(false);
//--- Переместить курсор в начало первой строки
MoveTextCursor(TO_BEGIN_FIRST_LINE,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка одновременного нажатия клавиш Ctrl + End |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyCtrlAndEnd(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'End' и (2) клавиша 'Ctrl' не нажата или (3) клавиша 'Shift' нажата
if(!(key_code==KEY_END && m_keys.KeyCtrlState()) || m_keys.KeyShiftState())
return(false);
//--- Переместить курсор в конец последней строки
MoveTextCursor(TO_END_LAST_LINE,false);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Shift + Left |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyShiftAndLeft(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Left' или (2) клавиша 'Ctrl' нажата или (3) клавиша 'Shift' не нажата
if(key_code!=KEY_LEFT || m_keys.KeyCtrlState() || !m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор влево на один символ
MoveTextCursor(TO_NEXT_LEFT_SYMBOL,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Shift + Right |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyShiftAndRight(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Right' или (2) клавиша 'Ctrl' нажата или (3) клавиша 'Shift' не нажата
if(key_code!=KEY_RIGHT || m_keys.KeyCtrlState() || !m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор вправо на один символ
MoveTextCursor(TO_NEXT_RIGHT_SYMBOL,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Shift + Up |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyShiftAndUp(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Up' или (2) клавиша 'Ctrl' нажата или (3) клавиша 'Shift' не нажата
if(key_code!=KEY_UP || m_keys.KeyCtrlState() || !m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор вверх на одну строку
MoveTextCursor(TO_NEXT_UP_LINE,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Shift + Down |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyShiftAndDown(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Down' или (2) клавиша 'Ctrl' нажата или (3) клавиша 'Shift' не нажата
if(key_code!=KEY_DOWN || m_keys.KeyCtrlState() || !m_keys.KeyShiftState())
return(false);
//--- Сместить текстовый курсор вниз на одну строку
MoveTextCursor(TO_NEXT_DOWN_LINE,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Shift + Home |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyShiftAndHome(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Home' или (2) клавиша 'Ctrl' нажата или (3) клавиша 'Shift' не нажата
if(key_code!=KEY_HOME || m_keys.KeyCtrlState() || !m_keys.KeyShiftState())
return(false);
//--- Переместить курсор в начало текущей строки
MoveTextCursor(TO_BEGIN_LINE,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Shift + End |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyShiftAndEnd(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'End' или (2) клавиша 'Ctrl' нажата или (3) клавиша 'Shift' не нажата
if(key_code!=KEY_END || m_keys.KeyCtrlState() || !m_keys.KeyShiftState())
return(false);
//--- Переместить курсор в конец текущей строки
MoveTextCursor(TO_END_LINE,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Ctrl + Shift + Left |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyCtrlShiftAndLeft(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Left' и (2) клавиша 'Ctrl' не нажата и (3) клавиша 'Shift' нажата
if(!(key_code==KEY_LEFT && m_keys.KeyCtrlState() && m_keys.KeyShiftState()))
return(false);
//--- Сместить текстовый курсор влево на одно слово
MoveTextCursor(TO_NEXT_LEFT_WORD,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Ctrl + Shift + Right |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyCtrlShiftAndRight(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Right' и (2) клавиша 'Ctrl' не нажата и (3) клавиша 'Shift' нажата
if(!(key_code==KEY_RIGHT && m_keys.KeyCtrlState() && m_keys.KeyShiftState()))
return(false);
//--- Сместить текстовый курсор вправо на одно слово
MoveTextCursor(TO_NEXT_RIGHT_WORD,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Ctrl + Shift + Home |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyCtrlShiftAndHome(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'Home' и (2) клавиша 'Ctrl' не нажата и (3) клавиша 'Shift' нажата
if(!(key_code==KEY_HOME && m_keys.KeyCtrlState() && m_keys.KeyShiftState()))
return(false);
//--- Переместить курсор в начало первой строки
MoveTextCursor(TO_BEGIN_FIRST_LINE,true);
return(true);
}
//+------------------------------------------------------------------+
//| Обработка нажатия на клавише Ctrl + Shift + End |
//+------------------------------------------------------------------+
bool CTextBox::OnPressedKeyCtrlShiftAndEnd(const long key_code)
{
//--- Выйти, если (1) это не клавиша 'End' и (2) клавиша 'Ctrl' не нажата и (3) клавиша 'Shift' нажата
if(!(key_code==KEY_END && m_keys.KeyCtrlState() && m_keys.KeyShiftState()))
return(false);
//--- Переместить курсор в конец последней строки
MoveTextCursor(TO_END_LAST_LINE,true);
return(true);
}
//+------------------------------------------------------------------+
//| Установить начальные индексы для выделенния текста |
//+------------------------------------------------------------------+
void CTextBox::SetStartSelectedTextIndexes(void)
{
//--- Если начальные индексы для выделения текста ещё не установлены
if(m_selected_line_from==WRONG_VALUE)
{
m_selected_line_from =(int)m_text_cursor_y_pos;
m_selected_symbol_from =(int)m_text_cursor_x_pos;
}
}
//+------------------------------------------------------------------+
//| Установить конечные индексы для выделенния текста |
//+------------------------------------------------------------------+
void CTextBox::SetEndSelectedTextIndexes(void)
{
//--- Установить конечные индексы для выделения текста
m_selected_line_to =(int)m_text_cursor_y_pos;
m_selected_symbol_to =(int)m_text_cursor_x_pos;
//--- Если все индексы равны, то сбросить выделение
if(m_selected_line_from==m_selected_line_to && m_selected_symbol_from==m_selected_symbol_to)
ResetSelectedText();
}
//+------------------------------------------------------------------+
//| Выделить весь текст |
//+------------------------------------------------------------------+
void CTextBox::SelectAllText(void)
{
//--- Получим размер массива символов
int symbols_total=::ArraySize(m_lines[0].m_symbol);
//--- Установить индексы для выделения текста
m_selected_line_from =0;
m_selected_line_to =0;
m_selected_symbol_from =0;
m_selected_symbol_to =symbols_total;
//--- Переместить ползунок горизонтальной полосы прокрутки на последнюю позицию
HorizontalScrolling();
//--- Переместить курсор в конец строки
SetTextCursor(symbols_total,0);
}
//+------------------------------------------------------------------+
//| Сбросить выделенный текст |
//+------------------------------------------------------------------+
void CTextBox::ResetSelectedText(void)
{
m_selected_line_from =WRONG_VALUE;
m_selected_line_to =WRONG_VALUE;
m_selected_symbol_from =WRONG_VALUE;
m_selected_symbol_to =WRONG_VALUE;
}
//+------------------------------------------------------------------+
//| Дезактивация поля ввода |
//+------------------------------------------------------------------+
void CTextBox::DeactivateTextBox(void)
{
//--- Выйти, если уже дезактивировано
if(!m_text_edit_state)
return;
//--- Дезактивировать
m_text_edit_state=false;
//--- Включим управление графиком
m_chart.SetInteger(CHART_KEYBOARD_CONTROL,true);
//--- Сброс выделения
ResetSelectedText();
//--- Если многострочный режим отключен
if(!m_multi_line_mode)
{
//--- Переместить курсор в начало строки
SetTextCursor(0,0);
//--- Сместить полосу прокрутки в начало строки
HorizontalScrolling(0);
}
}
//+------------------------------------------------------------------+
//| Ускоренная промотка полосы прокрутки |
//+------------------------------------------------------------------+
void CTextBox::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;
//--- Смещает поле ввода
ShiftData();
//--- Обновить полосы прокрутки
if(scroll_v) m_scrollv.Update(true);
if(scroll_h) m_scrollh.Update(true);
}
}
//+------------------------------------------------------------------+
//| Рисует текст |
//+------------------------------------------------------------------+
void CTextBox::Draw(void)
{
//--- Выводим текст
CTextBox::TextOut();
//--- Рисуем рамку
DrawBorder();
}
//+------------------------------------------------------------------+
//| Обновление элемента |
//+------------------------------------------------------------------+
void CTextBox::Update(const bool redraw=false)
{
//--- Перерисовать таблицу, если указано
if(redraw)
{
//--- Нарисовать
Draw();
//--- Применить
m_canvas.Update();
m_textbox.Update();
return;
}
//--- Применить
m_canvas.Update();
m_textbox.Update();
}
//+------------------------------------------------------------------+
//| Вывод текста на холст |
//+------------------------------------------------------------------+
void CTextBox::TextOut(void)
{
//--- Очистить холст
m_textbox.Erase(AreaColorCurrent());
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Корректировка в случае выхода из диапазона
m_text_cursor_y_pos=(m_text_cursor_y_pos>=lines_total)? lines_total-1 : m_text_cursor_y_pos;
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
//--- Если включен многострочный режим или количество символов больше нуля
if(m_multi_line_mode || symbols_total>0)
{
//--- Цвет текста
uint text_color=TextColorCurrent();
//--- Получим ширину строки
int line_width=(int)LineWidth(m_text_cursor_x_pos,m_text_cursor_y_pos);
//--- Получим высоту строки и пройдёмся по всем строкам в цикле
int line_height=(int)LineHeight();
m_textbox.FontSet();
for(uint i=0; i<lines_total; i++)
{
//--- Получим координаты для текста
int x=m_text_x_offset;
int y=m_text_y_offset+((int)i*line_height);
//--- Получим размер строки
uint string_length=::ArraySize(m_lines[i].m_symbol);
//--- Рисуем текст
for(uint s=0; s<string_length; s++)
{
//--- Если есть выделенный текст, определим его цвет, а также цвет фона текущего символа
if(CheckSelectedText(i,s))
{
//--- Цвет выделенного текста
text_color=::ColorToARGB(m_selected_text_color);
//--- Рассчитаем координаты для рисования фона
int x2 =x+m_lines[i].m_width[s];
int y2 =y+line_height-1;
//--- Рисуем цвет фона символа
m_textbox.FillRectangle(x,y,x2,y2,::ColorToARGB(m_selected_back_color,m_alpha));
}
else
text_color=TextColorCurrent();
//--- Нарисовать символ
m_textbox.TextOutFast(x,y,m_lines[i].m_symbol[s],text_color,TA_LEFT);
//--- X-координата для следующего символа
x+=m_lines[i].m_width[s];
}
}
}
//--- Если же многострочный режим отключен и нет ни одного символа, то будет отображаться текст по умолчанию (если указан)
else
{
if(m_default_text!="")
m_textbox.TextOut(m_area_x_size/2,m_area_y_size/2,m_default_text,::ColorToARGB(m_default_text_color),TA_CENTER|TA_VCENTER);
}
}
//+------------------------------------------------------------------+
//| Рисует рамку поля ввода |
//+------------------------------------------------------------------+
void CTextBox::DrawBorder(void)
{
//--- Получим смещение по оси X
int xo=(int)::ObjectGetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_XOFFSET);
int yo=(int)::ObjectGetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_YOFFSET);
//--- Границы
int x_size =(int)::ObjectGetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_XSIZE)-1;
int y_size =(int)::ObjectGetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_YSIZE)-1;
//--- Координаты
int x1=xo,y1=yo;
int x2=xo+x_size;
int y2=yo+y_size;
//--- Рисуем прямоугольник без заливки
m_canvas.Rectangle(x1,y1,x2,y2,BorderColorCurrent());
}
//+------------------------------------------------------------------+
//| Рисует текстовый курсор |
//+------------------------------------------------------------------+
void CTextBox::DrawCursor(void)
{
//--- Получим высоту строки
int line_height=(int)LineHeight();
//--- Получим X-координату курсора
CalculateTextCursorX();
//--- Нарисуем текстовый курсор
for(int i=0; i<line_height; i++)
{
//--- Получим Y-координату для пикселя
int y=m_text_y_offset+((int)m_text_cursor_y_pos*line_height)+i;
//--- Получим текущий цвет пикселя
uint pixel_color=m_textbox.PixelGet(m_text_cursor_x,y);
//--- Инвертируем цвет для курсора
pixel_color=::ColorToARGB(m_clr.Negative((color)pixel_color));
m_textbox.PixelSet(m_text_cursor_x,y,::ColorToARGB(pixel_color));
}
}
//+------------------------------------------------------------------+
//| Отображает текст и мигающий курсор |
//+------------------------------------------------------------------+
void CTextBox::DrawTextAndCursor(const bool show_state=false)
{
//--- Определим состояние для текстового курсора (показать/скрыть)
static bool state=false;
state=(!show_state)? !state : show_state;
//--- Выводим текст
CTextBox::TextOut();
//--- Нарисовать текстовый курсор
if(state)
DrawCursor();
//--- Обновить поле ввода
m_canvas.Update();
m_textbox.Update();
m_scrollh.Update(true);
m_scrollv.Update(true);
//--- Сброс счётчика
m_counter.ZeroTimeCounter();
}
//+------------------------------------------------------------------+
//| Возвращает цвет фона относительно текущего состояния элемента |
//+------------------------------------------------------------------+
uint CTextBox::AreaColorCurrent(void)
{
uint clr=(!CElementBase::IsLocked())? m_back_color : m_back_color_locked;
//--- Вернуть цвет
return(::ColorToARGB(clr,m_alpha));
}
//+------------------------------------------------------------------+
//| Возвращает цвет текста относительно текущего состояния элемента |
//+------------------------------------------------------------------+
uint CTextBox::TextColorCurrent(void)
{
uint clr=(!CElementBase::IsLocked())? m_label_color : m_label_color_locked;
//--- Вернуть цвет
return(::ColorToARGB(clr));
}
//+------------------------------------------------------------------+
//| Возвращает цвет рамки относительно текущего состояния элемента |
//+------------------------------------------------------------------+
uint CTextBox::BorderColorCurrent(void)
{
uint clr=clrBlack;
//--- Если элемент не заблокирован
if(!CElementBase::IsLocked())
{
//--- Если поле ввода активировано
if(m_text_edit_state)
clr=m_border_color_pressed;
//--- Если неактивировано, то проверяем фокус элемента
else
clr=(CElementBase::MouseFocus())? m_border_color_hover : m_border_color;
}
//--- Если элемент заблокирован
else
clr=m_border_color_locked;
//--- Вернуть цвет
return(::ColorToARGB(clr));
}
//+------------------------------------------------------------------+
//| Изменение цвета объектов |
//+------------------------------------------------------------------+
void CTextBox::ChangeObjectsColor(void)
{
//--- Отслеживаем изменение цвета, только если доступен сам и родитель
if(m_main.CElementBase::IsLocked() || !CElementBase::IsAvailable())
return;
//--- Если это момент пересечения границ элемента
if(CElementBase::CheckCrossingBorder())
{
//--- Изменить цвет
DrawBorder();
m_canvas.Update();
}
}
//+------------------------------------------------------------------+
//| Собирает строку из символов |
//+------------------------------------------------------------------+
string CTextBox::CollectString(const uint line_index,const uint symbols_total=0)
{
m_temp_input_string="";
//--- Получим размер строки
uint string_length=::ArraySize(m_lines[line_index].m_symbol);
//---
for(uint i=0; i<string_length; i++)
{
if(symbols_total>0)
{
if(i==symbols_total)
break;
}
//---
::StringAdd(m_temp_input_string,m_lines[line_index].m_symbol[i]);
}
//--- Вернуть собранную строку
return(m_temp_input_string);
}
//+------------------------------------------------------------------+
//| Добавляет символ и его свойства в массивы структуры |
//+------------------------------------------------------------------+
void CTextBox::AddSymbol(const string key_symbol)
{
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
//--- Установим новый размер массивам
ArraysResize(m_text_cursor_y_pos,symbols_total+1);
//--- Сместить все символы от конца массива к индексу добавляемого символа
MoveSymbols(m_text_cursor_y_pos,0,m_text_cursor_x_pos,false);
//--- Получим ширину символа
int width=m_textbox.TextWidth(key_symbol);
//--- Добавить символ в освободившийся элемент
m_lines[m_text_cursor_y_pos].m_symbol[m_text_cursor_x_pos] =key_symbol;
m_lines[m_text_cursor_y_pos].m_width[m_text_cursor_x_pos] =width;
//--- Увеличить счётчик позиции курсора
m_text_cursor_x_pos++;
}
//+------------------------------------------------------------------+
//| Удаляет символ |
//+------------------------------------------------------------------+
void CTextBox::DeleteSymbol(void)
{
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
//--- Если массив пустой
if(symbols_total<1)
{
//--- Установим курсор на нулевую позицию текщей строки
SetTextCursor(0,m_text_cursor_y_pos);
return;
}
//--- Получим позицию предыдущего символа
int check_pos=(int)m_text_cursor_x_pos-1;
//--- Выйти, если выход из диапазона
if(check_pos<0)
return;
//--- Сместить все символы на один элемент влево от индекса удаляемого символа
MoveSymbols(m_text_cursor_y_pos,m_text_cursor_x_pos,check_pos);
//--- Уменьшить счётчик позиции курсора
m_text_cursor_x_pos--;
//--- Установим новый размер массивам
ArraysResize(m_text_cursor_y_pos,symbols_total-1);
}
//+------------------------------------------------------------------+
//| Удаляет выделенный текст |
//+------------------------------------------------------------------+
bool CTextBox::DeleteSelectedText(void)
{
//--- Выйти, если текст не выделен
if(m_selected_line_from==WRONG_VALUE)
return(false);
//--- Если удаляются символы на одной строке
if(m_selected_line_from==m_selected_line_to)
DeleteTextOnOneLine();
//--- Если удаляются символы с нескольких строк
else
DeleteTextOnMultipleLines();
//--- Сброс выделенного текста
ResetSelectedText();
//--- Рассчитать размеры поля ввода
CalculateTextBoxSize();
//--- Установить новый размер полю ввода
ChangeTextBoxSize();
//--- Корректируем полосы прокрутки
CorrectingHorizontalScrollThumb();
CorrectingVerticalScrollThumb();
//--- Обновить текст в поле ввода
DrawTextAndCursor(true);
//--- Отправим сообщение об этом
::EventChartCustom(m_chart_id,ON_MOVE_TEXT_CURSOR,CElementBase::Id(),CElementBase::Index(),TextCursorInfo());
return(true);
}
//+------------------------------------------------------------------+
//| Удаляет выделенный на одной строке текст |
//+------------------------------------------------------------------+
void CTextBox::DeleteTextOnOneLine(void)
{
int symbols_total =::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
int symbols_to_delete =::fabs(m_selected_symbol_from-m_selected_symbol_to);
//--- Если начальный индекс символа справа
if(m_selected_symbol_to<m_selected_symbol_from)
{
//--- Сместим символы на освободившееся место в текущей строке
MoveSymbols(m_text_cursor_y_pos,m_selected_symbol_from,m_selected_symbol_to);
}
//--- Если начальный индекс символа слева
else
{
//--- Сместим текстовый курсор влево на количество удаляемых символов
m_text_cursor_x_pos-=symbols_to_delete;
//--- Сместим символы на освободившееся место в текущей строке
MoveSymbols(m_text_cursor_y_pos,m_selected_symbol_to,m_selected_symbol_from);
}
//--- Уменьшим размер массива текущей строки на извлечённое из неё количество символов
ArraysResize(m_text_cursor_y_pos,symbols_total-symbols_to_delete);
}
//+------------------------------------------------------------------+
//| Удаляет выделенный текст на нескольких строках |
//+------------------------------------------------------------------+
void CTextBox::DeleteTextOnMultipleLines(void)
{
//--- Общее количество символов на начальной и конечной строках
uint symbols_total_line_from =::ArraySize(m_lines[m_selected_line_from].m_symbol);
uint symbols_total_line_to =::ArraySize(m_lines[m_selected_line_to].m_symbol);
//--- Количество промежуточных строк для удаления
uint lines_to_delete=::fabs(m_selected_line_from-m_selected_line_to);
//--- Количество символов для удаления на начальной и конечной строках
uint symbols_to_delete_in_line_from =::fabs(symbols_total_line_from-m_selected_symbol_from);
uint symbols_to_delete_in_line_to =::fabs(symbols_total_line_to-m_selected_symbol_to);
//--- Если начальная строка ниже конечной
if(m_selected_line_from>m_selected_line_to)
{
//--- Скопируем в массив символы, которые нужно перенести
string array[];
CopyWrapSymbols(m_selected_line_from,m_selected_symbol_from,symbols_to_delete_in_line_from,array);
//--- Установим новый размер строке-приёмнику
uint new_size=m_selected_symbol_to+symbols_to_delete_in_line_from;
ArraysResize(m_selected_line_to,new_size);
//--- Добавить данные в массивы структуры строки-приёмника
PasteWrapSymbols(m_selected_line_to,m_selected_symbol_to,array);
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Сместим строки вверх на количество удаляемых строк
MoveLines(m_selected_line_to+1,lines_total-lines_to_delete,lines_to_delete,false);
//--- Установим новый размер массиву строк
::ArrayResize(m_lines,lines_total-lines_to_delete);
}
//--- Если начальная строка выше конечной
else
{
//--- Скопируем в массив символы, которые нужно перенести
string array[];
CopyWrapSymbols(m_selected_line_to,m_selected_symbol_to,symbols_to_delete_in_line_to,array);
//--- Установим новый размер строке-приёмнику
uint new_size=m_selected_symbol_from+symbols_to_delete_in_line_to;
ArraysResize(m_selected_line_from,new_size);
//--- Добавить данные в массивы структуры строки-приёмника
PasteWrapSymbols(m_selected_line_from,m_selected_symbol_from,array);
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Сместим строки вверх на количество удаляемых строк
MoveLines(m_selected_line_from+1,lines_total-lines_to_delete,lines_to_delete,false);
//--- Установим новый размер массиву строк
::ArrayResize(m_lines,lines_total-lines_to_delete);
//--- Переместить курсор на начальную позицию в выделении
SetTextCursor(m_selected_symbol_from,m_selected_line_from);
}
}
//+------------------------------------------------------------------+
//| Возвращает высоту строки |
//+------------------------------------------------------------------+
uint CTextBox::LineHeight(void)
{
//--- Установим шрифт для отображения на холсте (нужно, чтобы получить высоту строки)
m_textbox.FontSet(CElement::Font(),-CElement::FontSize()*10,FW_NORMAL);
//--- Вернём высоту строки
return(m_textbox.TextHeight("|"));
}
//+------------------------------------------------------------------+
//| Возвращает ширину строки от начала до указанной позиции |
//+------------------------------------------------------------------+
uint CTextBox::LineWidth(const uint symbol_index,const uint line_index)
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Предотвращение выхода из диапазона
uint l=(line_index<lines_total)? line_index : lines_total-1;
//--- Получим размер массива символов указанной строки
uint symbols_total=::ArraySize(m_lines[l].m_symbol);
//--- Предотвращение выхода из диапазона
uint s=(symbol_index<symbols_total)? symbol_index : symbols_total;
//--- Суммируем ширину всех символов
uint width=0;
for(uint i=0; i<s; i++)
width+=m_lines[l].m_width[i];
//--- Вернуть ширину строки
return(width);
}
//+------------------------------------------------------------------+
//| Возвращает максимальную ширину строки |
//+------------------------------------------------------------------+
uint CTextBox::MaxLineWidth(void)
{
uint max_line_width=0;
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
for(uint i=0; i<lines_total; i++)
{
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[i].m_symbol);
//--- Получим ширину строки
uint line_width=LineWidth(symbols_total,i);
//--- Сохраним максимальную ширину
if(line_width>max_line_width)
max_line_width=line_width;
}
//--- Вернуть максимальную ширину строки
return(max_line_width);
}
//+------------------------------------------------------------------+
//| Смещает строки на одну позицию вверх |
//+------------------------------------------------------------------+
void CTextBox::ShiftOnePositionUp(void)
{
//--- Если включен перенос слов
if(m_word_wrap_mode)
{
//--- Индекс предыдущей строки
uint prev_line_index=m_text_cursor_y_pos-1;
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[prev_line_index].m_symbol);
//--- Если предыдущая строка имеет признак окончания
if(m_lines[prev_line_index].m_end_of_line)
{
//--- (1) Уберём признак окончания и (2) переместим текстовый курсор в конец строки
m_lines[prev_line_index].m_end_of_line=false;
SetTextCursor(symbols_total,prev_line_index);
}
else
{
//--- (1) Переместим текстовый курсор в конец строки и (2) удалим символ
SetTextCursor(symbols_total,prev_line_index);
DeleteSymbol();
}
return;
}
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
//--- Если есть символы в этой строке, запомним их, чтобы добавить к предыдущей строке
m_temp_input_string=(symbols_total>0)? CollectString(m_text_cursor_y_pos) : "";
//--- Сместим строки от следующего элемента на одну строку вверх
MoveLines(m_text_cursor_y_pos,lines_total-1,1,false);
//--- Установим новый размер массиву строк
::ArrayResize(m_lines,lines_total-1);
//--- Уменьшим счётчик строк
m_text_cursor_y_pos--;
//--- Получим размер массива символов
symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
//--- Переместим курсор в конец
m_text_cursor_x_pos=symbols_total;
//--- Получим X-координату курсора
CalculateTextCursorX();
//--- Если есть строка, которую нужно добавить к предыдущей
if(m_temp_input_string!="")
AddToString(m_text_cursor_y_pos,m_temp_input_string);
}
//+------------------------------------------------------------------+
//| Смещает строки на одну позицию вниз |
//+------------------------------------------------------------------+
void CTextBox::ShiftOnePositionDown(void)
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Увеличим массив на один элемент
uint new_size=lines_total+1;
::ArrayResize(m_lines,new_size);
//--- Сместим строки от текущей позиции на один пункт вниз (начиная с конца массива)
MoveLines(lines_total,m_text_cursor_y_pos+1,1);
//--- Перенесём текст на новую строку
WrapTextToNewLine(m_text_cursor_y_pos,m_text_cursor_x_pos,true);
}
//+------------------------------------------------------------------+
//| Проверка наличия выделенного текста |
//+------------------------------------------------------------------+
bool CTextBox::CheckSelectedText(const uint line_index,const uint symbol_index)
{
bool is_selected_text=false;
//--- Выйти, если нет выделенного текста
if(m_selected_line_from==WRONG_VALUE)
return(false);
//--- Если начальный индекс на строке ниже
if(m_selected_line_from>m_selected_line_to)
{
//--- Конечная строка и символ справа от конечного выделенного
if((int)line_index==m_selected_line_to && (int)symbol_index>=m_selected_symbol_to)
{ is_selected_text=true; }
//--- Начальная строка и символ слева от начального выделенного
else if((int)line_index==m_selected_line_from && (int)symbol_index<m_selected_symbol_from)
{ is_selected_text=true; }
//--- Промежуточная строка (выделяются все символы)
else if((int)line_index>m_selected_line_to && (int)line_index<m_selected_line_from)
{ is_selected_text=true; }
}
//--- Если начальный индекс на строке выше
else if(m_selected_line_from<m_selected_line_to)
{
//--- Конечная строка и символ слева от конечного выделенного
if((int)line_index==m_selected_line_to && (int)symbol_index<m_selected_symbol_to)
{ is_selected_text=true; }
//--- Начальная строка и символ справа от начального выделенного
else if((int)line_index==m_selected_line_from && (int)symbol_index>=m_selected_symbol_from)
{ is_selected_text=true; }
//--- Промежуточная строка (выделяются все символы)
else if((int)line_index<m_selected_line_to && (int)line_index>m_selected_line_from)
{ is_selected_text=true; }
}
//--- Если начальный и конечный индекс на одной строке
else
{
//--- Нашли проверяемую строку
if((int)line_index>=m_selected_line_to && (int)line_index<=m_selected_line_from)
{
//--- Если смещение курсора вправо и символ в выделенном диапазоне
if(m_selected_symbol_from>m_selected_symbol_to)
{
if((int)symbol_index>=m_selected_symbol_to && (int)symbol_index<m_selected_symbol_from)
is_selected_text=true;
}
//--- Если смещение курсора влево и символ в выделенном диапазоне
else
{
if((int)symbol_index>=m_selected_symbol_from && (int)symbol_index<m_selected_symbol_to)
is_selected_text=true;
}
}
}
//--- Вернуть результат
return(is_selected_text);
}
//+------------------------------------------------------------------+
//| Проверка на наличие обязательной первой строки |
//+------------------------------------------------------------------+
uint CTextBox::CheckFirstLine(void)
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Если нет ни одной строки, устанавливаем размер массивам структуры
if(lines_total<1)
::ArrayResize(m_lines,++lines_total);
//--- Вернуть количество строк
return(lines_total);
}
//+------------------------------------------------------------------+
//| Устанавливает новый размер массивам свойств указанной строки |
//+------------------------------------------------------------------+
void CTextBox::ArraysResize(const uint line_index,const uint new_size)
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Предотвращение выхода из диапазона
uint l=(line_index<lines_total)? line_index : lines_total-1;
//--- Резервный размер массива
int reserve_size=100;
//--- Устанавливаем размер массивам структуры
::ArrayResize(m_lines[line_index].m_symbol,new_size,reserve_size);
::ArrayResize(m_lines[line_index].m_width,new_size,reserve_size);
}
//+------------------------------------------------------------------+
//| Делает копию указанной (source) строки в новое место (dest.) |
//+------------------------------------------------------------------+
void CTextBox::LineCopy(const uint destination,const uint source)
{
::ArrayCopy(m_lines[destination].m_width,m_lines[source].m_width);
::ArrayCopy(m_lines[destination].m_symbol,m_lines[source].m_symbol);
m_lines[destination].m_end_of_line=m_lines[source].m_end_of_line;
}
//+------------------------------------------------------------------+
//| Очищает указанную строку |
//+------------------------------------------------------------------+
void CTextBox::ClearLine(const uint line_index)
{
::ArrayFree(m_lines[line_index].m_symbol);
::ArrayFree(m_lines[line_index].m_width);
}
//+------------------------------------------------------------------+
//| Перемещение текстового курсора в указанном направлении |
//+------------------------------------------------------------------+
void CTextBox::MoveTextCursor(const ENUM_MOVE_TEXT_CURSOR direction)
{
switch(direction)
{
//--- Переместить курсор на один символ влево
case TO_NEXT_LEFT_SYMBOL : MoveTextCursorToLeft(); break;
//--- Переместить курсор на один символ вправо
case TO_NEXT_RIGHT_SYMBOL : MoveTextCursorToRight(); break;
//--- Переместить курсор на одно слово влево
case TO_NEXT_LEFT_WORD : MoveTextCursorToLeft(true); break;
//--- Переместить курсор на одно слово вправо
case TO_NEXT_RIGHT_WORD : MoveTextCursorToRight(true); break;
//--- Переместить курсор на одну строку вверх
case TO_NEXT_UP_LINE : MoveTextCursorToUp(); break;
//--- Переместить курсор на одну строку вниз
case TO_NEXT_DOWN_LINE : MoveTextCursorToDown(); break;
//--- Переместить курсор в начало текущей строки
case TO_BEGIN_LINE : SetTextCursor(0,m_text_cursor_y_pos); break;
//--- Переместить курсор в конец текущей строки
case TO_END_LINE :
{
//--- Получим количество символов в текущей строке
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
//--- Переместить курсор
SetTextCursor(symbols_total,m_text_cursor_y_pos);
break;
}
//--- Переместить курсор в начало первой строки
case TO_BEGIN_FIRST_LINE : SetTextCursor(0,0); break;
//--- Переместить курсор в конец последней строки
case TO_END_LAST_LINE :
{
//--- Получим количество строк и символов в последней строке
uint lines_total =::ArraySize(m_lines);
uint symbols_total =::ArraySize(m_lines[lines_total-1].m_symbol);
//--- Переместить курсор
SetTextCursor(symbols_total,lines_total-1);
break;
}
}
}
//+------------------------------------------------------------------+
//| Перемещение текстового курсора в указанном направлении и |
//| с условием |
//+------------------------------------------------------------------+
void CTextBox::MoveTextCursor(const ENUM_MOVE_TEXT_CURSOR direction,const bool with_highlighted_text)
{
//--- Если только перемещение текстового курсора
if(!with_highlighted_text)
{
//--- Сброс выделения
ResetSelectedText();
//--- Переместить курсор в начало первой строки
MoveTextCursor(direction);
}
//--- Если с выделением текста
else
{
//--- Установить начальные индексы для выделенния текста
SetStartSelectedTextIndexes();
//--- Сместить текстовый курсор на один символ
MoveTextCursor(direction);
//--- Установить конечные индексы для выделения текста
SetEndSelectedTextIndexes();
}
//--- Корректируем полосы прокрутки
CorrectingHorizontalScrollThumb();
CorrectingVerticalScrollThumb();
//--- Обновить текст в поле ввода
DrawTextAndCursor(true);
//--- Отправим сообщение об этом
::EventChartCustom(m_chart_id,ON_MOVE_TEXT_CURSOR,CElementBase::Id(),CElementBase::Index(),TextCursorInfo());
}
//+------------------------------------------------------------------+
//| Перемещение текстового курсора влево |
//+------------------------------------------------------------------+
void CTextBox::MoveTextCursorToLeft(const bool to_next_word=false)
{
//--- Если перейти к следующему символу
if(!to_next_word)
{
//--- Если позиция текстового курсора больше нуля
if(m_text_cursor_x_pos>0)
{
//--- Сместим его на предыдущий символ
m_text_cursor_x-=m_lines[m_text_cursor_y_pos].m_width[m_text_cursor_x_pos-1];
//--- Уменьшим счётчик символов
m_text_cursor_x_pos--;
}
else
{
//--- Если это не первая строка
if(m_text_cursor_y_pos>0)
{
//--- Перейдём в конец предыдущей строки
m_text_cursor_y_pos--;
CorrectingTextCursorXPos();
}
}
return;
}
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Получим количество символов в текущей строке
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
//--- Если курсор в начале текущей строки и это не первая строка,
// переместим курсор в конец предыдущей строки
if(m_text_cursor_x_pos==0 && m_text_cursor_y_pos>0)
{
//--- Получим индекс предыдущей строки
uint prev_line_index=m_text_cursor_y_pos-1;
//--- Получим количество символов предыдущей строки
symbols_total=::ArraySize(m_lines[prev_line_index].m_symbol);
//--- Переместим курсор в конец предыдущей строки
SetTextCursor(symbols_total,prev_line_index);
}
//--- Если курсор не в начале текущей строки или курсор на первой строке
else
{
//--- Найдём начало непрерывной последовательности символов (справа налево)
for(uint i=m_text_cursor_x_pos; i<=symbols_total; i--)
{
//--- Перейти к следующему, если курсор в конце строки
if(i==symbols_total)
continue;
//--- Если это первый символ строки
if(i==0)
{
//--- Установим курсор в начало строки
SetTextCursor(0,m_text_cursor_y_pos);
break;
}
//--- Если это не первый символ строки
else
{
//--- Если нашли начало непрерывной последовательности, на которой впервые.
// Началом считается пробел на следующем индексе.
if(i!=m_text_cursor_x_pos &&
m_lines[m_text_cursor_y_pos].m_symbol[i]!=SPACE &&
m_lines[m_text_cursor_y_pos].m_symbol[i-1]==SPACE)
{
//--- Установим курсор в начало новой непрерывной последовательности
SetTextCursor(i,m_text_cursor_y_pos);
break;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Перемещение текстового курсора на один символ вправо |
//+------------------------------------------------------------------+
void CTextBox::MoveTextCursorToRight(const bool to_next_word=false)
{
//--- Если перейти к следующему символу
if(!to_next_word)
{
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_width);
//--- Если это не конец строки
if(m_text_cursor_x_pos<symbols_total)
{
//--- Сместим позицию текстового курсора на следующий символ
m_text_cursor_x+=m_lines[m_text_cursor_y_pos].m_width[m_text_cursor_x_pos];
//--- Увеличим счётчик символов
m_text_cursor_x_pos++;
}
else
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Если это не последняя строка
if(m_text_cursor_y_pos<lines_total-1)
{
//--- Переместить курсор в начало следующей строки
m_text_cursor_x=m_text_x_offset;
SetTextCursor(0,++m_text_cursor_y_pos);
}
}
return;
}
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Получим количество символов в текущей строке
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_symbol);
//--- Если курсор в конце строки и это не последняя строка, переместим курсор в начало следующей строки
if(m_text_cursor_x_pos==symbols_total && m_text_cursor_y_pos<lines_total-1)
{
SetTextCursor(0,m_text_cursor_y_pos+1);
}
//--- Если курсор не в конце строки или это последняя строка
else
{
//--- Найдём начало непрерывной последовательности символов (слева направо)
for(uint i=m_text_cursor_x_pos; i<=symbols_total; i++)
{
//--- Если это первый символ, перейти к следующему
if(i==0)
continue;
//--- Если дошли до конца строки, переместить курсор в конец
if(i>=symbols_total-1)
{
SetTextCursor(symbols_total,m_text_cursor_y_pos);
break;
}
//--- Если нашли начало непрерывной последовательности, на которой впервые.
// Началом считается пробел на предыдущем индексе.
if(i!=m_text_cursor_x_pos &&
m_lines[m_text_cursor_y_pos].m_symbol[i]!=SPACE &&
m_lines[m_text_cursor_y_pos].m_symbol[i-1]==SPACE)
{
//--- Установим курсор в конец новой непрерывной последовательности
SetTextCursor(i,m_text_cursor_y_pos);
break;
}
}
}
}
//+------------------------------------------------------------------+
//| Перемещение текстового курсора на одну строку вверх |
//+------------------------------------------------------------------+
void CTextBox::MoveTextCursorToUp(void)
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Если не выходим за пределы массива
if(m_text_cursor_y_pos-1<lines_total)
{
//--- Переход на предыдущую строку
m_text_cursor_y_pos--;
//--- Корректировка текстового курсора по оси X
CorrectingTextCursorXPos(m_text_cursor_x_pos);
}
}
//+------------------------------------------------------------------+
//| Перемещение текстового курсора на одну строку вниз |
//+------------------------------------------------------------------+
void CTextBox::MoveTextCursorToDown(void)
{
uint lines_total=::ArraySize(m_lines);
//--- Если не выходим за пределы массива
if(m_text_cursor_y_pos+1<lines_total)
{
//--- Переход на следующую строку
m_text_cursor_y_pos++;
//--- Корректировка текстового курсора по оси X
CorrectingTextCursorXPos(m_text_cursor_x_pos);
}
}
//+------------------------------------------------------------------+
//| Устанавливает курсор по указанным позициям |
//+------------------------------------------------------------------+
void CTextBox::SetTextCursor(const uint x_pos,const uint y_pos)
{
m_text_cursor_x_pos=x_pos;
m_text_cursor_y_pos=(!m_multi_line_mode)? 0 : y_pos;
}
//+------------------------------------------------------------------+
//| Устанавливает курсор по указанным позициям по курсору мыши |
//+------------------------------------------------------------------+
void CTextBox::SetTextCursorByMouseCursor(void)
{
//--- Определим координаты на поле ввода под курсором мыши
int x =m_mouse.RelativeX(m_textbox);
int y =m_mouse.RelativeY(m_textbox);
//--- Получим высоту строки
int line_height=(int)LineHeight();
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Определим символ нажатия
for(uint l=0; l<lines_total; l++)
{
//--- Зададим начальные координаты для проверки условия
int x_offset=m_text_x_offset;
int y_offset=m_text_y_offset+((int)l*line_height);
//--- Проверка условия по оси Y
bool y_pos_check=(l<lines_total-1)?(y>=y_offset && y<y_offset+line_height) : y>=y_offset;
//--- Если нажатие было не на этой строке, перейти к следующей
if(!y_pos_check)
continue;
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[l].m_width);
//--- Если это пустая строка, переместить курсор на указанную позицию и выйти из цикла
if(symbols_total<1)
{
SetTextCursor(0,l);
HorizontalScrolling(0);
break;
}
//--- Найдём символ, на который нажали
for(uint s=0; s<symbols_total; s++)
{
//--- Если нашли символ, переместить курсор на указанную позицию и выйти из цикла
if(x>=x_offset && x<x_offset+m_lines[l].m_width[s])
{
SetTextCursor(s,l);
l=lines_total;
break;
}
//--- Прибавить ширину текущего символа для следующей проверки
x_offset+=m_lines[l].m_width[s];
//--- Если это последний символ, переместим курсор в конец строки и выйдем из цикла
if(s==symbols_total-1 && x>x_offset)
{
SetTextCursor(s+1,l);
l=lines_total;
break;
}
}
}
}
//+------------------------------------------------------------------+
//| Корректировка текстового курсора по оси X |
//+------------------------------------------------------------------+
void CTextBox::CorrectingTextCursorXPos(const int x_pos=WRONG_VALUE)
{
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[m_text_cursor_y_pos].m_width);
//--- Опеределим позицию курсора
uint text_cursor_x_pos=0;
//--- Если позиция указана
if(x_pos!=WRONG_VALUE)
text_cursor_x_pos=(x_pos>(int)symbols_total-1)? symbols_total : x_pos;
//--- Если позиция не указана, то установим курсор в конец строки
else
text_cursor_x_pos=symbols_total;
//--- Нулевая позиция, если в строке нет символов
m_text_cursor_x_pos=(symbols_total<1)? 0 : text_cursor_x_pos;
//--- Получим X-координату курсора
CalculateTextCursorX();
}
//+------------------------------------------------------------------+
//| Расчёт X-координаты для текстового курсора |
//+------------------------------------------------------------------+
void CTextBox::CalculateTextCursorX(void)
{
//--- Получим ширину строки
int line_width=(int)LineWidth(m_text_cursor_x_pos,m_text_cursor_y_pos);
//--- Рассчитать и сохранить X-координату курсора
m_text_cursor_x=m_text_x_offset+line_width;
}
//+------------------------------------------------------------------+
//| Расчёт Y-координаты для текстового курсора |
//+------------------------------------------------------------------+
void CTextBox::CalculateTextCursorY(void)
{
//--- Получим высоту строки
int line_height=(int)LineHeight();
//--- Получим Y-координату курсора
m_text_cursor_y=m_text_y_offset+int(line_height*m_text_cursor_y_pos);
}
//+------------------------------------------------------------------+
//| Расчёт границ поля ввода по двум осям |
//+------------------------------------------------------------------+
void CTextBox::CalculateBoundaries(void)
{
CalculateXBoundaries();
CalculateYBoundaries();
}
//+------------------------------------------------------------------+
//| Расчёт границ поля ввода по оси X |
//+------------------------------------------------------------------+
void CTextBox::CalculateXBoundaries(void)
{
//--- Получим X-координату и смещение по оси X
int x =(int)::ObjectGetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XDISTANCE);
int xoffset =(int)::ObjectGetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XOFFSET);
//--- Рассчитаем границы видимой части поля ввода
m_x_limit =(x+xoffset)-x;
m_x2_limit =(m_multi_line_mode)? (x+xoffset+m_x_size-m_scrollv.ScrollWidth()-m_text_x_offset)-x : (x+xoffset+m_x_size-m_text_x_offset)-x;
}
//+------------------------------------------------------------------+
//| Расчёт границ поля ввода по оси Y |
//+------------------------------------------------------------------+
void CTextBox::CalculateYBoundaries(void)
{
//--- Выйти, если отключен многострочный режим
if(!m_multi_line_mode)
return;
//--- Получим Y-координату и смещение по оси Y
int y =(int)::ObjectGetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YDISTANCE);
int yoffset =(int)::ObjectGetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YOFFSET);
//--- Рассчитаем границы видимой части поля ввода
m_y_limit =(y+yoffset)-y;
m_y2_limit =(y+yoffset+m_y_size-m_scrollh.ScrollWidth())-y;
}
//+------------------------------------------------------------------+
//| Расчёт X-координаты полосы прокрутки в левой границе поля ввода |
//+------------------------------------------------------------------+
int CTextBox::CalculateScrollThumbX(void)
{
return(m_text_cursor_x-m_text_x_offset);
}
//+------------------------------------------------------------------+
//| Расчёт X-координаты полосы прокрутки в правой границе поля ввода |
//+------------------------------------------------------------------+
int CTextBox::CalculateScrollThumbX2(void)
{
return((m_multi_line_mode)? m_text_cursor_x-m_x_size+m_scrollv.ScrollWidth()+m_text_x_offset : m_text_cursor_x-m_x_size+m_text_x_offset*2);
}
//+------------------------------------------------------------------+
//| Расчёт X-позиции ползунка полосы прокрутки |
//+------------------------------------------------------------------+
int CTextBox::CalculateScrollPosX(const bool to_right=false)
{
int calc_x =(!to_right)? CalculateScrollThumbX() : CalculateScrollThumbX2();
double pos_x_value =(calc_x-::fmod((double)calc_x,(double)m_shift_x_step))/m_shift_x_step+((!to_right)? 0 : 1);
//---
return((int)pos_x_value);
}
//+------------------------------------------------------------------+
//| Расчёт Y-координаты полосы прокрутки в верхней границе поля ввода|
//+------------------------------------------------------------------+
int CTextBox::CalculateScrollThumbY(void)
{
return(m_text_cursor_y-m_text_y_offset);
}
//+------------------------------------------------------------------+
//| Расчёт Y-координаты полосы прокрутки в нижней границе поля ввода |
//+------------------------------------------------------------------+
int CTextBox::CalculateScrollThumbY2(void)
{
//--- Установим шрифт для отображения на холсте (нужно, чтобы получить высоту строки)
m_textbox.FontSet(CElement::Font(),-CElement::FontSize()*10,FW_NORMAL);
//--- Получим высоту строки
int line_height=m_textbox.TextHeight("|");
//--- Рассчитать и вернуть значение
return(m_text_cursor_y-m_y_size+m_scrollh.ScrollWidth()+m_text_y_offset+line_height);
}
//+------------------------------------------------------------------+
//| Расчёт Y-позиции ползунка полосы прокрутки |
//+------------------------------------------------------------------+
int CTextBox::CalculateScrollPosY(const bool to_down=false)
{
int calc_y =(!to_down)? CalculateScrollThumbY() : CalculateScrollThumbY2();
double pos_y_value =(calc_y-::fmod((double)calc_y,(double)LineHeight()))/LineHeight()+((!to_down)? 0 : 1);
//---
return((int)pos_y_value);
}
//+------------------------------------------------------------------+
//| Корректировка горизонтальной полосы прокрутки |
//+------------------------------------------------------------------+
void CTextBox::CorrectingHorizontalScrollThumb(void)
{
//--- Получим границы видимой части поля ввода
CalculateXBoundaries();
//--- Получим X-координату курсора
CalculateTextCursorX();
//--- Если текстовый курсор вышел из поля видимости влево
if(m_text_cursor_x<=m_x_limit)
{
HorizontalScrolling(CalculateScrollPosX());
}
//--- Если текстовый курсор вышел из поля видимости вправо
else if(m_text_cursor_x>=m_x2_limit)
{
HorizontalScrolling(CalculateScrollPosX(true));
}
}
//+------------------------------------------------------------------+
//| Корректировка вертикальной полосы прокрутки |
//+------------------------------------------------------------------+
void CTextBox::CorrectingVerticalScrollThumb(void)
{
//--- Получим границы видимой части поля ввода
CalculateYBoundaries();
//--- Получим Y-координату курсора
CalculateTextCursorY();
//--- Если текстовый курсор вышел из поля видимости вверх
if(m_text_cursor_y<=m_y_limit)
{
VerticalScrolling(CalculateScrollPosY());
}
//--- Если текстовый курсор вышел из поля видимости вниз
else if(m_text_cursor_y+(int)LineHeight()>=m_y2_limit)
{
VerticalScrolling(CalculateScrollPosY(true));
}
}
//+------------------------------------------------------------------+
//| Рассчитывает размеры текстового поля ввода |
//+------------------------------------------------------------------+
void CTextBox::CalculateTextBoxSize(void)
{
CalculateTextBoxXSize();
CalculateTextBoxYSize();
}
//+------------------------------------------------------------------+
//| Рассчитывает ширину текстового поля ввода |
//+------------------------------------------------------------------+
bool CTextBox::CalculateTextBoxXSize(void)
{
//--- Запомним текущие размеры
int area_x_size_curr=m_area_x_size;
//--- Получим максимальную ширину строки из текстового поля ввода
int max_line_width=int((m_text_x_offset*2)+MaxLineWidth()+m_scrollv.ScrollWidth());
//--- Определим общую ширину
m_area_x_size=(max_line_width>m_x_size)? max_line_width : m_x_size;
//--- Определим видимую ширину
m_area_visible_x_size=m_x_size-2;
//--- Сохраним ограничение по смещению
m_shift_x2_limit=m_area_x_size-m_area_visible_x_size;
//--- Признак того, что размеры не изменились
if(area_x_size_curr==m_area_x_size)
return(false);
//--- Признак того, что размеры были изменились
return(true);
}
//+------------------------------------------------------------------+
//| Рассчитывает высоту текстового поля ввода |4
//+------------------------------------------------------------------+
bool CTextBox::CalculateTextBoxYSize(void)
{
//--- Запомним текущие размеры
int area_y_size_curr=m_area_y_size;
//--- Получим высоту строки
int line_height=(int)LineHeight();
//--- Получим размер массива строк
int lines_total=::ArraySize(m_lines);
//--- Рассчитаем общую высоту элемента
int lines_height=int((m_text_y_offset*2)+(line_height*lines_total)+m_scrollh.ScrollWidth());//*2);
//--- Определим общую высоту
m_area_y_size=(m_multi_line_mode && lines_height>m_y_size)? lines_height : m_y_size;
//--- Определим видимую высоту
m_area_visible_y_size=m_y_size-2;
//--- Сохраним ограничение по смещению
m_shift_y2_limit=m_area_y_size-m_area_visible_y_size;
//--- Признак того, что размеры не изменились
if(area_y_size_curr==m_area_y_size)
return(false);
//--- Признак того, что размеры были изменились
return(true);
}
//+------------------------------------------------------------------+
//| Изменить основные размеры элемента |
//+------------------------------------------------------------------+
void CTextBox::ChangeMainSize(const int x_size,const int y_size)
{
//--- Установить новый размер
CElementBase::XSize(x_size);
CElementBase::YSize(y_size);
m_canvas.XSize(m_x_size);
m_canvas.YSize(m_y_size);
m_canvas.Resize(m_x_size,m_y_size);
}
//+------------------------------------------------------------------+
//| Изменить размеры поля ввода |
//+------------------------------------------------------------------+
void CTextBox::ChangeTextBoxSize(const bool is_x_offset=false,const bool is_y_offset=false)
{
//--- Установить новый размер таблице
m_textbox.XSize(m_area_x_size);
m_textbox.YSize(m_area_y_size);
m_textbox.Resize(m_area_x_size,m_area_y_size);
//--- Установим размеры видимой области
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XSIZE,m_area_visible_x_size);
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YSIZE,m_area_visible_y_size);
//--- Разница между общей шириной и видимой частью
int x_different =m_area_x_size-m_area_visible_x_size;
int y_different =m_area_y_size-m_area_visible_y_size;
//--- Зададим смещение фрейма внутри изображения по осям X и Y
int x_offset =(int)::ObjectGetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XOFFSET);
int y_offset =(int)::ObjectGetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YOFFSET);
int x_offset2 =(!is_x_offset)? 0 : (x_offset<=x_different)? x_offset : x_different;
int y_offset2 =(!is_y_offset)? 0 : (y_offset<=y_different)? y_offset : y_different;
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_XOFFSET,x_offset2);
::ObjectSetInteger(m_chart_id,m_textbox.ChartObjectName(),OBJPROP_YOFFSET,y_offset2);
//--- Изменить размеры полос прокрутки
ChangeScrollsSize();
//--- Перенос по словам
WordWrap();
//--- Корректировка данных
ShiftData();
}
//+------------------------------------------------------------------+
//| Изменить размеры полос прокрутки |
//+------------------------------------------------------------------+
void CTextBox::ChangeScrollsSize(void)
{
//--- Расчёт количества шагов для смещения
uint x_size_total =m_area_x_size/m_shift_x_step;
uint visible_x_size_total =m_area_visible_x_size/m_shift_x_step;
uint y_size_total =LinesTotal()+1;
uint visible_y_size_total =VisibleLinesTotal();
//--- Рассчитать размеры полос прокрутки
m_scrollh.Reinit(x_size_total,visible_x_size_total);
m_scrollv.Reinit(y_size_total,visible_y_size_total);
//--- Выйти, если это однострочное поле ввода
if(!m_multi_line_mode)
return;
//--- Если (1) горизонтальная полоса прокрутки не нужна или (2) включен перенос по словам
if(!m_scrollh.IsScroll() || m_word_wrap_mode)
{
HorizontalScrolling(0);
//--- Скрыть горизонтальную полосу прокрутки
m_scrollh.Hide();
//--- Изменить высоту вертикальной полосы прокрутки
if(m_multi_line_mode)
m_scrollv.ChangeYSize(CElementBase::YSize()-2);
}
else
{
//--- Показать горизонтальную полосу прокрутки
if(CElementBase::IsVisible())
{
m_scrollh.Show();
m_scrollh.GetIncButtonPointer().Show();
m_scrollh.GetDecButtonPointer().Show();
//--- Отправим сообщение об изменении в графическом интерфейсе
::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
}
//--- Рассчитать и изменить высоту вертикальной полосы прокрутки
if(m_multi_line_mode)
m_scrollv.ChangeYSize(CElementBase::YSize()-m_scrollh.ScrollWidth()-2);
}
//--- Если вертикальная полоса прокрутки не нужна
if(!m_scrollv.IsScroll())
{
VerticalScrolling(0);
//--- Скрыть вертикальную полосу прокрутки
m_scrollv.Hide();
//--- Изменить ширину горизонтальной полосы прокрутки, если отключен перенос по словам
if(!m_word_wrap_mode)
m_scrollh.ChangeXSize(CElementBase::XSize()-1);
}
else
{
//--- Показать вертикальную полосу прокрутки
if(CElementBase::IsVisible())
{
m_scrollv.Show();
m_scrollv.GetIncButtonPointer().Show();
m_scrollv.GetDecButtonPointer().Show();
//--- Отправим сообщение об изменении в графическом интерфейсе
::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
}
//--- Изменить ширину горизонтальной полосы прокрутки, если отключен перенос по словам
if(!m_word_wrap_mode)
m_scrollh.ChangeXSize(CElementBase::XSize()-m_scrollh.ScrollWidth()-1);
}
}
//+------------------------------------------------------------------+
//| Перенос по словам |
//+------------------------------------------------------------------+
void CTextBox::WordWrap(void)
{
//--- Выйти, если режимы (1) многострочного поля ввода или (2) переноса по словам отключены
if(!m_multi_line_mode || !m_word_wrap_mode)
return;
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Проверим, нужно ли выровнять текст по ширине поля ввода
for(uint i=0; i<lines_total; i++)
{
//--- Для определения первых видимых индексов (1) символа и (2) пробела
int symbol_index =WRONG_VALUE;
int space_index =WRONG_VALUE;
//--- Индекс следующей строки
uint next_line_index=i+1;
//--- Если строка не помещается, то перенесём часть текущей строки на новую строку
if(CheckForOverflow(i,symbol_index,space_index))
{
//--- Если пробел найден, то он переносится не будет
if(space_index!=WRONG_VALUE)
space_index++;
//--- Увеличим массив строк на один элемент
::ArrayResize(m_lines,++lines_total);
//--- Сместим строки от текущей позиции на один пункт вниз
MoveLines(lines_total-1,next_line_index,1);
//--- Проверим индекс символа, от которого будет перенос текста
int check_index=(space_index==WRONG_VALUE && symbol_index!=WRONG_VALUE)? symbol_index : space_index;
//--- Перенесём текст на новую строку
WrapTextToNewLine(i,check_index);
}
//--- Если строка помещается, то проверим, не нужно ли осуществить обратный перенос
else
{
//--- Пропускаем, если (1) это строка с окончанием или (2) это последняя строка
if(m_lines[i].m_end_of_line || next_line_index>=lines_total)
continue;
//--- Определим количество символов для переноса
uint wrap_symbols_total=0;
//--- Если нужно перенести оставшийся текст следующей строки на текущую
if(WrapSymbolsTotal(i,wrap_symbols_total))
{
WrapTextToPrevLine(next_line_index,wrap_symbols_total,true);
//--- Обновить размер массива для дальнейшего использования в цикле
lines_total=::ArraySize(m_lines);
//--- Шаг назад, чтобы избежать пропуск строки для следующей проверки
i--;
}
//--- Перенести только то, что помещается
else
WrapTextToPrevLine(next_line_index,wrap_symbols_total);
}
}
}
//+------------------------------------------------------------------+
//| Проверка на переполнение строки |
//+------------------------------------------------------------------+
bool CTextBox::CheckForOverflow(const uint line_index,int &symbol_index,int &space_index)
{
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[line_index].m_symbol);
//--- Отступы
uint x_offset_plus=m_text_x_offset+m_scrollv.XSize();
//--- Получим полную ширину строки
uint full_line_width=LineWidth(symbols_total,line_index)+x_offset_plus;
//--- Если ширина этой строки помещается в поле
if(full_line_width<(uint)m_area_visible_x_size)
return(false);
//--- Определим индексы символов переполнения
for(uint s=symbols_total-1; s>0; s--)
{
//--- Получим (1) ширину подстроки от начала до текущего символа и (2) символ
uint line_width =LineWidth(s,line_index)+x_offset_plus;
string symbol =m_lines[line_index].m_symbol[s];
//--- Если ещё не нашли видимый символ
if(symbol_index==WRONG_VALUE)
{
//--- Если ширина подстроки помещается в область поля ввода, запомним индекс символа
if(line_width<(uint)m_area_visible_x_size)
symbol_index=(int)s;
//--- Перейти к следующему символу
continue;
}
//--- Если это пробел, запомним его индекс и остановим цикл
if(symbol==SPACE)
{
space_index=(int)s;
break;
}
}
//--- Выполнение условия означает, что строка не помещается
bool is_overflow=(symbol_index!=WRONG_VALUE || space_index!=WRONG_VALUE);
//--- Вернуть результат
return(is_overflow);
}
//+------------------------------------------------------------------+
//| Возвращает количество слов в указанной строке |
//+------------------------------------------------------------------+
uint CTextBox::WordsTotal(const uint line_index)
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Предотвращение выхода из диапазона
uint l=(line_index<lines_total)? line_index : lines_total-1;
//--- Получим размер массива символов указанной строки
uint symbols_total=::ArraySize(m_lines[l].m_symbol);
//--- Счётчик слов
uint words_counter=0;
//--- Ищем пробел по указанному индексу
for(uint s=1; s<symbols_total; s++)
{
//--- Считаем, если (2) дошли до конца строки или (2) нашли пробел (конец слова)
if(s+1==symbols_total || (m_lines[l].m_symbol[s]!=SPACE && m_lines[l].m_symbol[s-1]==SPACE))
words_counter++;
}
//--- Вернуть количество слов
return((words_counter<1)? 1 : words_counter);
}
//+------------------------------------------------------------------+
//| Возвращает количество переносимых символов с признаком объёма |
//+------------------------------------------------------------------+
bool CTextBox::WrapSymbolsTotal(const uint line_index,uint &wrap_symbols_total)
{
//--- Признаки (1) количества символов для переноса и (2) строки без пробелов
bool is_all_text=false,is_solid_row=false;
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[line_index].m_symbol);
//--- Отступы
uint x_offset_plus=m_text_x_offset+m_scrollv.XSize();
//--- Получим полную ширину строки
uint full_line_width=LineWidth(symbols_total,line_index)+x_offset_plus;
//--- Получим ширину свободного пространства
uint free_space=m_area_visible_x_size-full_line_width;
//--- Получим количество слов в следующей строке
uint next_line_index =line_index+1;
uint words_total =WordsTotal(next_line_index);
//--- Получим размер массива символов
uint next_line_symbols_total=::ArraySize(m_lines[next_line_index].m_symbol);
//--- Определить количество слов, которые можно перенести со следующей строки (поиск по пробелу)
for(uint w=0; w<words_total; w++)
{
//--- Получим (1) индекс пробела и (2) ширину подстроки от начала до пробела
uint ss_index =SymbolIndexBySpaceNumber(next_line_index,w);
uint substring_width =LineWidth(ss_index,next_line_index);
//--- Если подстрока помещается в свободное пространство текущей строки
if(substring_width<free_space)
{
//--- ...проверим, можно ли добавить ещё одно слово
wrap_symbols_total=ss_index;
//--- Остановиться, если это вся строка
if(next_line_symbols_total==wrap_symbols_total)
{
is_all_text=true;
break;
}
}
else
{
//--- Если это сплошная строка без пробела
if(ss_index==next_line_symbols_total)
is_solid_row=true;
//---
break;
}
}
//--- Сразу вернуть результат, если (1) это строка с пробелом или (2) нет свободного места
if(!is_solid_row || free_space<1)
return(is_all_text);
//--- Получим полную ширину следующей строки
full_line_width=LineWidth(next_line_symbols_total,next_line_index)+x_offset_plus;
//--- Если (1) строка не помещается и нет пробелов в конце (2) текущей и (3) предыдущей строках
if(full_line_width>free_space &&
m_lines[line_index].m_symbol[symbols_total-1]!=SPACE &&
m_lines[next_line_index].m_symbol[next_line_symbols_total-1]!=SPACE)
{
//--- Определить количество символов, которые можно перенести со следующей строки
for(uint s=next_line_symbols_total-1; s>=0; s--)
{
//--- Получим ширину подстроки от начала до указанного символа
uint substring_width=LineWidth(s,next_line_index);
//--- Если подстрока не помещается в свободное пространство указанного контейнера, перейти к следующему символу
if(substring_width>=free_space)
continue;
//--- Если подстрока помещается, запомним значение и остановимся
wrap_symbols_total=s;
break;
}
}
//--- Вернуть истину, если нужно перенести весь текст
return(is_all_text);
}
//+------------------------------------------------------------------+
//| Возвращает индекс символа пробела по его номеру |
//+------------------------------------------------------------------+
uint CTextBox::SymbolIndexBySpaceNumber(const uint line_index,const uint space_index)
{
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Предотвращение выхода из диапазона
uint l=(line_index<lines_total)? line_index : lines_total-1;
//--- Получим размер массива символов указанной строки
uint symbols_total=::ArraySize(m_lines[l].m_symbol);
//--- (1) Для определения индекса символа пробела и (2) счётчик пробелов
uint symbol_index =0;
uint space_counter =0;
//--- Ищем пробел по указанному индексу
for(uint s=1; s<symbols_total; s++)
{
//--- Если нашли пробел
if(m_lines[l].m_symbol[s]!=SPACE && m_lines[l].m_symbol[s-1]==SPACE)
{
//--- Если счётчик равен указанному индексу пробела, запомним его и остановим цикл
if(space_counter==space_index)
{
symbol_index=s;
break;
}
//--- Увеличим счётчик пробелов
space_counter++;
}
}
//--- Вернуть размер строки, если не нашли индекс пробела
return((symbol_index<1)? symbols_total : symbol_index);
}
//+------------------------------------------------------------------+
//| Перемещение строк на указанное количество позиций |
//+------------------------------------------------------------------+
void CTextBox::MoveLines(const uint from_index,const uint to_index,const uint count,const bool to_down=true)
{
//--- Смещение строк по направлению вниз
if(to_down)
{
for(uint i=from_index; i>to_index; i--)
{
//--- Индекс предыдущего элемента массива строк
uint prev_index=i-count;
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[prev_index].m_symbol);
//--- Установим новый размер массивам
ArraysResize(i,symbols_total);
//--- Сделать копию строки
LineCopy(i,prev_index);
//--- Если это последняя итерация
if(prev_index==to_index)
{
//--- Выйти, если это первая строка
if(to_index<1)
break;
}
}
}
//--- Смещение строк по направлению вверх
else
{
for(uint i=from_index; i<to_index; i++)
{
//--- Индекс следующего элемента массива строк
uint next_index=i+count;
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[next_index].m_symbol);
//--- Установим новый размер массивам
ArraysResize(i,symbols_total);
//--- Сделать копию строки
LineCopy(i,next_index);
}
}
}
//+------------------------------------------------------------------+
//| Перемещение символов в указанной строке |
//+------------------------------------------------------------------+
void CTextBox::MoveSymbols(const uint line_index,const uint from_pos,const uint to_pos,const bool to_left=true)
{
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[line_index].m_symbol);
//--- Разница
uint offset=from_pos-to_pos;
//--- Если нужно сместить символы влево
if(to_left)
{
for(uint s=to_pos; s<symbols_total-offset; s++)
{
uint i=s+offset;
m_lines[line_index].m_symbol[s] =m_lines[line_index].m_symbol[i];
m_lines[line_index].m_width[s] =m_lines[line_index].m_width[i];
}
}
//--- Если нужно сместить символы вправо
else
{
for(uint s=symbols_total-1; s>to_pos; s--)
{
uint i=s-1;
m_lines[line_index].m_symbol[s] =m_lines[line_index].m_symbol[i];
m_lines[line_index].m_width[s] =m_lines[line_index].m_width[i];
}
}
}
//+------------------------------------------------------------------+
//| Добавляет текст в указанную строку |
//+------------------------------------------------------------------+
void CTextBox::AddToString(const uint line_index,const string text)
{
//--- Переносим строку в массив
uchar array[];
int total=::StringToCharArray(text,array)-1;
if(total<0)
return;
//--- Получим размер массива символов
uint symbols_total=::ArraySize(m_lines[line_index].m_symbol);
//--- Установим новый размер массивам
uint new_size=symbols_total+total;
ArraysResize(line_index,new_size);
//--- Добавить данные в массивы структуры
for(uint i=symbols_total; i<new_size; i++)
{
m_lines[line_index].m_symbol[i] =::CharToString(array[i-symbols_total]);
m_lines[line_index].m_width[i] =m_textbox.TextWidth(m_lines[line_index].m_symbol[i]);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// Agregado
void CTextBox::InsertToString(const uint line_index, const uint start_pos, const string text)
{
//---
uchar array[];
int total=::StringToCharArray(text,array)-1;
if(total<0)
return;
//---
uint symbols_total=::ArraySize(m_lines[line_index].m_symbol);
//---
uint new_size=symbols_total+total;
ArraysResize(line_index,new_size);
//---
for(int i = (int)new_size - 1; i >= (int)(start_pos + total); i--)
{
uint source_index = i - total;
m_lines[line_index].m_symbol[i] = m_lines[line_index].m_symbol[source_index];
m_lines[line_index].m_width[i] = m_lines[line_index].m_width[source_index];
}
//---
for(uint i = 0; i < uint(total); i++)
{
uint target_index = start_pos + i;
m_lines[line_index].m_symbol[target_index] = ::CharToString(array[i]);
m_lines[line_index].m_width[target_index] = m_textbox.TextWidth(m_lines[line_index].m_symbol[target_index]);
}
}
//+------------------------------------------------------------------+
//| Копирует в переданный массив символы для переноса |
//+------------------------------------------------------------------+
void CTextBox::CopyWrapSymbols(const uint line_index,const uint start_pos,const uint symbols_total,string &array[])
{
//--- Установим размер массиву
::ArrayResize(array,symbols_total);
//--- Скопируем в массив символы, которые нужно перенести
for(uint i=0; i<symbols_total; i++)
array[i]=m_lines[line_index].m_symbol[start_pos+i];
}
//+------------------------------------------------------------------+
//| Вставляет символы в указанную строку |
//+------------------------------------------------------------------+
void CTextBox::PasteWrapSymbols(const uint line_index,const uint start_pos,string &array[])
{
uint array_size=::ArraySize(array);
//--- Добавить данные в массивы структуры новой строки
for(uint i=0; i<array_size; i++)
{
uint s=start_pos+i;
m_lines[line_index].m_symbol[s] =array[i];
m_lines[line_index].m_width[s] =m_textbox.TextWidth(array[i]);
}
}
//+------------------------------------------------------------------+
//| Перенос текста на новую строку |
//+------------------------------------------------------------------+
void CTextBox::WrapTextToNewLine(const uint line_index,const uint symbol_index,const bool by_pressed_enter=false)
{
//--- Получим размер массива символов из строки
uint symbols_total=::ArraySize(m_lines[line_index].m_symbol);
//--- Последний индекс символа
uint last_symbol_index=symbols_total-1;
//--- Корректировка в случае пустой строки
uint check_symbol_index=(symbol_index>last_symbol_index && symbol_index!=symbols_total)? last_symbol_index : symbol_index;
//--- Индекс следующей строки
uint next_line_index=line_index+1;
//--- Количество символов, которые нужно перенести на новую строку
uint new_line_size=symbols_total-check_symbol_index;
//--- Скопируем в массив символы, которые нужно перенести
string array[];
CopyWrapSymbols(line_index,check_symbol_index,new_line_size,array);
//--- Установим новый размер массивам структуры в строке
ArraysResize(line_index,symbols_total-new_line_size);
//--- Установим новый размер массивам структуры в новой строке
ArraysResize(next_line_index,new_line_size);
//--- Добавить данные в массивы структуры новой строки
PasteWrapSymbols(next_line_index,0,array);
//--- Определим новое положение текстового курсора
int x_pos=int(new_line_size-(symbols_total-m_text_cursor_x_pos));
m_text_cursor_x_pos =(x_pos<0)? (int)m_text_cursor_x_pos : x_pos;
m_text_cursor_y_pos =(x_pos<0)? (int)line_index : (int)next_line_index;
//--- Если указано, что вызов по нажатию на клавише Enter
if(by_pressed_enter)
{
//--- Если строка имела признак окончания, то ставим признак окончания текущей и следующей
if(m_lines[line_index].m_end_of_line)
{
m_lines[line_index].m_end_of_line =true;
m_lines[next_line_index].m_end_of_line =true;
}
//--- Если нет, то только текущей
else
{
m_lines[line_index].m_end_of_line =true;
m_lines[next_line_index].m_end_of_line =false;
}
}
else
{
//--- Если строка имела признак окончания, то продолжаем и устанавливаем признак на следующей строке
if(m_lines[line_index].m_end_of_line)
{
m_lines[line_index].m_end_of_line =false;
m_lines[next_line_index].m_end_of_line =true;
}
//--- Если строка не имела признак окончания, то продолжаем в обеих строках
else
{
m_lines[line_index].m_end_of_line =false;
m_lines[next_line_index].m_end_of_line =false;
}
}
}
//+------------------------------------------------------------------+
//| Перенос текста из следующей строки в текущую |
//+------------------------------------------------------------------+
void CTextBox::WrapTextToPrevLine(const uint next_line_index,const uint wrap_symbols_total,const bool is_all_text=false)
{
//--- Получим размер массива символов из строки
uint symbols_total=::ArraySize(m_lines[next_line_index].m_symbol);
//--- Индекс предыдущей строки
uint prev_line_index=next_line_index-1;
//--- Скопируем в массив символы, которые нужно перенести
string array[];
CopyWrapSymbols(next_line_index,0,wrap_symbols_total,array);
//--- Получим размер массива символов из предыдущей строки
uint prev_line_symbols_total=::ArraySize(m_lines[prev_line_index].m_symbol);
//--- Увеличим размер массива предыдущей строки на добавляемое количество символов
uint new_prev_line_size=prev_line_symbols_total+wrap_symbols_total;
ArraysResize(prev_line_index,new_prev_line_size);
//--- Добавить данные в массивы структуры новой строки
PasteWrapSymbols(prev_line_index,new_prev_line_size-wrap_symbols_total,array);
//--- Сместим символы на освободившееся место в текущей строке
MoveSymbols(next_line_index,wrap_symbols_total,0);
//--- Уменьшим размер массива текущей строки на извлечённое из неё количество символов
ArraysResize(next_line_index,symbols_total-wrap_symbols_total);
//--- Скорректировать текстовый курсор
if((is_all_text && next_line_index==m_text_cursor_y_pos) ||
(!is_all_text && next_line_index==m_text_cursor_y_pos && wrap_symbols_total>0))
{
m_text_cursor_x_pos=new_prev_line_size-(wrap_symbols_total-m_text_cursor_x_pos);
m_text_cursor_y_pos--;
}
//--- Выйти, если это не весь оставшийся текст строки
if(!is_all_text)
return;
//--- Добавить признак оконачания для предыдущей строки, если у текущей строки он тоже есть
if(m_lines[next_line_index].m_end_of_line)
m_lines[next_line_index-1].m_end_of_line=true;
//--- Получим размер массива строк
uint lines_total=::ArraySize(m_lines);
//--- Сместим строки на одну вверх
MoveLines(next_line_index,lines_total-1,1,false);
//--- Установим новый размер массиву строк
::ArrayResize(m_lines,lines_total-1);
}
//+------------------------------------------------------------------+
//| Изменить ширину по правому краю формы |
//+------------------------------------------------------------------+
void CTextBox::ChangeWidthByRightWindowSide(void)
{
//--- Выйти, если включен режим фиксации к правому краю формы
if(m_anchor_right_window_side)
return;
//--- Координаты
int x=0;
//--- Размеры
int x_size =m_main.X2()-CElementBase::X()-m_auto_xresize_right_offset;
int y_size =(m_auto_yresize_mode)? m_main.Y2()-CElementBase::Y()-m_auto_yresize_bottom_offset : m_y_size;
//--- Установить новый размер
ChangeMainSize(x_size,y_size);
//--- Рассчитать размеры поля ввода
CalculateTextBoxSize();
//--- Установить новый размер полю ввода
ChangeTextBoxSize();
//--- В режиме переноса слов нужно повторно пересчитать и установить размеры
if(m_word_wrap_mode)
{
CalculateTextBoxSize();
ChangeTextBoxSize();
}
//--- Нарисовать текст и дезактивировать поле ввода
DeactivateTextBox();
//--- Перерисовать элемент
Draw();
if(m_scrollh.IsScroll())
m_scrollh.Update(true);
if(m_scrollv.IsScroll())
m_scrollv.Update(true);
}
//+------------------------------------------------------------------+
//| Изменить высоту по нижнему краю окна |
//+------------------------------------------------------------------+
void CTextBox::ChangeHeightByBottomWindowSide(void)
{
//--- Выйти, если включен режим фиксации к нижнему краю формы
if(m_anchor_bottom_window_side)
return;
//--- Координаты
int y=0;
//--- Размеры
int x_size =(m_auto_xresize_mode)? m_main.X2()-CElementBase::X()-m_auto_xresize_right_offset : m_x_size;
int y_size =m_main.Y2()-CElementBase::Y()-m_auto_yresize_bottom_offset;
//--- Установить новый размер
ChangeMainSize(x_size,y_size);
//--- Рассчитать размеры поля ввода
CalculateTextBoxSize();
//--- Установить новый размер полю ввода
ChangeTextBoxSize();
//--- Нарисовать текст и дезактивировать поле ввода
DeactivateTextBox();
//--- Перерисовать элемент
Draw();
if(m_scrollh.IsScroll())
m_scrollh.Update(true);
if(m_scrollv.IsScroll())
m_scrollv.Update(true);
}
//+------------------------------------------------------------------+