source: trunk/src/HaliteSortListViewCtrl.hpp @ 447

Revision 447, 12.8 KB checked in by Eoin, 12 years ago (diff)

Cleaned up the SortListView? class.

Line 
1
2//         Copyright Eóin O'Callaghan 2006 - 2008.
3// Distributed under the Boost Software License, Version 1.0.
4//    (See accompanying file LICENSE_1_0.txt or copy at
5//          http://www.boost.org/LICENSE_1_0.txt)
6
7#pragma once
8
9#include "stdAfx.hpp"
10
11#include <functional>
12
13#include <boost/array.hpp>
14#include <boost/signals.hpp>
15#include <boost/algorithm/string/split.hpp>
16#include <boost/serialization/vector.hpp>
17#include <boost/serialization/split_free.hpp>
18#include <boost/ptr_container/ptr_map.hpp>
19
20#include <winstl/controls/listview_sequence.hpp>
21
22#include "Halite.hpp"
23#include "halTorrent.hpp"
24#include "halEvent.hpp"
25#include "WinAPIWaitableTimer.hpp"
26
27#include "UxthemeWrapper.hpp"
28
29#define LVS_EX_DOUBLEBUFFER     0x00010000
30
31#include "WTLx/SelectionManager.hpp"
32#include "HaliteUpdateLock.hpp"
33
34namespace hal
35{
36
37template<typename T>
38int compare(const T& l, const T& r)
39{
40        if (l == r) 
41                return 0;
42        else if (l > r) 
43                return 1;
44        else 
45                return -1;
46}
47
48}
49
50template <class TBase, typename adapterType=void*>
51class CHaliteSortListViewCtrl : 
52        public WTL::CSortListViewCtrlImpl<CHaliteSortListViewCtrl<TBase, adapterType> >
53{
54public:
55        typedef CHaliteSortListViewCtrl<TBase, adapterType> thisClass;
56        typedef CSortListViewCtrlImpl<thisClass> parentClass;
57       
58        class CHaliteHeaderCtrl : public CWindowImpl<CHaliteHeaderCtrl, WTL::CHeaderCtrl>
59        {
60        public:
61                enum { COL_MENU_NAMES = 123, COL_MAX_NAMES = 256};
62
63                CHaliteHeaderCtrl(thisClass& listView) :
64                        listView_(listView)
65                {}
66
67                BEGIN_MSG_MAP(CHaliteHeaderCtrl)
68                        REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRClick)
69                        COMMAND_RANGE_HANDLER(COL_MENU_NAMES, COL_MENU_NAMES+COL_MAX_NAMES, OnMenuNames)
70
71                        DEFAULT_REFLECTION_HANDLER()
72                END_MSG_MAP()
73               
74                void Attach(HWND hWndNew)
75                {
76                        ATLASSERT(::IsWindow(hWndNew));
77                        CWindowImpl<CHaliteHeaderCtrl, CHeaderCtrl>::SubclassWindow(hWndNew);
78                }
79               
80                LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
81                {
82                        POINT ptPoint;
83                        GetCursorPos(&ptPoint);
84                        menu_.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
85
86                        return 0;
87                }
88
89                LRESULT OnMenuNames(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
90                {               
91                        ATLASSERT(wID-COL_MENU_NAMES <= GetItemCount());
92
93                        bool visible = listView_.OnNameChecked(wID-COL_MENU_NAMES);
94
95                        MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
96               
97                        minfo.fMask = MIIM_STATE;
98                        minfo.fState = visible ? MFS_CHECKED : MFS_UNCHECKED;
99               
100                        menu_.SetMenuItemInfo(wID, false, &minfo);
101
102                        return 0;
103                }
104
105                WTL::CMenu& Menu()
106                {
107                        return menu_;
108                }
109               
110        private:
111                WTL::CMenu menu_;
112                thisClass& listView_;
113        };
114       
115        struct ColumnAdapter
116        {
117                virtual int compare(adapterType& l, adapterType& r) = 0;
118                virtual std::wstring print(adapterType& t) = 0;
119        };
120
121public:
122        typedef WTLx::selection_manager<thisClass, std::wstring> SelectionManager;
123        typedef SelectionManager selection_manage_class;
124       
125        thisClass() :
126                manager_(*this),
127                header_(*this),
128                update_lock_(0),
129                autoSort_(false),
130                descending_(false),
131                sortCol_(-1)
132        {               
133                if (TBase::LISTVIEW_ID_MENU)
134                {
135                        CMenuHandle menu;
136                        BOOL menu_created = menu.LoadMenu(TBase::LISTVIEW_ID_MENU);
137                        assert(menu_created);   
138                       
139                        menu_.Attach(menu.GetSubMenu(0));
140                }
141        }
142
143        BEGIN_MSG_MAP_EX(thisClass)
144                COMMAND_ID_HANDLER(ID_LVM_AUTOSORT, OnAutoSort)
145               
146                REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRClick)
147                REFLECTED_NOTIFY_CODE_HANDLER(LVN_ITEMCHANGED, OnItemChanged)
148
149                DEFAULT_REFLECTION_HANDLER()
150                CHAIN_MSG_MAP(parentClass)
151        END_MSG_MAP()
152
153        void Attach(HWND hWndNew)
154        {
155                ATLASSERT(::IsWindow(hWndNew));
156        parentClass::SubclassWindow(hWndNew);
157
158                TBase* pT = static_cast<TBase*>(this);
159                pT->OnAttach();
160        }
161       
162        HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
163                        DWORD dwStyle = 0, DWORD dwExStyle = 0,
164                        ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
165        {
166                HWND hwnd = parentClass::Create(hWndParent, 
167                        (RECT &)rect.m_lpRect, szWindowName, dwStyle, dwExStyle, (UINT)MenuOrID.m_hMenu, lpCreateParam);
168                       
169                SetExtendedListViewStyle(WS_EX_CLIENTEDGE|LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_DOUBLEBUFFER);
170                SetSortListViewExtendedStyle(SORTLV_USESHELLBITMAPS, SORTLV_USESHELLBITMAPS);
171               
172                return hwnd;
173        }
174       
175        bool SubclassWindow(HWND hwnd)
176        {
177                if(!parentClass::SubclassWindow(hwnd))
178                        return false;
179                       
180                SetExtendedListViewStyle(WS_EX_CLIENTEDGE|LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_DOUBLEBUFFER);
181                SetSortListViewExtendedStyle(SORTLV_USESHELLBITMAPS, SORTLV_USESHELLBITMAPS);
182               
183                return true;
184        }               
185       
186        template<typename N, typename W, typename O, typename P>
187        void SetDefaults(N nameList, W widthList, O orderList, P visibleList, bool autoSort=false)
188        {
189                listNames_.assign(nameList.begin(), nameList.end());
190                listWidths_.assign(widthList.begin(), widthList.end());
191                listOrder_.assign(orderList.begin(), orderList.end());
192                listVisible_.assign(visibleList.begin(), visibleList.end());
193               
194                autoSort_ = autoSort;
195        }
196       
197        void ApplyDetails()
198        {
199                vectorSizePreConditions();
200               
201                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
202               
203                if (!menu_)
204                {
205                        menu_.CreatePopupMenu();
206                }
207                else
208                {                               
209                        minfo.fMask = MIIM_SUBMENU;
210                        minfo.fType = MFT_SEPARATOR;
211                       
212                        menu_.InsertMenuItem(menu_.GetMenuItemCount(), true, &minfo);           
213                }
214
215                minfo.fMask = MIIM_STRING|MIIM_ID|MIIM_FTYPE|MIIM_STATE;
216                minfo.fType = MFT_STRING;
217                minfo.fState = autoSort_ ? MFS_CHECKED : MFS_UNCHECKED;
218                minfo.wID = ID_LVM_AUTOSORT;
219               
220                wstring autoarrange = hal::app().res_wstr(HAL_AUTOSORT);
221                minfo.dwTypeData = (LPWSTR)autoarrange.c_str();
222               
223                menu_.InsertMenuItem(menu_.GetMenuItemCount(), true, &minfo);
224               
225                header_.Attach(this->GetHeader());
226                header_.ModifyStyle(0, HDS_DRAGDROP|HDS_FULLDRAG);
227
228                header_.Menu().CreatePopupMenu();
229
230                for (int i=header_.GetItemCount(), e=int(listNames_.size()); i<e; ++i)
231                {
232                        MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
233                        minfo.fMask = MIIM_STRING|MIIM_ID|MIIM_FTYPE|MIIM_STATE;
234                        minfo.fType = MFT_STRING;
235                        minfo.dwTypeData = (LPWSTR)listNames_[i].c_str();
236                        minfo.wID = CHaliteHeaderCtrl::COL_MENU_NAMES+i;
237
238                        AddColumn(listNames_[i].c_str(), i);
239
240                        if (listVisible_[i])
241                        {
242                                minfo.fState = MFS_CHECKED;                     
243                                SetColumnWidth(i, listWidths_[i]);
244                        }
245                        else
246                        {
247                                minfo.fState = MFS_UNCHECKED;
248                                SetColumnWidth(i, 0);
249                        }
250
251                        header_.Menu().InsertMenuItem(header_.Menu().GetMenuItemCount(), false, &minfo);
252                }
253               
254                SetColumnOrderArray(listNames_.size(), &listOrder_[0]);
255               
256                m_bSortDescending = descending_;
257                if (sortCol_ >= 0 && sortCol_ < m_arrColSortType.GetSize())
258                        SetSortColumn(sortCol_);
259        }
260       
261        void GetListViewDetails()
262        {
263                vectorSizePreConditions();             
264               
265                for (size_t i=0; i<listNames_.size(); ++i)
266                {
267                        if (listVisible_[i])
268                                listWidths_[i] = GetColumnWidth(i);
269                }
270               
271                GetColumnOrderArray(listNames_.size(), &listOrder_[0]);
272               
273                sortCol_ = GetSortColumn();
274                descending_ = IsSortDescending();       
275        }
276       
277        LRESULT OnAutoSort(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
278        {
279                autoSort_ = !autoSort_;
280               
281                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
282               
283                minfo.fMask = MIIM_STATE;
284                minfo.fState = autoSort_ ? MFS_CHECKED : MFS_UNCHECKED;
285               
286                menu_.SetMenuItemInfo(ID_LVM_AUTOSORT, false, &minfo);
287               
288                return 0;
289        }
290
291        bool OnNameChecked(int i)
292        {
293                if (!listVisible_[i])
294                {               
295                        GetColumnOrderArray(listNames_.size(), &listOrder_[0]);
296                        SetColumnWidth(i, listWidths_[i]);
297
298                        listOrder_.erase(std::find(listOrder_.begin(), listOrder_.end(), i));
299                       
300                        int index = i + std::count(listVisible_.begin()+i, listVisible_.end(), false) - 1;
301                        listOrder_.insert(listOrder_.begin()+index, i);
302
303                        SetColumnOrderArray(listNames_.size(), &listOrder_[0]);
304                        listVisible_[i] = true;
305                }
306                else
307                {
308                        listWidths_[i] = GetColumnWidth(i);     
309                        GetColumnOrderArray(listNames_.size(), &listOrder_[0]);
310
311                        SetColumnWidth(i, 0);
312
313                        listOrder_.erase(std::find(listOrder_.begin(), listOrder_.end(), i));
314                        listOrder_.insert(listOrder_.begin(), i);
315
316                        SetColumnOrderArray(listNames_.size(), &listOrder_[0]);
317                        listVisible_[i] = false;
318                }
319       
320                InvalidateRect(NULL, true);
321                return listVisible_[i];
322        }
323
324        LRESULT OnClick(int, LPNMHDR pnmh, BOOL&)
325        {
326                return 0;
327        }
328
329        LRESULT OnItemChanged(int, LPNMHDR pnmh, BOOL&)
330        {               
331                hal::try_update_lock<thisClass> lock(*this);
332               
333                if (lock) manager_.sync_list(true, true);
334               
335                return 0;
336        }
337
338        LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
339        {
340                LPNMITEMACTIVATE pia = (LPNMITEMACTIVATE)pnmh;
341                manager_.sync_list(true);
342               
343                if (menu_)
344                {
345                        assert (menu_.IsMenu());
346       
347                        POINT ptPoint;
348                        GetCursorPos(&ptPoint);
349                        menu_.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
350                }
351
352                return 0;
353        }
354
355        LRESULT OnColClick(int i, LPNMHDR pnmh, BOOL&)
356        {
357                LPNMLISTVIEW pnlv = (LPNMLISTVIEW)pnmh;
358               
359                MessageBox((lexical_cast<wstring>(pnlv->iSubItem)).c_str(), L"Hi", 0);
360                return 0;
361        }
362       
363        void SetColumnSortType(int iCol, WORD wType, ColumnAdapter* colAdapter=NULL)
364        {
365                parentClass::SetColumnSortType(iCol, wType);
366               
367                if (LVCOLSORT_CUSTOM == wType)
368                        regColumnAdapter(iCol, colAdapter);
369        }
370       
371        friend class boost::serialization::access;
372    template<class Archive>
373    void serialize(Archive & ar, const unsigned int version)
374    {
375                using boost::serialization::make_nvp;
376                if (version >= 1)
377                {
378                        ar & make_nvp("width", listWidths_);
379                        ar & make_nvp("order", listOrder_);
380                        ar & make_nvp("visible", listVisible_);
381                        ar & make_nvp("autoSort", autoSort_);
382                }
383                if (version >= 2)
384                {
385                        ar & make_nvp("descending", descending_);
386                        ar & make_nvp("sortCol", sortCol_);
387                }
388    }
389
390        const SelectionManager& manager() { return manager_; }
391               
392        std::vector<int>& listColumnWidth() { return listColumnWidth_; }
393        std::vector<int>& listColumnOrder() { return listColumnOrder_; }
394       
395        const std::vector<int>& listColumnWidth() const { return listColumnWidth_; }
396        const std::vector<int>& listColumnOrder() const { return listColumnOrder_; }
397       
398        bool canUpdate() const { return updateLock_ == 0; }
399       
400        void clearFocused() { manager_.clear(); }
401        void clearSelected() { manager_.clear_all_selected(); }
402        void clearAll() { manager_.clear_all(); }
403       
404        int CompareItemsCustom(LVCompareParam* pItem1, LVCompareParam* pItem2, int iSortCol)
405        {
406                hal::mutex_update_lock<thisClass> lock(*this);
407               
408                TBase* pT = static_cast<TBase*>(this);
409               
410                adapterType left = pT->CustomItemConversion(pItem1, iSortCol);
411                adapterType right = pT->CustomItemConversion(pItem2, iSortCol);
412               
413                return pT->CustomItemComparision(left, right, iSortCol);
414        }
415       
416        bool autoSort() { return autoSort_; }
417       
418        void ConditionallyDoAutoSort()
419        {
420                int iCol = GetSortColumn();
421                if (autoSort() && iCol >= 0 && iCol < m_arrColSortType.GetSize())
422                        DoSortItems(iCol, IsSortDescending()); 
423        }
424               
425        ColumnAdapter* getColumnAdapter(size_t index)
426        {
427                boost::ptr_map<size_t, ColumnAdapter>::iterator
428                        i = columnAdapters_.find(index);
429       
430                if (i != columnAdapters_.end())
431                {
432                        return i->second;
433                }               
434                return NULL;
435        }
436
437        static bool is_selected (const winstl::listview_sequence::sequence_value_type& v) 
438        { 
439                return (v.state() & LVIS_SELECTED) != 0; 
440        }
441
442protected:     
443        inline void* CustomItemConversion(LVCompareParam* param, int iSortCol)
444        {
445                assert(false);
446                return NULL;
447        }
448       
449        int CustomItemComparision(adapterType left, adapterType right, int iSortCol)
450        {
451                ColumnAdapter* pCA = getColumnAdapter(iSortCol);
452               
453                if (pCA)
454                        return pCA->compare(left, right);
455                else 
456                        return 0;
457        }
458       
459        void regColumnAdapter(size_t key, ColumnAdapter* colAdapter)
460        {
461                assert (colAdapter);
462                columnAdapters_.insert(key, colAdapter);
463        }
464       
465        SelectionManager manager_;
466       
467private:
468        void vectorSizePreConditions()
469        {
470        }
471       
472        WTL::CMenu menu_;
473        CHaliteHeaderCtrl header_;     
474       
475        std::vector<wstring> listNames_;
476        std::vector<int> listWidths_;
477        std::vector<int> listOrder_;
478        std::vector<bool> listVisible_;
479        bool autoSort_;
480        bool descending_;
481        int sortCol_;
482       
483        mutable int update_lock_;
484        mutable hal::mutex_t mutex_;
485
486        friend class hal::mutex_update_lock<thisClass>; 
487        friend class hal::try_update_lock<thisClass>;           
488       
489        boost::ptr_map<size_t, ColumnAdapter> columnAdapters_;
490       
491        WinAPIWaitableTimer syncTimer_;
492};
493
494template<>
495inline const std::wstring hal::to_wstr_shim<const winstl::listview_sequence::sequence_value_type>
496        (const winstl::listview_sequence::sequence_value_type& v)
497{
498        return std::wstring(winstl::c_str_ptr(v));
499}
500
501
502namespace boost {
503namespace serialization {
504template <class TBase, typename adapterType>
505struct version< CHaliteSortListViewCtrl<TBase, adapterType> >
506{
507    typedef mpl::int_<2> type;
508    typedef mpl::integral_c_tag tag;
509    BOOST_STATIC_CONSTANT(unsigned int, value = version::type::value);                                                             
510};
511}
512}
Note: See TracBrowser for help on using the repository browser.