#pragma once // ================================================================================ // Main CListControl implementation // // For ready-to-use CListControl specializations, // see CListControlSimple.h and CListControlOwnerData.h // ================================================================================ #pragma comment(lib, "uxtheme.lib") #include #include #include #include #include #include "CMiddleDragImpl.h" #include "wtl-pp.h" #include "gesture.h" #include "gdiplus_helpers.h" #define CListControl_ScrollWindowFix #ifdef CListControl_ScrollWindowFix #define WS_EX_COMPOSITED_CListControl 0 #else #define WS_EX_COMPOSITED_CListControl WS_EX_COMPOSITED #endif typedef std::function< bool ( UINT, DWORD, CPoint ) > CaptureProc_t; typedef CWinTraits CListControlTraits; class CListControlImpl : public CWindowImpl { public: CListControlImpl() {} DECLARE_WND_CLASS_EX(TEXT("{4B94B650-C2D8-40de-A0AD-E8FADF62D56C}"),CS_DBLCLKS,COLOR_WINDOW); // Wrapper around CWindowImpl::Create(). // Creates CListControl replacing another dialog control with the specified ID. // Note that m_dlgWantEnter is set to false by this method, as it's typically unwanted in dialogs. void CreateInDialog( CWindow wndDialog, UINT replaceControlID ); enum { MSG_SIZE_ASYNC = WM_USER + 13, MSG_EXEC_DEFERRED, UserMsgBase }; BEGIN_MSG_MAP_EX(CListControlImpl) MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, MousePassThru); MESSAGE_HANDLER_EX(MSG_EXEC_DEFERRED, OnExecDeferred); MESSAGE_HANDLER(WM_PAINT,OnPaint); MSG_WM_PRINTCLIENT(OnPrintClient); MESSAGE_HANDLER(WM_VSCROLL,OnVScroll); MESSAGE_HANDLER(WM_HSCROLL,OnHScroll); MESSAGE_HANDLER(WM_SIZE,OnSize); MESSAGE_HANDLER(WM_MOUSEHWHEEL,OnHWheel); MESSAGE_HANDLER(WM_MOUSEWHEEL,OnVWheel); MESSAGE_HANDLER(WM_LBUTTONDOWN,SetFocusPassThru); MESSAGE_HANDLER(WM_RBUTTONDOWN,SetFocusPassThru); MESSAGE_HANDLER(WM_MBUTTONDOWN,SetFocusPassThru); MESSAGE_HANDLER(WM_LBUTTONDBLCLK,SetFocusPassThru); MESSAGE_HANDLER(WM_RBUTTONDBLCLK,SetFocusPassThru); MESSAGE_HANDLER(WM_MBUTTONDBLCLK,SetFocusPassThru); MESSAGE_HANDLER(WM_CREATE,OnCreatePassThru); MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkgnd); MESSAGE_HANDLER(MSG_SIZE_ASYNC,OnSizeAsync); MESSAGE_HANDLER(WM_GESTURE, OnGesture) MSG_WM_THEMECHANGED(OnThemeChanged) MSG_WM_KILLFOCUS(OnKillFocus) MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged) MESSAGE_HANDLER_EX( WM_GETDLGCODE, OnGetDlgCode ) END_MSG_MAP() virtual void ReloadData() { OnViewAreaChanged(); } virtual void ReloadItems( pfc::bit_array const & mask ) { UpdateItems( mask); } //! Hookable function called in response to reordering of items. Redraws the view and updates internal data to reflect the change. virtual void OnItemsReordered( const size_t * order, size_t count ); //! Hookable function called in response to removal of items. Redraws the view and updates internal data to reflect the change. virtual void OnItemsRemoved( pfc::bit_array const & mask, size_t oldCount ) { ReloadData(); } //! Hookable function called in response to insertion of items. Redraws the view and updates internal data to reflect the change. virtual void OnItemsInserted( size_t at, size_t count, bool bSelect ) { ReloadData(); } void ReloadItem(size_t i) { ReloadItems( pfc::bit_array_one(i) ); } void OnViewAreaChanged() {OnViewAreaChanged(GetViewOrigin());} void OnViewAreaChanged(CPoint p_originOverride); void UpdateGroupHeader(int p_id); void UpdateItems(const pfc::bit_array & p_mask); void UpdateItemsAndHeaders(const pfc::bit_array & p_mask); void UpdateItem(t_size p_item) {UpdateItems(pfc::bit_array_one(p_item));} void UpdateItemsAll() {Invalidate();} void EnsureItemVisible(t_size p_item, bool bUser = false); void EnsureHeaderVisible(int p_group); virtual void EnsureVisibleRectAbs(const CRect & p_rect); CRect GetItemRect(t_size p_item) const; bool GetGroupHeaderRect(int p_group,CRect & p_rect) const; CRect GetItemRectAbs(t_size p_item) const; bool GetGroupHeaderRectAbs(int p_group,CRect & p_rect) const; CPoint GetViewOrigin() const {return m_viewOrigin;} CPoint GetViewOffset() const {return GetViewOrigin() - GetClientOrigin();} int GetViewAreaWidth() const {return GetItemWidth();} int GetViewAreaHeight() const; CRect GetViewAreaRectAbs() const; CRect GetViewAreaRect() const; CRect GetValidViewOriginArea() const; t_size GetGroupCount() const; bool GetItemRangeAbs(const CRect & p_rect,t_size & p_base,t_size & p_count) const; bool GetItemRangeAbsInclHeaders(const CRect & p_rect,t_size & p_base,t_size & p_count) const; bool GetItemRange(const CRect & p_rect,t_size & p_base,t_size & p_count) const; void MoveViewOriginNoClip(CPoint p_target); void MoveViewOrigin(CPoint p_target); CPoint ClipViewOrigin(CPoint p_origin) const; void MoveViewOriginDelta(CPoint p_delta) {MoveViewOrigin( GetViewOrigin() + p_delta );} void MoveViewOriginDeltaNoClip(CPoint p_delta) {MoveViewOriginNoClip( GetViewOrigin() + p_delta );} bool ItemFromPoint(CPoint const & p_pt,t_size & p_item) const {return ItemFromPointAbs(p_pt + GetViewOffset(),p_item);} bool GroupHeaderFromPoint(CPoint const & p_pt,int & p_group) const {return GroupHeaderFromPointAbs(p_pt + GetViewOffset(),p_group);} bool ItemFromPointAbs(CPoint const & p_pt,t_size & p_item) const; bool GroupHeaderFromPointAbs(CPoint const & p_pt,int & p_group) const; bool ResolveGroupRange(int p_id,t_size & p_base,t_size & p_count) const; virtual int GetGroupHeaderHeight() const {return 0;} virtual int GetItemHeight() const {return 0;} virtual int GetItemWidth() const {return 0;} virtual t_size GetItemCount() const {return 0;} virtual int GetItemGroup(t_size p_item) const {return 0;} //override optionally virtual void RenderItem(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc); //override optionally virtual void RenderGroupHeader(int p_group,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc); //called by default RenderItem implementation virtual void RenderItemText(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc, bool allowColors) {} //called by default RenderItem implementation virtual void RenderGroupHeaderText(int p_group,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc) {} virtual void RenderItemBackground(CDCHandle p_dc,const CRect & p_itemRect,size_t item, uint32_t bkColor); virtual void RenderGroupHeaderBackground(CDCHandle p_dc,const CRect & p_headerRect,int iGroup); virtual void RenderBackground( CDCHandle dc, CRect const & rc ); virtual void OnViewOriginChange(CPoint p_delta) {} virtual void RenderOverlay(const CRect & p_updaterect,CDCHandle p_dc) {} virtual bool FixedOverlayPresent() {return false;} virtual CRect GetClientRectHook() const; enum { colorText = COLOR_WINDOWTEXT, colorBackground = COLOR_WINDOW, colorHighlight = COLOR_HOTLIGHT, colorSelection = COLOR_HIGHLIGHT, }; virtual COLORREF GetSysColorHook( int colorIndex ) const; //! Called by CListControlWithSelectionBase. virtual void OnItemClicked(t_size item, CPoint pt) {} //! Called by CListControlWithSelectionBase. virtual void OnGroupHeaderClicked(int groupId, CPoint pt) {} //! Return true to indicate that some area of the control has a special purpose and clicks there should not trigger changes in focus/selection. virtual bool OnClickedSpecialHitTest(CPoint pt) { return false; } virtual bool OnClickedSpecial(DWORD status, CPoint pt) {return false;} virtual bool AllowScrollbar(bool vertical) const {return true;} CPoint GetClientOrigin() const {return GetClientRectHook().TopLeft();} CRect GetVisibleRectAbs() const { CRect view = GetClientRectHook(); view.OffsetRect( GetViewOrigin() - view.TopLeft() ); return view; } bool IsSameItemOrHeaderAbs(const CPoint & p_point1, const CPoint & p_point2) const; void AddItemToUpdateRgn(HRGN p_rgn, t_size p_index) const; void AddGroupHeaderToUpdateRgn(HRGN p_rgn, int id) const; t_size InsertIndexFromPoint(const CPoint & p_pt) const; //! Translate point to insert location for drag and drop. \n //! Made virtual so it can be specialized to allow only specific drop locations. virtual t_size InsertIndexFromPointEx(const CPoint & pt, bool & bInside) const; virtual void ListHandleResize(); //! Can smooth-scroll *now* ? Used to suppress smooth scroll on temporary basis due to specific user operations in progress virtual bool CanSmoothScroll() const { return true; } //! Is smooth scroll enabled by user? virtual bool UserEnabledSmoothScroll() const; virtual bool ToggleSelectedItemsHook(pfc::bit_array const & mask) { return false; } void SetCaptureEx(CaptureProc_t proc); void SetCaptureMsgHandled(BOOL v) { this->SetMsgHandled(v); } SIZE GetDPI() const { return this->m_dpi;} // Should this control take enter key in dialogs or not? // Default to true for compatibility with existing code - but when used in dialogs, you'll want it set to false to hit [OK] with enter. // Note that CreateInDialog() sets this to false. Change it later if you want enter key presses to reach this control in a dialog. bool m_dlgWantEnter = true; bool WantReturn() const { return m_dlgWantEnter; } void SetWantReturn(bool v) { m_dlgWantEnter = v; } enum { rowStyleGrid = 0, rowStyleFlat, rowStylePlaylist, rowStylePlaylistDelimited, }; void SetPlaylistStyle() {SetRowStyle(rowStylePlaylist);} void SetRowStyle(unsigned v) { this->m_rowStyle = v; if (m_hWnd) Invalidate(); } void SetFlatStyle() {SetRowStyle(rowStyleFlat);} unsigned m_rowStyle = rowStylePlaylistDelimited; bool DelimitColumns() const { return m_rowStyle == rowStyleGrid || m_rowStyle == rowStylePlaylistDelimited; } static COLORREF BlendGridColor( COLORREF bk, COLORREF tx ); static COLORREF BlendGridColor( COLORREF bk ); COLORREF GridColor(); private: void RenderRect(const CRect & p_rect,CDCHandle p_dc); int HandleWheel(int & p_accum,int p_delta, bool bHoriz); void OnKillFocus(CWindow); void OnWindowPosChanged(LPWINDOWPOS); void PaintContent(CRect rcPaint, HDC dc); void OnPrintClient(HDC dc, UINT uFlags); LRESULT OnPaint(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnVScroll(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnHScroll(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnSize(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnSizeAsync(UINT,WPARAM,LPARAM,BOOL&) {ListHandleResize();return 0;} LRESULT OnVWheel(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnHWheel(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnGesture(UINT,WPARAM,LPARAM,BOOL&); LRESULT SetFocusPassThru(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnCreatePassThru(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnEraseBkgnd(UINT,WPARAM,LPARAM,BOOL&); LRESULT MousePassThru(UINT, WPARAM, LPARAM); LRESULT OnGetDlgCode(UINT, WPARAM, LPARAM); void OnThemeChanged(); int GetScrollThumbPos(int which); void RefreshSliders(); void RefreshSlider(bool p_vertical); void OnSizeAsync_Trigger(); static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam); bool MouseWheelFromHook(UINT msg, LPARAM data); bool m_suppressMouseWheel = false; int m_wheelAccumX = 0, m_wheelAccumY = 0; CPoint m_viewOrigin = CPoint(0,0); bool m_sizeAsyncPending = false; CPoint m_gesturePoint; protected: pfc::map_t m_themeCache; CTheme & themeFor( const char * what ); CTheme & theme() { return themeFor("LISTVIEW");} const SIZE m_dpi = QueryScreenDPIEx(); CGestureAPI m_gestureAPI; bool m_ensureVisibleUser = false; CaptureProc_t m_captureProc; void defer( std::function f ); LRESULT OnExecDeferred(UINT, WPARAM, LPARAM); // Overlays our stuff on top of generic DoDragDrop call. // Currently catches mouse wheel messages in mid-drag&drop and handles them in our view. HRESULT DoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect); private: bool m_defferredMsgPending = false; std::list > m_deferred; }; class CListControlFontOps : public CListControlImpl { private: typedef CListControlImpl TParent; public: CListControlFontOps(); BEGIN_MSG_MAP_EX(CListControlFontOps) MESSAGE_HANDLER(WM_GETFONT,OnGetFont); MESSAGE_HANDLER(WM_SETFONT,OnSetFont); CHAIN_MSG_MAP(TParent); END_MSG_MAP() CFontHandle GetFont() const { return m_font; } void SetFont(HFONT font, bool bUpdateView = true); protected: CFontHandle GetGroupHeaderFont() const {return (HFONT)m_groupHeaderFont;} virtual double GroupHeaderFontScale() const { return 1.25; } virtual int GroupHeaderFontWeight(int origVal) const { //return pfc::min_t(FW_BLACK, origVal + 200); return origVal; } //! Overridden implementations should always forward the call to the base class. virtual void OnSetFont(bool bUpdatingView) {} int GetGroupHeaderHeight() const {return m_groupHeaderHeight;} int GetItemHeight() const {return m_itemHeight;} private: LRESULT OnSetFont(UINT,WPARAM,LPARAM,BOOL&); LRESULT OnGetFont(UINT,WPARAM,LPARAM,BOOL&); void UpdateGroupHeaderFont(); void CalculateHeights(); int m_itemHeight, m_groupHeaderHeight; CFontHandle m_font; CFont m_groupHeaderFont; }; #include "CListControlHeaderImpl.h" #include "CListControlTruncationTooltipImpl.h" typedef CMiddleDragImpl CListControl;