//+------------------------------------------------------------------+ //| ListView.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "..\Element.mqh" #include "Scrolls.mqh" //+------------------------------------------------------------------+ //| Класс для создания списка | //+------------------------------------------------------------------+ class CListView : public CElement { private: //--- Объекты для создания списка CRectCanvas m_listview; CScrollV m_scrollv; //--- Массив свойств пунктов списка struct LVItemOptions { int m_y; // Y-координата верхнего края строки int m_y2; // Y-координата нижнего края строки string m_value; // Текст пункта bool m_state; // Состояние чек-бокса }; LVItemOptions m_items[]; //--- Размер списка и его видимой части int m_items_total; //--- Общий размер и размер видимой части списка int m_list_y_size; int m_list_visible_y_size; //--- Общее смещение списка int m_y_offset; //--- Размер пунктов по оси Y int m_item_y_size; //--- (1) Индекс и (2) текст выбранного пункта int m_selected_item; string m_selected_item_text; //--- Индекс предыдущего выделенного пункта int m_prev_selected_item; //--- Для определения фокуса строки int m_item_index_focus; //--- Для определение момента перехода курсора мыши с одной строки на другую int m_prev_item_index_focus; //--- Режим списка с чек-боксами bool m_checkbox_mode; //--- Для расчёта границ видимой части поля ввода int m_y_limit; int m_y2_limit; //--- Режим подсветки при наведении курсора bool m_lights_hover; //--- Счётчик таймера для перемотки списка int m_timer_counter; //--- Для определения индексов видимой части списка int m_visible_list_from_index; int m_visible_list_to_index; //--- public: CListView(void); ~CListView(void); //--- Методы для создания списка bool CreateListView(const int x_gap,const int y_gap); //--- private: void InitializeProperties(const int x_gap,const int y_gap); bool CreateCanvas(void); bool CreateList(void); bool CreateScrollV(void); //--- public: //--- Возвращает указатель на полосу прокрутки CScrollV *GetScrollVPointer(void) { return(::GetPointer(m_scrollv)); } //--- (1) Высота пункта, возвращает (2) размер списка и (3) видимой его части void ItemYSize(const int y_size) { m_item_y_size=y_size; } int ItemsTotal(void) const { return(::ArraySize(m_items)); } int VisibleItemsTotal(void); //--- (1) Состояние полосы прокрутки, (2) режим подсветки пунктов при наведении, (3) режим списка с чек-боксами bool ScrollState(void) const { return(m_scrollv.State()); } void LightsHover(const bool state) { m_lights_hover=state; } void CheckBoxMode(const bool state) { m_checkbox_mode=state; } //--- Возвращает (1) индекс и (2) текст выделенного пункта в списке int SelectedItemIndex(void) const { return(m_selected_item); } string SelectedItemText(void) const { return(m_selected_item_text); } //--- Установка ярлыков для кнопки в нажатом состоянии (доступен/заблокирован) void IconFilePressed(const string file_path); void IconFilePressedLocked(const string file_path); //--- (1) Установка значения, (2) получение значения, (3) получение состояния void SetValue(const uint item_index,const string value,const bool redraw=false); string GetValue(const uint item_index); bool GetState(const uint item_index); //--- Выделение пункта void SelectItem(const uint item_index,const bool redraw=false); //--- Установка (1) размера списка и (2) видимой его части void ListSize(const int items_total); //--- Реконструкция списка void Rebuilding(const int items_total,const bool redraw=false); //--- Добавляет пункт в список void AddItem(const int item_index,const string value="",const bool redraw=false); //--- Удаляет пункт из списка void DeleteItem(const int item_index,const bool redraw=false); //--- Очищает список (удаление всех пунктов) void Clear(const bool redraw=false); //--- Прокрутка списка void Scrolling(const int pos=WRONG_VALUE); //--- Изменение размеров 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 OnClickList(const string pressed_object); //--- Возвращает индекс пункта, на котором нажали int PressedItemIndex(void); //--- Изменение цвета пунктов списка при наведении void ChangeItemsColor(void); //--- Проверка фокуса строки списка при наведении курсора int CheckItemFocus(void); //--- Смещение списка относительно позиции полосы прокрутки void ShiftData(void); //--- Ускоренная перемотка списка void FastSwitching(void); //--- Рассчитывает размер списка void CalculateListYSize(void); //--- Изменить основные размеры элемента void ChangeMainSize(const int x_size,const int y_size); //--- Изменить размеры списка void ChangeListSize(void); //--- Изменить размеры полос прокрутки void ChangeScrollsSize(void); //--- Расчёт с учётом последних изменений и изменение размеров списка void RecalculateAndResizeList(const bool redraw=false); //--- Инициализация указанного пункта значениями по умолчанию void ItemInitialize(const uint item_index); //--- Делает копию указанного пункта (source) в новое место (dest.) void ItemCopy(const uint item_dest,const uint item_source); //--- Расчёт Y-координаты пункта int CalculationItemY(const int item_index=0); //--- Расчёт ширины пунктов int CalculationItemsWidth(void); //--- Расчёт границ поля ввода по оси Y void CalculateYBoundaries(void); //--- Корректировка вертикальной полосы прокрутки void CorrectingVerticalScrollThumb(void); //--- Расчёт Y-позиции ползунка полосы прокрутки int CalculateScrollPosY(const bool to_down=false); //--- Расчёт Y-координат полосы прокрутки в верхней/нижней границе списка int CalculateScrollThumbY(void); int CalculateScrollThumbY2(void); //--- Определение индексов видимой области списка void VisibleListIndexes(void); //--- Рисует список virtual void DrawList(const bool only_visible=false); //--- Рисует рамку virtual void DrawBorder(void); //--- Рисует картинки пунктов virtual void DrawImages(void); //--- Рисует картинку virtual void DrawImage(void); //--- Рисует текст пунктов virtual void DrawText(void); //--- Перерисовывает указанный пункт списка void RedrawItem(const int item_index); //--- Перерисовывает пункты списка по указанному режиму void RedrawItemsByMode(const bool is_selected_row=false); //--- Возвращает текущий цвет фона пункта uint ItemColorCurrent(const int item_index,const bool is_item_focus); //--- Возвращает цвет текста пункта uint TextColor(const int item_index); //--- Изменить ширину по правому краю окна virtual void ChangeWidthByRightWindowSide(void); //--- Изменить высоту по нижнему краю окна virtual void ChangeHeightByBottomWindowSide(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CListView::CListView(void) : m_item_y_size(18), m_lights_hover(false), m_items_total(0), m_y_offset(0), m_checkbox_mode(false), m_selected_item(WRONG_VALUE), m_selected_item_text(""), m_prev_selected_item(WRONG_VALUE), m_item_index_focus(WRONG_VALUE), m_prev_item_index_focus(WRONG_VALUE), m_visible_list_from_index(WRONG_VALUE), m_visible_list_to_index(WRONG_VALUE) { //--- Сохраним имя класса элемента в базовом классе CElementBase::ClassName(CLASS_NAME); //--- Установим размер списка и его видимой части ListSize(m_items_total); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CListView::~CListView(void) { } //+------------------------------------------------------------------+ //| Обработчик событий | //+------------------------------------------------------------------+ void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Обработка события перемещения курсора if(id==CHARTEVENT_MOUSE_MOVE) { //--- Смещаем список, если управление ползунком полосы прокрутки в действии if(m_scrollv.ScrollBarControl()) { //--- Обновить список и полосу прокрутки ShiftData(); m_scrollv.Update(true); return; } //--- Сброс цвета if(!CElementBase::MouseFocus()) { if(m_prev_item_index_focus==WRONG_VALUE) return; //--- Снять фокус m_canvas.MouseFocus(false); //--- Изменяет цвет строк списка при наведении ChangeItemsColor(); return; } //--- Проверка фокуса над списком int x_offset=(m_scrollv.IsVisible())? m_scrollv.ScrollWidth() : 0; m_canvas.MouseFocus(m_mouse.X()>m_canvas.X() && m_mouse.X()m_canvas.Y() && m_mouse.Y()0) Scrolling(pos-1); //--- Обновить полосу прокрутки m_scrollv.Update(true); } //--- Обработка нажатия на объектах if(id==CHARTEVENT_OBJECT_CLICK) { //--- Если было нажатие на списке if(OnClickList(sparam)) 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; } } } //+------------------------------------------------------------------+ //| Таймер | //+------------------------------------------------------------------+ void CListView::OnEventTimer(void) { //--- Ускоренная перемотка значений FastSwitching(); } //+------------------------------------------------------------------+ //| Создаёт список | //+------------------------------------------------------------------+ bool CListView::CreateListView(const int x_gap,const int y_gap) { //--- Выйти, если нет указателя на главный элемент if(!CElement::CheckMainPointer()) return(false); //--- Инициализация свойств InitializeProperties(x_gap,y_gap); //--- Рассчитаем размеры списка CalculateListYSize(); //--- Создание списка if(!CreateCanvas()) return(false); if(!CreateList()) return(false); if(!CreateScrollV()) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Инициализация свойств | //+------------------------------------------------------------------+ void CListView::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_selected_item =(m_selected_item==WRONG_VALUE && !m_checkbox_mode) ? 0 : m_selected_item; //--- Цвета пунктов в различных состояниях m_back_color =(m_back_color!=clrNONE)? m_back_color : clrWhite; m_back_color_hover =(m_back_color_hover!=clrNONE)? m_back_color_hover : C'229,243,255'; m_back_color_pressed =(m_back_color_pressed!=clrNONE)? m_back_color_pressed : C'51,153,255'; m_label_color =(m_label_color!=clrNONE)? m_label_color : clrBlack; m_label_color_hover =(m_label_color_hover!=clrNONE)? m_label_color_hover : clrBlack; m_label_color_pressed =(m_label_color_pressed!=clrNONE)? m_label_color_pressed : clrWhite; m_border_color =(m_border_color!=clrNONE)? m_border_color : C'150,170,180'; //--- Отступы для картинок и текста от краёв ячеек m_icon_x_gap =(m_icon_x_gap>0)? m_icon_x_gap : 7; m_icon_y_gap =(m_icon_y_gap>0)? m_icon_y_gap : 4; m_label_x_gap =(m_label_x_gap>0)? m_label_x_gap : 5; m_label_y_gap =(m_label_y_gap>0)? m_label_y_gap : 4; //--- Отступы от крайней точки CElementBase::XGap(x_gap); CElementBase::YGap(y_gap); } //+------------------------------------------------------------------+ //| Создаёт фон для списка | //+------------------------------------------------------------------+ bool CListView::CreateCanvas(void) { //--- Формирование имени объекта string name=CElementBase::ElementName("listview"); //--- Создание объекта if(!CElement::CreateCanvas(name,m_x,m_y,m_x_size,m_y_size)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Создаёт объект для рисования | //+------------------------------------------------------------------+ bool CListView::CreateList(void) { //--- Формирование имени объекта string name=CElementBase::ProgramName()+"_"+"listview_array"+"_"+(string)CElementBase::Id(); //--- Размер int x_size=m_x_size-2; //--- Координаты int x =m_x+1; int y =m_y+1; //--- Создание объекта ::ResetLastError(); if(!m_listview.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,x_size,m_list_y_size,COLOR_FORMAT_ARGB_NORMALIZE)) { ::Print(__FUNCTION__," > Не удалось создать холст для рисования списка: ",::GetLastError()); return(false); } //--- Прикрепить к графику if(!m_listview.Attach(m_chart_id,name,COLOR_FORMAT_ARGB_NORMALIZE)) { ::Print(__FUNCTION__," > Не удалось присоединить холст для рисования к графику: ",::GetLastError()); return(false); } //--- Свойства ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_ZORDER,m_zorder+1); ::ObjectSetString(m_chart_id,m_listview.ChartObjectName(),OBJPROP_TOOLTIP,"\n"); //--- Если нужен список с чек-боксами if(m_checkbox_mode) { IconFile(RESOURCE_CHECKBOX_OFF); IconFileLocked(RESOURCE_CHECKBOX_OFF_LOCKED); CElement::IconFilePressed(RESOURCE_CHECKBOX_ON); CElement::IconFilePressedLocked(RESOURCE_CHECKBOX_ON_LOCKED); } //--- Координаты m_listview.X(x); m_listview.Y(y); //--- Сохраним размеры m_listview.XSize(x_size); m_listview.YSize(m_list_y_size); m_listview.Resize(x_size,m_list_y_size); //--- Отступы от крайней точки панели m_listview.XGap(CElement::CalculateXGap(x)); m_listview.YGap(CElement::CalculateYGap(y)); //--- Установим размеры видимой области ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_XSIZE,x_size); ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_YSIZE,m_list_visible_y_size); //--- Зададим смещение фрейма внутри изображения по осям X и Y ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_XOFFSET,0); ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_YOFFSET,0); return(true); } //+------------------------------------------------------------------+ //| Создаёт вертикальный скролл | //+------------------------------------------------------------------+ bool CListView::CreateScrollV(void) { //--- Сохранить указатель на главный элемент m_scrollv.MainPointer(this); //--- Координаты int x=16,y=1; //--- Свойства m_scrollv.Index((m_scrollv.Index()!=WRONG_VALUE)? m_scrollv.Index() : 0); m_scrollv.XSize(15); m_scrollv.YSize(CElementBase::YSize()-2); m_scrollv.IsDropdown(CElementBase::IsDropdown()); m_scrollv.AnchorRightWindowSide(true); //--- Расчёт количества шагов для смещения uint items_total =ItemsTotal(); uint visible_items_total =VisibleItemsTotal(); //--- Создание полосы прокрутки if(!m_scrollv.CreateScroll(x,y,items_total,visible_items_total)) return(false); //--- Скрыть полосу прокрутки, если видимая часть больше, чем размер списка if(m_list_visible_y_size>m_list_y_size) m_scrollv.Hide(); //--- Добавить элемент в массив CElement::AddToArray(m_scrollv); return(true); } //+------------------------------------------------------------------+ //| Выделяет указанный пункт | //+------------------------------------------------------------------+ void CListView::SelectItem(const uint item_index,const bool redraw=false) { //--- Выйти, если нет пунктов в списке if(ItemsTotal()<1) return; //--- Корректировка в случае выхода из диапазона int checked_index=(item_index>=(uint)m_items_total)? m_items_total-1 :(int)item_index; //--- Если это список с чек-боксами if(m_checkbox_mode) { m_selected_item =WRONG_VALUE; m_selected_item_text =""; //--- Установим противоположное значение чек-боксу m_items[checked_index].m_state=!m_items[checked_index].m_state; //--- Перерисовать пункт, если указано if(redraw) RedrawItem(item_index); //--- return; } //--- Сохраним индекс и текст выделенного пункта m_selected_item =checked_index; m_selected_item_text =m_items[m_selected_item].m_value; //--- Перерисовать список, если указано if(redraw) Update(true); } //+------------------------------------------------------------------+ //| Возвращает количество видимых пунктов | //+------------------------------------------------------------------+ int CListView::VisibleItemsTotal(void) { double visible_items_total =m_list_visible_y_size/m_item_y_size; double check_y_size =visible_items_total*m_item_y_size; //--- visible_items_total=(check_y_size Вызов этого метода нужно осуществлять, когда в списке есть хотя бы один пункт!"); //--- Корректировка в случае выхода из диапазона uint i=(item_index>=array_size)? array_size-1 : item_index; //--- Сохраним значение в списке m_items[i].m_value=value; //--- Перерисовать пункт, если указано if(redraw) RedrawItem(item_index); } //+------------------------------------------------------------------+ //| Получение значения в списке по указанному индексу | //+------------------------------------------------------------------+ string CListView::GetValue(const uint item_index) { uint array_size=ItemsTotal(); //--- Если нет ни одного пункта в списке, сообщить об этом if(array_size<1) ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, когда в списке есть хотя бы один пункт!"); //--- Корректировка в случае выхода из диапазона uint i=(item_index>=array_size)? array_size-1 : item_index; //--- Сохраним значение в списке return(m_items[i].m_value); } //+------------------------------------------------------------------+ //| Получение состояния чек-бокса по указанному индексу | //+------------------------------------------------------------------+ bool CListView::GetState(const uint item_index) { uint array_size=ItemsTotal(); //--- Если нет ни одного пункта в списке, сообщить об этом if(array_size<1) ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, когда в списке есть хотя бы один пункт!"); //--- Корректировка в случае выхода из диапазона uint i=(item_index>=array_size)? array_size-1 : item_index; //--- Сохраним значение в списке return(m_items[i].m_state); } //+------------------------------------------------------------------+ //| Устанавливает размер списка | //+------------------------------------------------------------------+ void CListView::ListSize(const int items_total) { //--- Не имеет смысла делать список менее двух пунктов m_items_total=(items_total<1) ? 0 : items_total; ::ArrayResize(m_items,m_items_total); //--- Инициализация пунктов значениями по умолчанию for(int i=0; i=m_items_total)? m_items_total-1 : item_index; //--- Сместить другие пункты (двигаемся от конца массива к индексу добавляемого пункта) for(int i=array_size; i>=checked_item_index; i--) { //--- Инициализация нового пункта значениями по умолчанию if(i==checked_item_index) { ItemInitialize(i); m_items[i].m_value=value; continue; } //--- Индекс предыдущего пункта uint prev_i=i-1; //--- Перемещаем данные из предыдущего пункта в текущий ItemCopy(i,prev_i); } //--- Рассчитать и установить новые размеры списка RecalculateAndResizeList(redraw); } //+------------------------------------------------------------------+ //| Удаляет пункт из списка | //+------------------------------------------------------------------+ void CListView::DeleteItem(const int item_index,const bool redraw=false) { //--- Увеличим размер массива на один элемент int array_size=ItemsTotal(); //--- Корректировка индекса в случае выхода из диапазона int checked_item_index=(item_index>=m_items_total)? m_items_total-1 : item_index; //--- Сместить другие пункты (двигаемся от указанного индекса к последнему пункту) for(int i=checked_item_index; ilast_pos_index)? last_pos_index : pos; //--- Сдвигаем ползунок полосы прокрутки m_scrollv.MovingThumb(index); //--- Сдвигаем список ShiftData(); } //+------------------------------------------------------------------+ //| Изменение размеров | //+------------------------------------------------------------------+ void CListView::ChangeSize(const uint x_size,const uint 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 CListView::ChangeItemsColor(void) { //--- Выйдем, если отключена подсветка при наведении курсора или прокрутка списка активна if(!m_lights_hover || m_scrollv.State()) return; //--- Выйдем, если элемент не выпадающий и форма заблокирована if(!CElementBase::IsDropdown() && m_main.CElementBase::IsLocked()) return; //--- Если не в фокусе if(!m_canvas.MouseFocus()) { //--- Если ещё не отмечено, что не в фокусе if(m_prev_item_index_focus!=WRONG_VALUE) { m_item_index_focus=WRONG_VALUE; //--- Изменить цвет RedrawItemsByMode(); //--- Сбросить фокус m_prev_item_index_focus=WRONG_VALUE; } } //--- Если в фокусе else { //--- Проверить фокус над строками if(m_item_index_focus==WRONG_VALUE) { //--- Получим индекс строки в фокусе m_item_index_focus=CheckItemFocus(); //--- Изменить цвет строки RedrawItemsByMode(); //--- Сохранить как предыдущий индекс в фокусе m_prev_item_index_focus=m_item_index_focus; return; } //--- Получим относительную Y-координату под курсором мыши int y=m_mouse.RelativeY(m_listview); //--- Проверка фокуса bool condition=(y>m_items[m_item_index_focus].m_y && y<=m_items[m_item_index_focus].m_y2); //--- Если фокус изменился if(!condition) { //--- Получим индекс строки в фокусе m_item_index_focus=CheckItemFocus(); //--- Выйти, если вышли из области списка if(m_item_index_focus<0) return; //--- Изменить цвет строки RedrawItemsByMode(); //--- Сохранить как предыдущий индекс в фокусе m_prev_item_index_focus=m_item_index_focus; } } } //+------------------------------------------------------------------+ //| Проверка фокуса строки списка при наведении курсора | //+------------------------------------------------------------------+ int CListView::CheckItemFocus(void) { int item_index_focus=WRONG_VALUE; //--- Получим относительную Y-координату под курсором мыши int y=m_mouse.RelativeY(m_listview); ///--- Получим индексы локальной области таблицы VisibleListIndexes(); //--- Ищем фокус for(int i=m_visible_list_from_index; im_items[i].m_y && y<=m_items[i].m_y2) { item_index_focus=(int)i; break; } } //--- Вернём индекс строки в фокусе return(item_index_focus); } //+------------------------------------------------------------------+ //| Смещение списка относительно позиции полосы прокрутки | //+------------------------------------------------------------------+ void CListView::ShiftData(void) { //--- Сохраним ограничение по смещению int shift_y2_limit=m_list_y_size-m_list_visible_y_size; //--- Получим текущую позицию ползунка полосы прокрутки int v_offset=(m_scrollv.CurrentPos()*m_item_y_size); //--- Рассчитаем отступы для смещения int y_offset=(v_offset=shift_y2_limit-(m_y_offset*2+1))? shift_y2_limit : v_offset; //--- Первая позиция, если полосы прокрутки нет long y=(!m_scrollv.IsVisible())? 0 : y_offset; //--- Смещение данных ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_YOFFSET,y); } //+------------------------------------------------------------------+ //| Перемещение элемента | //+------------------------------------------------------------------+ void CListView::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_listview.X(m_main.X2()-m_listview.XGap()); } else { CElementBase::X(m_main.X()+XGap()); m_listview.X(m_main.X()+m_listview.XGap()); } //--- Если привязка снизу if(m_anchor_bottom_window_side) { CElementBase::Y(m_main.Y2()-YGap()); m_listview.Y(m_main.Y2()-m_listview.YGap()); } else { CElementBase::Y(m_main.Y()+YGap()); m_listview.Y(m_main.Y()+m_listview.YGap()); } //--- Обновление координат графических объектов ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_XDISTANCE,m_listview.X()); ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_YDISTANCE,m_listview.Y()); //--- Обновить положение объектов CElement::Moving(only_visible); } //+------------------------------------------------------------------+ //| Показывает список | //+------------------------------------------------------------------+ void CListView::Show(void) { //--- Выйти, если элемент уже видим if(CElementBase::IsVisible()) return; //--- Показать элемент ::ObjectSetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); //--- Состояние видимости CElementBase::IsVisible(true); //--- Обновить положение объектов Moving(); //--- Показать полосу прокрутки if(m_scrollv.IsScroll()) m_scrollv.Show(); } //+------------------------------------------------------------------+ //| Скрывает список | //+------------------------------------------------------------------+ void CListView::Hide(void) { if(!CElementBase::IsVisible()) return; //--- Скрыть элемент ::ObjectSetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS); ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS); //--- Скрыть полосу прокрутки m_scrollv.Hide(); //--- Состояние видимости CElementBase::IsVisible(false); } //+------------------------------------------------------------------+ //| Удаление | //+------------------------------------------------------------------+ void CListView::Delete(void) { CElement::Delete(); m_listview.Destroy(); //--- Освободить массив ::ArrayFree(m_items); } //+------------------------------------------------------------------+ //| Установка приоритетов | //+------------------------------------------------------------------+ void CListView::SetZorders(void) { CElement::SetZorders(); ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_ZORDER,m_zorder+1); } //+------------------------------------------------------------------+ //| Сброс приоритетов | //+------------------------------------------------------------------+ void CListView::ResetZorders(void) { CElement::ResetZorders(); ::ObjectSetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_ZORDER,WRONG_VALUE); } //+------------------------------------------------------------------+ //| Рисует элемент | //+------------------------------------------------------------------+ void CListView::Draw(void) { DrawList(); } //+------------------------------------------------------------------+ //| Обновление элемента | //+------------------------------------------------------------------+ void CListView::Update(const bool redraw=false) { //--- Перерисовать таблицу, если указано if(redraw) { //--- Рассчитать размеры CalculateListYSize(); //--- Установить новый размер ChangeListSize(); //--- Нарисовать Draw(); //--- Применить m_canvas.Update(); m_listview.Update(); return; } //--- Применить m_canvas.Update(); m_listview.Update(); } //+------------------------------------------------------------------+ //| Обработка нажатия на пункте списка | //+------------------------------------------------------------------+ bool CListView::OnClickList(const string pressed_object) { //--- Выйти, если (1) список не в фокусе или (2) полоса прокрутки в активном режиме if(!CElementBase::MouseFocus() || m_scrollv.State()) return(false); //--- Выйдем, если (1) чужое имя объекта или (2) список чист if(m_listview.ChartObjectName()!=pressed_object || ItemsTotal()<1) return(false); //--- Определим пункт, на котором нажали int index=PressedItemIndex(); //--- Если список без чек-боксов if(!m_checkbox_mode) { //--- Скорректируем вертикальную полосу прокрутки CorrectingVerticalScrollThumb(); //--- Изменить цвет пункта RedrawItemsByMode(true); } else { //--- Изменим состояние чек-бокса на противоположное m_items[index].m_state=!m_items[index].m_state; //--- Обновить список RedrawItem(index); } //--- Отправим сообщение об этом ::EventChartCustom(m_chart_id,ON_CLICK_LIST_ITEM,CElementBase::Id(),m_selected_item,""); return(true); } //+------------------------------------------------------------------+ //| Возвращает индекс пункта, на котором нажали | //+------------------------------------------------------------------+ int CListView::PressedItemIndex(void) { int index=0; //--- Получим относительную Y-координату под курсором мыши int y=m_mouse.RelativeY(m_listview); //--- Определим ряд, на котором нажали for(int i=0; i=m_items[i].m_y && y<=m_items[i].m_y2)) continue; //--- Запомним индекс index=i; //--- Если список без чек-боксов if(!m_checkbox_mode) { //--- Сохраним индекс ряда и строку первой ячейки m_prev_selected_item =(m_selected_item==WRONG_VALUE)? index : m_selected_item; m_selected_item =index; m_selected_item_text =m_items[index].m_value; } break; } //--- Вернуть индекс return(index); } //+------------------------------------------------------------------+ //| Ускоренная перемотка списка | //+------------------------------------------------------------------+ void CListView::FastSwitching(void) { //--- Выйдем, если нет фокуса на списке if(!CElementBase::MouseFocus()) return; //--- Вернём счётчик к первоначальному значению, если кнопка мыши отжата if(!m_mouse.LeftButtonState()) m_timer_counter=SPIN_DELAY_MSC; //--- Если же кнопка мыши нажата else { //--- Увеличим счётчик на установленный интервал m_timer_counter+=TIMER_STEP_MSC; //--- Выйдем, если меньше нуля if(m_timer_counter<0) return; //--- Флаг перемотки bool scroll_v=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; } //--- Выйти, если кнопки не нажаты if(!scroll_v) return; //--- Обновить список ShiftData(); m_scrollv.Update(true); } } //+------------------------------------------------------------------+ //| Рассчитывает полный размер списка по оси Y | //+------------------------------------------------------------------+ void CListView::CalculateListYSize(void) { //--- Рассчитаем общую высоту таблицы int y_size =(m_item_y_size*m_items_total)+(m_y_offset*2)+1; m_list_y_size =(y_size<=m_y_size)? m_y_size-2 : y_size; //--- Зададим высоту фрейма для показа фрагмента изображения (видимой части таблицы таблицы) m_list_visible_y_size=m_y_size-2; //--- Корректировка размера видимой части по оси Y m_list_visible_y_size=(m_list_visible_y_size>=m_list_y_size)? m_list_y_size : m_list_visible_y_size; //--- Расчёт координат for(int i=0; i=m_y2_limit) { Scrolling(CalculateScrollPosY(true)); } } //+------------------------------------------------------------------+ //| Расчёт Y-позиции ползунка полосы прокрутки | //+------------------------------------------------------------------+ int CListView::CalculateScrollPosY(const bool to_down=false) { int calc_y =(!to_down)? CalculateScrollThumbY() : CalculateScrollThumbY2(); double pos_y_value =(calc_y-::fmod((double)calc_y,(double)m_item_y_size))/m_item_y_size+((!to_down)? 0 : 1); //--- return((int)pos_y_value); } //+------------------------------------------------------------------+ //| Расчёт Y-координаты полосы прокрутки в верхней границе списка | //+------------------------------------------------------------------+ int CListView::CalculateScrollThumbY(void) { return(m_items[m_selected_item].m_y-m_y_offset); } //+------------------------------------------------------------------+ //| Расчёт Y-координаты полосы прокрутки в нижней границе списка | //+------------------------------------------------------------------+ int CListView::CalculateScrollThumbY2(void) { //--- Рассчитать и вернуть значение return(m_items[m_selected_item].m_y-m_y_size+m_item_y_size); } //+------------------------------------------------------------------+ //| Определение индексов видимой области списка | //+------------------------------------------------------------------+ void CListView::VisibleListIndexes(void) { //--- Определяем границы с учётом смещения видимой области таблицы int yoffset1 =(int)::ObjectGetInteger(m_chart_id,m_listview.ChartObjectName(),OBJPROP_YOFFSET); int yoffset2 =yoffset1+m_list_visible_y_size; //--- Определяем первый и последний индексы видимой области таблицы m_visible_list_from_index =int(double(yoffset1/m_item_y_size)); m_visible_list_to_index =int(double(yoffset2/m_item_y_size)); //--- Нижний индекс на один больше, если не выходим из диапазона m_visible_list_to_index=(m_visible_list_to_index+1>m_items_total)? m_items_total : m_visible_list_to_index+1; } //+------------------------------------------------------------------+ //| Рисует список | //+------------------------------------------------------------------+ void CListView::DrawList(const bool only_visible=false) { //--- Если не указано перерисовать только видимую часть списка if(!only_visible) { //--- Установим индексы строк всего списка от самого начала и до конца m_visible_list_from_index =0; m_visible_list_to_index =m_items_total; } //--- Получим индексы строк видимой части списка else VisibleListIndexes(); //--- Нарисовать фон DrawBackground(); m_listview.Erase(::ColorToARGB(m_back_color,m_alpha)); //--- Нарисовать картинки DrawImages(); //--- Нарисовать текст DrawText(); //--- Нарисовать рамку DrawBorder(); } //+------------------------------------------------------------------+ //| Рисует рамку поля ввода | //+------------------------------------------------------------------+ void CListView::DrawBorder(void) { //--- Получим смещение по оси X int x_offset =(int)::ObjectGetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_XOFFSET); int y_offset =(int)::ObjectGetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_YOFFSET); //--- Границы int x_size =(int)::ObjectGetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_XSIZE); int y_size =(int)::ObjectGetInteger(m_chart_id,m_canvas.ChartObjectName(),OBJPROP_YSIZE); //--- Координаты int x1 =x_offset; int y1 =y_offset; int x2 =x_offset+x_size; int y2 =y_offset+y_size; //--- Рисуем прямоугольник без заливки m_canvas.Rectangle(x1,y1,x2-1,y2-1,::ColorToARGB(m_border_color)); } //+------------------------------------------------------------------+ //| Рисует картинки | //+------------------------------------------------------------------+ void CListView::DrawImages(void) { //--- Выйти, если чек-боксы отключены if(!m_checkbox_mode) return; //--- Рисуем чек-боксы в пунктах for(int i=m_visible_list_from_index; im_items[item_index].m_y && y<=m_items[item_index].m_y2); } //--- Нарисовать пункт m_listview.FillRectangle(x1,y1,x2,y2,ItemColorCurrent(item_index,is_item_focus)); //--- Нарисовать рамку m_listview.Rectangle(x1,y1,x2,y2,::ColorToARGB(m_back_color,m_alpha)); //--- Рисуем картинки, если список с чек-боксами if(m_checkbox_mode) { //--- Расчёт координат m_images_group[0].m_y_gap=m_items[item_index].m_y+m_icon_y_gap; //--- Установить соответствующую картинку CElement::ChangeImage(0,(m_items[item_index].m_state)? 2 : 0); CListView::DrawImage(); } //--- Нарисовать текст x1 =m_label_x_gap; y1 =m_items[item_index].m_y+m_label_y_gap; m_listview.TextOut(x1,y1,m_items[item_index].m_value,TextColor(item_index),TA_LEFT|TA_TOP); //--- Обновить холст m_listview.Update(); } //+------------------------------------------------------------------+ //| Перерисовывает пункты списка по указанному режиму | //+------------------------------------------------------------------+ void CListView::RedrawItemsByMode(const bool is_selected_item=false) { //--- Текущий и предыдущий индексы строк int item_index =WRONG_VALUE; int prev_item_index =WRONG_VALUE; //--- Инициализиция индексов строк относительного указанного режима if(is_selected_item) { item_index =m_selected_item; prev_item_index =m_prev_selected_item; } else { item_index =m_item_index_focus; prev_item_index =m_prev_item_index_focus; } //--- Выйти, если индексы не определены if(prev_item_index==WRONG_VALUE && item_index==WRONG_VALUE) return; //--- Количество пунктов для рисования uint items_total=(item_index!=WRONG_VALUE && prev_item_index!=WRONG_VALUE && item_index!=prev_item_index)? 2 : 1; //--- Координаты int x1=0; int x2=CalculationItemsWidth(); int y1[2]={0},y2[2]={0}; //--- Массив для значений в определённой последовательности int indexes[2]; //--- Если (1) курсор мыши сдвинулся вниз или (2) в первый раз здесь if(item_index>m_prev_item_index_focus || item_index==WRONG_VALUE) { indexes[0]=(item_index==WRONG_VALUE || prev_item_index!=WRONG_VALUE)? prev_item_index : item_index; indexes[1]=item_index; } //--- Если курсор мыши сдвинулся вверх else { indexes[0]=item_index; indexes[1]=prev_item_index; } //--- Рисуем фон пунктов for(uint i=0; i