source: trunk/src/HaliteSortListViewCtrl.hpp @ 657

Revision 657, 16.7 KB checked in by Eoin, 11 years ago (diff)

Classic sorting and external sorting not playing nice together.

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