source: trunk/src/HaliteSortListViewCtrl.hpp @ 685

Revision 685, 16.6 KB checked in by Eoin, 11 years ago (diff)

Secondary sorting complete.

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/ptr_container/ptr_map.hpp>
17
18#include <winstl/controls/listview_sequence.hpp>
19
20#include "Halite.hpp"
21#include "halTorrent.hpp"
22#include "halEvent.hpp"
23#include "WinAPIWaitableTimer.hpp"
24
25#include "UxthemeWrapper.hpp"
26
27#define LVS_EX_DOUBLEBUFFER     0x00010000
28
29#include "WTLx/SelectionManager.hpp"
30#include "WTLx/ListViewIterators.hpp"
31#include "WTLx/ListViewSortMixin.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 ATL::CWindowImpl<TBase, WTL::CListViewCtrl>,
53        public WTLx::ListViewIterators<CHaliteSortListViewCtrl<TBase, AdapterType> >,
54        public WTLx::ListViewSortMixin<CHaliteSortListViewCtrl<TBase, AdapterType> >
55{
56public:
57        typedef CHaliteSortListViewCtrl<TBase, AdapterType> thisClass;
58        typedef ATL::CWindowImpl<TBase, WTL::CListViewCtrl> parentClass;
59        typedef WTLx::ListViewSortMixin<thisClass> listClass;
60       
61        class CHaliteHeaderCtrl : public CWindowImpl<CHaliteHeaderCtrl, WTL::CHeaderCtrl>
62        {
63        public:
64                enum { COL_MENU_NAMES = 123, COL_MAX_NAMES = 256 };
65
66                CHaliteHeaderCtrl(thisClass& listView) :
67                        listView_(listView)
68                {}
69
70                BEGIN_MSG_MAP(CHaliteHeaderCtrl)
71                        REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRClick)
72                        COMMAND_RANGE_HANDLER(COL_MENU_NAMES, COL_MENU_NAMES+COL_MAX_NAMES, OnMenuNames)
73
74                        DEFAULT_REFLECTION_HANDLER()
75                END_MSG_MAP()
76               
77                void Attach(HWND hWndNew)
78                {
79                        ATLASSERT(::IsWindow(hWndNew));
80                        CWindowImpl<CHaliteHeaderCtrl, WTL::CHeaderCtrl>::SubclassWindow(hWndNew);
81                }
82               
83                LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
84                {
85                        POINT ptPoint;
86                        GetCursorPos(&ptPoint);
87                        menu_.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
88
89                        return 0;
90                }
91
92                LRESULT OnMenuNames(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
93                {               
94                        ATLASSERT(wID-COL_MENU_NAMES <= GetItemCount());
95
96                        bool visible = listView_.OnNameChecked(wID-COL_MENU_NAMES);
97
98                        return 0;
99                }
100
101                WTL::CMenu& Menu()
102                {
103                        return menu_;
104                }
105               
106        private:
107                WTL::CMenu menu_;
108                thisClass& listView_;
109        };
110       
111        struct ColumnAdapter
112        {
113                virtual int compare(AdapterType& l, AdapterType& r) = 0;
114                virtual std::wstring print(AdapterType& t) = 0;
115        };
116
117public:
118        typedef WTLx::selection_manager<thisClass, std::wstring> SelectionManager;
119        typedef SelectionManager selection_manage_class;
120       
121        thisClass() :
122                manager_(*this),
123                header_(*this),
124                update_lock_(0),
125                auto_sort_(false),
126                descending_(false),
127                sortCol_(-1)
128        {               
129                if (TBase::LISTVIEW_ID_MENU)
130                {
131                        WTL::CMenuHandle menu;
132                        BOOL menu_created = menu.LoadMenu(TBase::LISTVIEW_ID_MENU);
133                        assert(menu_created);   
134                       
135                        menu_.Attach(menu.GetSubMenu(0));
136                }
137        }
138
139        BEGIN_MSG_MAP_EX(thisClass)
140                COMMAND_ID_HANDLER(ID_LVM_AUTOSORT, OnAutoSort)
141               
142                REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRClick)
143                REFLECTED_NOTIFY_CODE_HANDLER(LVN_ITEMCHANGED, OnItemChanged)
144
145                CHAIN_MSG_MAP(listClass)
146                DEFAULT_REFLECTION_HANDLER()
147        END_MSG_MAP()
148
149        void Attach(HWND hWndNew)
150        {
151                ATLASSERT(::IsWindow(hWndNew));
152        parentClass::SubclassWindow(hWndNew);
153
154                TBase* pT = static_cast<TBase*>(this);
155                pT->OnAttach();
156        }
157
158        HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
159                        DWORD dwStyle = 0, DWORD dwExStyle = 0,
160                        ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
161        {
162                HWND hwnd = parentClass::Create(hWndParent, 
163                        (RECT &)rect.m_lpRect, szWindowName, dwStyle, dwExStyle, (UINT)MenuOrID.m_hMenu, lpCreateParam);
164                       
165                SetExtendedListViewStyle(WS_EX_CLIENTEDGE|LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_DOUBLEBUFFER|LVS_EX_SUBITEMIMAGES);
166                SetListViewSortMixinExtendedStyle(SORTLV_USESHELLBITMAPS, SORTLV_USESHELLBITMAPS);
167               
168                return hwnd;
169        }
170       
171        bool SubclassWindow(HWND hwnd)
172        {
173                if(!parentClass::SubclassWindow(hwnd))
174                        return false;
175                       
176                SetExtendedListViewStyle(WS_EX_CLIENTEDGE|LVS_EX_FULLROWSELECT|LVS_EX_HEADERDRAGDROP|LVS_EX_DOUBLEBUFFER);
177                SetListViewSortMixinExtendedStyle(SORTLV_USESHELLBITMAPS, SORTLV_USESHELLBITMAPS);
178               
179                return true;
180        }               
181       
182        void SafeLoadFromIni()
183        {
184                std::vector<wstring> listNames;
185                std::vector<int> listWidths;
186                std::vector<int> listOrder;
187                std::vector<bool> listVisible;
188
189                listNames.assign(list_names_.begin(), list_names_.end());
190                listWidths.assign(list_widths_.begin(), list_widths_.end());
191                listOrder.assign(list_order_.begin(), list_order_.end());
192                listVisible.assign(list_visible_.begin(), list_visible_.end());
193
194                TBase* pT = static_cast<TBase*>(this);
195                if (!pT->load_from_ini() || !vector_size_pre_conditions())
196                {
197                        list_names_.assign(listNames.begin(), listNames.end());
198                        list_widths_.assign(listWidths.begin(), listWidths.end());
199                        list_order_.assign(listOrder.begin(), listOrder.end());
200                        list_visible_.assign(listVisible.begin(), listVisible.end());
201                }               
202        }
203
204        void InitialSetup(WTL::CMenuHandle menu=WTL::CMenuHandle())
205        {
206                SetExtendedListViewStyle(LVS_EX_HEADERDRAGDROP|LVS_EX_DOUBLEBUFFER);
207                SetSortListViewExtendedStyle(SORTLV_USESHELLBITMAPS,SORTLV_USESHELLBITMAPS);
208
209                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
210               
211                if (!menu)
212                {
213                        menu_.CreatePopupMenu();
214                }
215                else
216                {               
217                        menu_.Attach(menu.GetSubMenu(0));
218
219                        minfo.fMask = MIIM_SUBMENU;
220                        minfo.fType = MFT_SEPARATOR;
221                       
222                        menu_.InsertMenuItem(menu_.GetMenuItemCount(), true, &minfo);           
223                }
224
225                minfo.fMask = MIIM_STRING|MIIM_ID|MIIM_FTYPE|MIIM_STATE;
226                minfo.fType = MFT_STRING;
227                minfo.fState = auto_sort_ ? MFS_CHECKED : MFS_UNCHECKED;
228                minfo.wID = ID_LVM_AUTOSORT;
229               
230                std::wstring autoarrange = hal::app().res_wstr(HAL_AUTOSORT);
231                minfo.dwTypeData = (LPWSTR)autoarrange.c_str();
232               
233                menu_.InsertMenuItem(menu_.GetMenuItemCount(), true, &minfo);
234
235                header_.SubclassWindow(this->GetHeader());
236                header_.ModifyStyle(0, HDS_DRAGDROP|HDS_FULLDRAG);
237                if (header_.Menu().IsNull()) 
238                        header_.Menu().CreatePopupMenu();
239        }
240       
241        void GetListViewDetails()
242        {
243                vector_size_pre_conditions();           
244               
245                for (size_t i=0; i<list_names_.size(); ++i)
246                {
247                        if (list_visible_[i])
248                                list_widths_[i] = GetColumnWidth(i);
249                }
250               
251                GetColumnOrderArray(list_names_.size(), &list_order_[0]);
252               
253                sortCol_ = GetSortColumn();
254                descending_ = IsSortDescending();       
255        }
256       
257        LRESULT OnAutoSort(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
258        {
259                auto_sort_ = !auto_sort_;
260               
261                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
262               
263                minfo.fMask = MIIM_STATE;
264                minfo.fState = auto_sort_ ? MFS_CHECKED : MFS_UNCHECKED;
265               
266                menu_.SetMenuItemInfo(ID_LVM_AUTOSORT, false, &minfo);
267               
268                return 0;
269        }
270
271        bool OnNameChecked(int i)
272        {
273                if (!list_visible_[i])
274                {               
275                        GetColumnOrderArray(list_names_.size(), &list_order_[0]);
276                        SetColumnWidth(i, list_widths_[i]);
277
278                        list_order_.erase(std::find(list_order_.begin(), list_order_.end(), i));
279                       
280                        int index = i + std::count(list_visible_.begin()+i, list_visible_.end(), false) - 1;
281                        list_order_.insert(list_order_.begin()+index, i);
282
283                        SetColumnOrderArray(list_names_.size(), &list_order_[0]);
284                        list_visible_[i] = true;
285                }
286                else
287                {
288                        list_widths_[i] = GetColumnWidth(i);   
289                        GetColumnOrderArray(list_names_.size(), &list_order_[0]);
290
291                        SetColumnWidth(i, 0);
292
293                        list_order_.erase(std::find(list_order_.begin(), list_order_.end(), i));
294                        list_order_.insert(list_order_.begin(), i);
295
296                        SetColumnOrderArray(list_names_.size(), &list_order_[0]);
297                        list_visible_[i] = false;
298                }
299               
300                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};   
301                minfo.fMask = MIIM_STATE;
302                minfo.fState = list_visible_[i] ? MFS_CHECKED : MFS_UNCHECKED; 
303                header_.Menu().SetMenuItemInfo(CHaliteHeaderCtrl::COL_MENU_NAMES+i, false, &minfo);
304       
305                InvalidateRect(NULL, true);
306                return list_visible_[i];
307        }
308
309        LRESULT OnClick(int, LPNMHDR pnmh, BOOL&)
310        {
311                return 0;
312        }
313
314        LRESULT OnItemChanged(int, LPNMHDR pnmh, BOOL&)
315        {               
316                hal::try_update_lock<thisClass> lock(*this);
317               
318                if (lock) manager_.sync_list(true, true);
319               
320                return 0;
321        }
322
323        LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
324        {
325                LPNMITEMACTIVATE pia = (LPNMITEMACTIVATE)pnmh;
326                manager_.sync_list(true);
327               
328                if (menu_)
329                {
330                        assert (menu_.IsMenu());
331       
332                        POINT ptPoint;
333                        GetCursorPos(&ptPoint);
334                        menu_.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
335                }
336
337                return 0;
338        }
339
340        LRESULT OnColClick(int i, LPNMHDR pnmh, BOOL&)
341        {
342                LPNMLISTVIEW pnlv = (LPNMLISTVIEW)pnmh;
343               
344                MessageBox((lexical_cast<wstring>(pnlv->iSubItem)).c_str(), L"Hi", 0);
345                return 0;
346        }
347
348        bool DoSortItemsExternal(int iCol, bool bDescending = false)
349        {
350                HAL_DEV_MSG(hal::wform(L"sort_once_ = %1%") % sort_once_);
351
352                sort_once_ = true;
353
354                return true;
355        }
356       
357        bool IsSortOnce(bool mod_value =  true) 
358        { 
359                if (!sort_once_)
360                        return false; 
361
362                if (mod_value) sort_once_ = false;
363                return true;
364        }
365
366        int AddColumn(LPCTSTR strItem, int nItem, bool visible, int width=-1)
367        {
368                return AddColumn(strItem, nItem, -1,
369                        LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM,
370                        LVCFMT_LEFT, visible, width);
371        }
372
373        int AddColumn(LPCTSTR strItem, int nItem, int nSubItem = -1,
374                        int nMask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM,
375                        int nFmt = LVCFMT_LEFT, bool visible=true, int width=-1)
376        {
377                int i = parentClass::AddColumn(strItem, nItem, nSubItem, nMask, nFmt);
378
379                if (i == -1) return i;
380
381                if (width != -1) SetColumnWidth(i, width);
382
383                if (header_.Menu().IsNull()) 
384                        header_.Menu().CreatePopupMenu();
385
386                WTL::CMenuHandle menu = header_.Menu();
387
388                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
389                minfo.fMask = MIIM_STRING|MIIM_ID|MIIM_FTYPE|MIIM_STATE;
390                minfo.fType = MFT_STRING;
391                minfo.dwTypeData = (LPTSTR)strItem;
392                minfo.wID = CHaliteHeaderCtrl::COL_MENU_NAMES+i;
393
394                if (visible)
395                        minfo.fState = MFS_CHECKED;
396                else
397                {
398                        minfo.fState = MFS_UNCHECKED;
399                        SetColumnWidth(i, 0);
400                }
401
402                int w = GetColumnWidth(i);
403
404                list_names_.push_back(strItem);
405                list_visible_.push_back(visible);
406                list_widths_.push_back(w);
407                list_order_.push_back(i);
408
409                menu.InsertMenuItem(menu.GetMenuItemCount(), false, &minfo);
410                return i;
411        }
412
413        void SetColumnSortType(int iCol, WORD wType, ColumnAdapter* colAdapter=NULL)
414        {
415                listClass::SetColumnSortType(iCol, wType);
416               
417                if (WTL::LVCOLSORT_CUSTOM == wType)
418                        regColumnAdapter(iCol, colAdapter);
419        }
420
421        void SetColumnOrderState()
422        {
423                while ((int)list_order_.size() < header_.GetItemCount())
424                        list_order_.push_back(header_.GetItemCount());
425
426                GetColumnOrderArray(list_order_.size(), &list_order_[0]);
427        }
428
429        void SetSortState()
430        {
431                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
432               
433                minfo.fMask = MIIM_STATE;
434                minfo.fState = auto_sort_ ? MFS_CHECKED : MFS_UNCHECKED;
435               
436                menu_.SetMenuItemInfo(ID_LVM_AUTOSORT, false, &minfo);
437
438                if (sortCol_ >= 0 && sortCol_ < m_arrColSortType.GetSize())
439                        SetSortColumn(sortCol_);
440        }
441
442        friend class boost::serialization::access;
443        template<class Archive>
444    void save(Archive & ar, const unsigned int version) const
445    {
446                for (size_t i=0; i<list_widths_.size(); ++i)
447                {
448                        if (list_visible_[i])
449                                list_widths_[i] = GetColumnWidth(i);
450                }
451
452                GetColumnOrderArray(list_order_.size(), &list_order_[0]);
453                sortCol_ = GetSortColumn();
454                descending_ = IsSortDescending();       
455
456                using boost::serialization::make_nvp;
457
458                ar & make_nvp("width", list_widths_);
459                ar & make_nvp("order", list_order_);
460                ar & make_nvp("visible", list_visible_);
461                ar & make_nvp("autoSort", auto_sort_);
462
463                ar & make_nvp("descending", descending_);
464                ar & make_nvp("sortCol", sortCol_);
465
466                ar & make_nvp("secondary_descending", listClass::bSecondaryDescending);
467                ar & make_nvp("secondary_sort_column", listClass::iSecondarySort);             
468    }
469
470    template<class Archive>
471    void load(Archive & ar, const unsigned int version)
472    {
473                using boost::serialization::make_nvp;
474
475                ar & make_nvp("width", list_widths_);
476                ar & make_nvp("order", list_order_);
477                ar & make_nvp("visible", list_visible_);
478                ar & make_nvp("autoSort", auto_sort_);
479
480                ar & make_nvp("descending", descending_);
481                ar & make_nvp("sortCol", sortCol_);
482
483                ar & make_nvp("secondary_descending", listClass::bSecondaryDescending);
484                ar & make_nvp("secondary_sort_column", listClass::iSecondarySort);             
485               
486                SetColumnOrderArray(list_order_.size(), &list_order_[0]);
487
488                m_bSortDescending = descending_;
489                if (sortCol_ >= 0 && sortCol_ < m_arrColSortType.GetSize())
490                        SetSortColumn(sortCol_);
491
492                for (size_t i=0; i<list_widths_.size(); ++i)
493                {
494                        SetColumnWidth(i, list_widths_[i]);
495                        if (!list_visible_[i])
496                        {
497                                list_visible_[i] = true;
498                                OnNameChecked(i);
499                        }
500                }
501
502                SetColumnOrderState();
503                SetSortState();
504    }
505
506    BOOST_SERIALIZATION_SPLIT_MEMBER()
507
508        const SelectionManager& manager() { return manager_; }
509               
510        std::vector<int>& ListColumnWidth() { return listColumnWidth_; }
511        std::vector<int>& ListColumnOrder() { return listColumnOrder_; }
512       
513        const std::vector<int>& ListColumnWidth() const { return listColumnWidth_; }
514        const std::vector<int>& ListColumnOrder() const { return listColumnOrder_; }
515       
516        bool CanUpdate() const { return updateLock_ == 0; }
517       
518        void clearFocused() { manager_.clear(); }
519        void clearSelected() { manager_.clear_all_selected(); }
520        void clearAll() { manager_.clear_all(); }
521       
522/*      int CompareItemsCustom(LVCompareParam* pItem1, LVCompareParam* pItem2, int iSortCol)
523        {
524                hal::mutex_update_lock<thisClass> lock(*this);
525               
526                TBase* pT = static_cast<TBase*>(this);
527               
528                AdapterType left = pT->CustomItemConversion(pItem1, iSortCol);
529                AdapterType right = pT->CustomItemConversion(pItem2, iSortCol);
530               
531                return pT->CustomItemComparision(left, right, iSortCol);
532        }
533*/
534        bool AutoSort() { return auto_sort_; }
535       
536/*      void ConditionallyDoAutoSort()
537        {
538                int iCol = GetSortColumn();
539                if (AutoSort() && iCol >= 0 && iCol < m_arrColSortType.GetSize())
540                        DoSortItems(iCol, IsSortDescending()); 
541        }
542*/             
543        ColumnAdapter* getColumnAdapter(size_t index)
544        {
545                boost::ptr_map<size_t, ColumnAdapter>::iterator
546                        i = column_adapters_.find(index);
547       
548                if (i != column_adapters_.end())
549                {
550                        return i->second;
551                }               
552                return NULL;
553        }
554
555        static bool is_selected (const winstl::listview_sequence::sequence_value_type& v) 
556        { 
557                return (v.state() & LVIS_SELECTED) != 0; 
558        }
559
560protected:     
561/*      inline void* CustomItemConversion(LVCompareParam* param, int iSortCol)
562        {
563                assert(false);
564                return NULL;
565        }
566       
567        int CustomItemComparision(AdapterType left, AdapterType right, int iSortCol)
568        {
569                ColumnAdapter* pCA = getColumnAdapter(iSortCol);
570               
571                if (pCA)
572                        return pCA->compare(left, right);
573                else
574                        return 0;
575        }
576*/
577        void regColumnAdapter(size_t key, ColumnAdapter* colAdapter)
578        {
579                assert (colAdapter);
580                column_adapters_.insert(key, colAdapter);
581        }
582
583//      AdapterType convert(const LPLVITEM item);
584//      void convert(LPLVITEM item, AdapterType adapter);
585       
586        mutable int update_lock_;
587        mutable hal::mutex_t mutex_;
588
589        friend class hal::mutex_update_lock<thisClass>; 
590        friend class hal::try_update_lock<thisClass>;   
591
592        SelectionManager manager_;
593        WTL::CMenu menu_;
594        CHaliteHeaderCtrl header_;     
595       
596private:
597        bool vector_size_pre_conditions()
598        {
599                bool ret = (list_names_.size() == list_widths_.size()) &&
600                        (list_names_.size() == list_order_.size()) &&
601                        (list_names_.size() == list_visible_.size());
602
603                return ret;
604        }       
605       
606        mutable std::vector<wstring> list_names_;
607        mutable std::vector<int> list_widths_;
608        mutable std::vector<int> list_order_;
609        mutable std::vector<bool> list_visible_;
610
611        mutable bool auto_sort_;
612        mutable bool sort_once_;
613        mutable bool descending_;
614        mutable int sortCol_;
615               
616        boost::ptr_map<size_t, ColumnAdapter> column_adapters_;
617};
618
619template<>
620inline const std::wstring hal::to_wstr_shim<const winstl::listview_sequence::sequence_value_type>
621        (const winstl::listview_sequence::sequence_value_type& v)
622{
623        return std::wstring(winstl::c_str_ptr(v));
624}
625
626namespace boost {
627namespace serialization {
628        template <class TBase, typename AdapterType>
629        struct version< CHaliteSortListViewCtrl<TBase, AdapterType> >
630        {
631                typedef mpl::int_<2> type;
632                typedef mpl::integral_c_tag tag;
633                BOOST_STATIC_CONSTANT(unsigned int, value = version::type::value);                                                             
634        };
635}
636}
Note: See TracBrowser for help on using the repository browser.