//+------------------------------------------------------------------+ //| Wnd.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "Rect.mqh" #include "Defines.mqh" #include class CDragWnd; //+------------------------------------------------------------------+ //| Class CWnd | //| Usage: base class of the control object that creates | //| control panels and indicator panels | //+------------------------------------------------------------------+ class CWnd : public CObject { protected: //--- parameters of creation long m_chart_id; // chart ID int m_subwin; // chart subwindow string m_name; // object name //--- geometry CRect m_rect; // chart area //--- ID long m_id; // object ID //--- state flags int m_state_flags; //--- properties flags int m_prop_flags; //--- alignment int m_align_flags; // alignment flags int m_align_left; // fixed offset from left border int m_align_top; // fixed offset from top border int m_align_right; // fixed offset from right border int m_align_bottom; // fixed offset from bottom border //--- the last saved state of mouse int m_mouse_x; // X coordinate int m_mouse_y; // Y coordinate int m_mouse_flags; // state of buttons uint m_last_click; // last click time //--- drag object CDragWnd *m_drag_object; // pointer to the dragged object public: CWnd(void); ~CWnd(void); //--- create virtual bool Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2); //--- release memory virtual void Destroy(const int reason=0); //--- chart event handler virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); virtual bool OnMouseEvent(const int x,const int y,const int flags); //--- naming (read only) string Name(void) const { return(m_name); } //--- access the contents of container int ControlsTotal(void) const { return(0); } CWnd* Control(const int ind) const { return(NULL); } virtual CWnd* ControlFind(const long id); //--- geometry const CRect Rect(void) const { return(m_rect); } int Left(void) const { return(m_rect.left); } virtual void Left(const int x) { m_rect.left=x; } int Top(void) const { return(m_rect.top); } virtual void Top(const int y) { m_rect.top=y; } int Right(void) const { return(m_rect.right); } virtual void Right(const int x) { m_rect.right=x; } int Bottom(void) const { return(m_rect.bottom); } virtual void Bottom(const int y) { m_rect.bottom=y; } int Width(void) const { return(m_rect.Width()); } virtual bool Width(const int w); int Height(void) const { return(m_rect.Height()); } virtual bool Height(const int h); CSize Size(void) const { return(m_rect.Size()); } virtual bool Size(const int w,const int h); virtual bool Size(const CSize &size); virtual bool Move(const int x,const int y); virtual bool Move(const CPoint &point); virtual bool Shift(const int dx,const int dy); bool Contains(const int x,const int y) const { return(m_rect.Contains(x,y)); } bool Contains(CWnd *control) const; //--- alignment void Alignment(const int flags,const int left,const int top,const int right,const int bottom); virtual bool Align(const CRect &rect); //--- ID virtual long Id(const long id); long Id(void) const { return(m_id); } //--- state bool IsEnabled(void) const { return(IS_ENABLED); } virtual bool Enable(void); virtual bool Disable(void); bool IsVisible(void) const { return(IS_VISIBLE); } virtual bool Visible(const bool flag); virtual bool Show(void); virtual bool Hide(void); bool IsActive(void) const { return(IS_ACTIVE); } virtual bool Activate(void); virtual bool Deactivate(void); //--- state flags int StateFlags(void) const { return(m_state_flags); } void StateFlags(const int flags) { m_state_flags=flags; } void StateFlagsSet(const int flags) { m_state_flags|=flags; } void StateFlagsReset(const int flags) { m_state_flags&=~flags; } //--- properties flags int PropFlags(void) const { return(m_prop_flags); } void PropFlags(const int flags) { m_prop_flags=flags; } void PropFlagsSet(const int flags) { m_prop_flags|=flags; } void PropFlagsReset(const int flags) { m_prop_flags&=~flags; } //--- for mouse operations int MouseX(void) const { return(m_mouse_x); } void MouseX(const int value) { m_mouse_x=value; } int MouseY(void) const { return(m_mouse_y); } void MouseY(const int value) { m_mouse_y=value; } int MouseFlags(void) const { return(m_mouse_flags); } virtual void MouseFlags(const int value) { m_mouse_flags=value; } bool MouseFocusKill(const long id=CONTROLS_INVALID_ID); bool BringToTop(void); protected: //--- internal event handlers virtual bool OnCreate(void) { return(true); } virtual bool OnDestroy(void) { return(true); } virtual bool OnMove(void) { return(true); } virtual bool OnResize(void) { return(true); } virtual bool OnEnable(void) { return(true); } virtual bool OnDisable(void) { return(true); } virtual bool OnShow(void) { return(true); } virtual bool OnHide(void) { return(true); } virtual bool OnActivate(void) { return(true); } virtual bool OnDeactivate(void) { return(true); } virtual bool OnClick(void); virtual bool OnDblClick(void); virtual bool OnChange(void) { return(true); } //--- mouse event handlers virtual bool OnMouseDown(void); virtual bool OnMouseUp(void); //--- handlers of dragging virtual bool OnDragStart(void); virtual bool OnDragProcess(const int x,const int y); virtual bool OnDragEnd(void); //--- methods for drag-object virtual bool DragObjectCreate(void) { return(false); } virtual bool DragObjectDestroy(void); }; //+------------------------------------------------------------------+ //| Common handler of chart events | //+------------------------------------------------------------------+ bool CWnd::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { if((id!=CHARTEVENT_MOUSE_MOVE)) return(false); if(!IS_VISIBLE) return(false); int x=(int)lparam; int y=(int)dparam; int flags=(int)StringToInteger(sparam); //--- if(m_drag_object!=NULL) return(m_drag_object.OnMouseEvent(x,y,flags)); //--- return(OnMouseEvent(x,y,flags)); } //+------------------------------------------------------------------+ //| Common handler of mouse events | //+------------------------------------------------------------------+ bool CWnd::OnMouseEvent(const int x,const int y,const int flags) { if(!Contains(x,y)) { //--- if cursor is not inside the element and this element is active - deactivate if(IS_ACTIVE) { //--- reset state and coordinates m_mouse_x =0; m_mouse_y =0; m_mouse_flags=MOUSE_INVALID_FLAGS; //--- deactivate Deactivate(); } return(false); } //--- check the state of the left mouse button if((flags&MOUSE_LEFT)!=0) { //--- left mouse button is pressed if(m_mouse_flags==MOUSE_INVALID_FLAGS) { //--- but not in this control (i.e., cursor entered the element with mouse button pressed) //--- activate the control, but there will be no click if(!IS_ACTIVE) { //--- generate event EventChartCustom(CONTROLS_SELF_MESSAGE,ON_MOUSE_FOCUS_SET,m_id,0.0,m_name); //--- activate return(Activate()); } return(true); } if((m_mouse_flags&MOUSE_LEFT)!=0) { //--- mouse button has already been pressed if(IS_CAN_DRAG) return(OnDragProcess(x,y)); if(IS_CLICKS_BY_PRESS) { EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CLICK,m_id,0.0,m_name); //--- handled return(true); } } else { //--- mouse button has been released (pressing) //--- save the state and coordinates m_mouse_flags=flags; m_mouse_x =x; m_mouse_y =y; //--- call the handler return(OnMouseDown()); } } else { //--- left mouse button is released if(m_mouse_flags==MOUSE_INVALID_FLAGS) { //--- cursor entered the control with mouse button released //--- activate control and save state to the member m_mouse_flags=flags; //--- generate event EventChartCustom(CONTROLS_SELF_MESSAGE,ON_MOUSE_FOCUS_SET,m_id,0.0,m_name); //--- activate return(Activate()); } if((m_mouse_flags&MOUSE_LEFT)!=0) { //--- mouse button has been pressed (clicking) //--- save the state and coordinates m_mouse_flags=flags; m_mouse_x =x; m_mouse_y =y; //--- call the handler return(OnMouseUp()); } } //--- handled return(true); } //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CWnd::CWnd(void) : m_chart_id(CONTROLS_INVALID_ID), m_subwin(CONTROLS_INVALID_ID), m_name(NULL), m_id(CONTROLS_INVALID_ID), m_state_flags(WND_STATE_FLAG_ENABLE+WND_STATE_FLAG_VISIBLE), m_prop_flags(0), m_align_flags(WND_ALIGN_NONE), m_align_left(0), m_align_top(0), m_align_right(0), m_align_bottom(0), m_mouse_x(0), m_mouse_y(0), m_mouse_flags(MOUSE_INVALID_FLAGS), m_last_click(0), m_drag_object(NULL) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CWnd::~CWnd(void) { } //+------------------------------------------------------------------+ //| Create a control | //+------------------------------------------------------------------+ bool CWnd::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { //--- attach to chart m_chart_id=chart; m_name =name; m_subwin =subwin; //--- set coordinates of area Left(x1); Top(y1); Right(x2); Bottom(y2); //--- always successful return(true); } //+------------------------------------------------------------------+ //| Destruction of the control | //+------------------------------------------------------------------+ void CWnd::Destroy(const int reason) { //--- call virtual event handler if(OnDestroy()) m_name=""; } //+------------------------------------------------------------------+ //| Find control by specified ID | //+------------------------------------------------------------------+ CWnd* CWnd::ControlFind(const long id) { CWnd *result=NULL; //--- check if(id==m_id) result=GetPointer(this); //--- return the result return(result); } //+------------------------------------------------------------------+ //| Change width of control | //+------------------------------------------------------------------+ bool CWnd::Width(const int w) { //--- change width m_rect.Width(w); //--- call virtual event handler return(OnResize()); } //+------------------------------------------------------------------+ //| Change height of control | //+------------------------------------------------------------------+ bool CWnd::Height(const int h) { //--- change height m_rect.Height(h); //--- call virtual event handler return(OnResize()); } //+------------------------------------------------------------------+ //| Resize control | //+------------------------------------------------------------------+ bool CWnd::Size(const int w,const int h) { //--- change size m_rect.Size(w,h); //--- call virtual event handler return(OnResize()); } //+------------------------------------------------------------------+ //| Resize control | //+------------------------------------------------------------------+ bool CWnd::Size(const CSize &size) { //--- change size m_rect.Size(size); //--- call virtual event handler return(OnResize()); } //+------------------------------------------------------------------+ //| Absolute movement of the control | //+------------------------------------------------------------------+ bool CWnd::Move(const int x,const int y) { //--- moving m_rect.Move(x,y); //--- call virtual event handler return(OnMove()); } //+------------------------------------------------------------------+ //| Absolute movement of the control | //+------------------------------------------------------------------+ bool CWnd::Move(const CPoint &point) { //--- moving m_rect.Move(point); //--- call virtual event handler return(OnMove()); } //+------------------------------------------------------------------+ //| Relative movement of the control | //+------------------------------------------------------------------+ bool CWnd::Shift(const int dx,const int dy) { //--- moving m_rect.Shift(dx,dy); //--- call virtual event handler return(OnMove()); } //+------------------------------------------------------------------+ //| Check contains | //+------------------------------------------------------------------+ bool CWnd::Contains(CWnd *control) const { //--- check if(control==NULL) return(false); //--- result return(Contains(control.Left(),control.Top()) && Contains(control.Right(),control.Bottom())); } //+------------------------------------------------------------------+ //| Enables event handling by the control | //+------------------------------------------------------------------+ bool CWnd::Enable(void) { //--- if there are now changes, then succeed if(IS_ENABLED) return(true); //--- change flag StateFlagsSet(WND_STATE_FLAG_ENABLE); //--- call virtual event handler return(OnEnable()); } //+------------------------------------------------------------------+ //| Disables event handling by the control | //+------------------------------------------------------------------+ bool CWnd::Disable(void) { //--- if there are now changes, then succeed if(!IS_ENABLED) return(true); //--- change flag StateFlagsReset(WND_STATE_FLAG_ENABLE); //--- call virtual event handler return(OnDisable()); } //+------------------------------------------------------------------+ //| Set the "object is visible" flag for the control | //+------------------------------------------------------------------+ bool CWnd::Visible(const bool flag) { //--- if there are now changes, then succeed if(IS_VISIBLE==flag) return(true); //--- call virtual event handler return(flag ? Show() : Hide()); } //+------------------------------------------------------------------+ //| Makes the control visible | //+------------------------------------------------------------------+ bool CWnd::Show(void) { //--- change flag StateFlagsSet(WND_STATE_FLAG_VISIBLE); //--- call virtual event handler return(OnShow()); } //+------------------------------------------------------------------+ //| Makes the control hidden | //+------------------------------------------------------------------+ bool CWnd::Hide(void) { //--- change flag StateFlagsReset(WND_STATE_FLAG_VISIBLE); //--- call virtual event handler return(OnHide()); } //+------------------------------------------------------------------+ //| Makes the control active | //+------------------------------------------------------------------+ bool CWnd::Activate(void) { //--- if there are now changes, then succeed if(IS_ACTIVE) return(true); //--- change flag StateFlagsSet(WND_STATE_FLAG_ACTIVE); //--- call virtual event handler return(OnActivate()); } //+------------------------------------------------------------------+ //| Makes the control inactive | //+------------------------------------------------------------------+ bool CWnd::Deactivate(void) { //--- if there are now changes, then succeed if(!IS_ACTIVE) return(true); //--- change flag StateFlagsReset(WND_STATE_FLAG_ACTIVE); //--- call virtual event handler return(OnDeactivate()); } //+------------------------------------------------------------------+ //| Set ID of control | //+------------------------------------------------------------------+ long CWnd::Id(const long id) { m_id=id; //--- always use only one ID return(1); } //+------------------------------------------------------------------+ //| Set parameters of alignment | //+------------------------------------------------------------------+ void CWnd::Alignment(const int flags,const int left,const int top,const int right,const int bottom) { m_align_flags =flags; m_align_left =left; m_align_top =top; m_align_right =right; m_align_bottom=bottom; } //+------------------------------------------------------------------+ //| Align element in specified chart area | //+------------------------------------------------------------------+ bool CWnd::Align(const CRect &rect) { int new_value=0; //--- check if(m_align_flags==WND_ALIGN_NONE) return(true); //--- we are interested only in alignment by right and bottom borders, //--- as left and right borders are processed in the OnMove() if((m_align_flags&WND_ALIGN_RIGHT)!=0) { //--- there is alignment by right border, if((m_align_flags&WND_ALIGN_LEFT)!=0) { //--- and by left border (change size) new_value=rect.Width()-m_align_left-m_align_right; if(!Size(new_value,Height())) return(false); } else { //--- no alignment by left border (move) new_value=rect.right-Width()-m_align_right; if(!Move(new_value,Top())) return(false); } } if((m_align_flags&WND_ALIGN_BOTTOM)!=0) { //--- there is alignment by bottom border, if((m_align_flags&WND_ALIGN_TOP)!=0) { //--- and by top border (change size) new_value=rect.Height()-m_align_top-m_align_bottom; if(!Size(Width(),new_value)) return(false); } else { //--- no alignment by top border (move) new_value=rect.bottom-Height()-m_align_bottom; if(!Move(Left(),new_value)) return(false); } } //--- succeed return(true); } //+------------------------------------------------------------------+ //| Remove the mouse focus from control | //+------------------------------------------------------------------+ bool CWnd::MouseFocusKill(const long id) { //--- check if(id==m_id) return(false); //--- reset flag Deactivate(); //--- clean m_mouse_x =0; m_mouse_y =0; m_mouse_flags=MOUSE_INVALID_FLAGS; //--- call the handler return(OnDeactivate()); } //+------------------------------------------------------------------+ //| Increases the priority of an element | //+------------------------------------------------------------------+ bool CWnd::BringToTop(void) { //--- generate event EventChartCustom(CONTROLS_SELF_MESSAGE,ON_BRING_TO_TOP,m_id,0.0,m_name); //--- handled return(true); } //+------------------------------------------------------------------+ //| Handler of the "click" event | //+------------------------------------------------------------------+ bool CWnd::OnClick(void) { //--- send notification EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CLICK,m_id,0.0,m_name); //--- handled return(true); } //+------------------------------------------------------------------+ //| Handler of the "doubl click" event | //+------------------------------------------------------------------+ bool CWnd::OnDblClick(void) { //--- send notification EventChartCustom(CONTROLS_SELF_MESSAGE,ON_DBL_CLICK,m_id,0.0,m_name); //--- handled return(true); } //+------------------------------------------------------------------+ //| Handler of click on the left mouse button | //+------------------------------------------------------------------+ bool CWnd::OnMouseDown(void) { if(IS_CAN_DRAG) return(OnDragStart()); if(IS_CLICKS_BY_PRESS) return(OnClick()); //--- handled return(true); } //+------------------------------------------------------------------+ //| Handler of releasing the left mouse button | //+------------------------------------------------------------------+ bool CWnd::OnMouseUp(void) { if(IS_CAN_DBL_CLICK) { uint last_time=GetTickCount(); if(m_last_click==0 || last_time-m_last_click>CONTROLS_DBL_CLICK_TIME) { m_last_click=(last_time==0) ? 1 : last_time; } else { m_last_click=0; return(OnDblClick()); } } if(IS_CAN_DRAG) return(OnDragEnd()); if(!IS_CLICKS_BY_PRESS) return(OnClick()); //--- handled return(true); } //+------------------------------------------------------------------+ //| Handler of the control dragging start | //+------------------------------------------------------------------+ bool CWnd::OnDragStart(void) { if(!IS_CAN_DRAG) return(true); //--- disable scrolling of chart with mouse ChartSetInteger(m_chart_id,CHART_MOUSE_SCROLL,false); //--- generate event EventChartCustom(CONTROLS_SELF_MESSAGE,ON_DRAG_START,m_id,0.0,m_name); //--- handled return(true); } //+------------------------------------------------------------------+ //| Handler of control dragging process | //+------------------------------------------------------------------+ bool CWnd::OnDragProcess(const int x,const int y) { Shift(x-m_mouse_x,y-m_mouse_y); //--- save m_mouse_x=x; m_mouse_y=y; //--- handled return(true); } //+------------------------------------------------------------------+ //| Handler of the control dragging end | //+------------------------------------------------------------------+ bool CWnd::OnDragEnd(void) { if(!IS_CAN_DRAG) return(true); //--- enable scrolling of chart with mouse ChartSetInteger(m_chart_id,CHART_MOUSE_SCROLL,true); //--- generate event EventChartCustom(CONTROLS_SELF_MESSAGE,ON_DRAG_END,m_id,0.0,m_name); //--- handled return(true); } //+------------------------------------------------------------------+ //| Destroy the dragged object | //+------------------------------------------------------------------+ bool CWnd::DragObjectDestroy(void) { if(m_drag_object!=NULL) { delete m_drag_object; m_drag_object=NULL; } //--- succeed return(true); } //+------------------------------------------------------------------+ //| Class CDragWnd | //| Usage: base class for drag | //+------------------------------------------------------------------+ class CDragWnd : public CWnd { protected: int m_limit_left; // left constraint int m_limit_top; // top constraint int m_limit_right; // right constraint int m_limit_bottom; // bottom constraint public: CDragWnd(void); ~CDragWnd(void); //--- constraints void Limits(const int l,const int t,const int r,const int b); protected: virtual bool OnDragProcess(const int x,const int y); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CDragWnd::CDragWnd(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CDragWnd::~CDragWnd(void) { } //+------------------------------------------------------------------+ //| Constrain the control dragging | //+------------------------------------------------------------------+ void CDragWnd::Limits(const int l,const int t,const int r,const int b) { //--- save m_limit_left =l; m_limit_top =t; m_limit_right =r; m_limit_bottom=b; } //+------------------------------------------------------------------+ //| Handler of control dragging process | //+------------------------------------------------------------------+ bool CDragWnd::OnDragProcess(const int x,const int y) { int dx=x-m_mouse_x; int dy=y-m_mouse_y; //--- check shift if(Right()+dx>m_limit_right) dx=m_limit_right-Right(); if(Left()+dxm_limit_bottom) dy=m_limit_bottom-Bottom(); if(Top()+dy