source: branch_0_3_1/src/HaliteSortListViewCtrl.hpp @ 485

Revision 485, 13.7 KB checked in by Eoin, 12 years ago (diff)

Going to release snapshot 0.3.1. Disabled standalone torrent xml files for this release.

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 SafeLoadFromIni()
198        {
199                std::vector<wstring> listNames;
200                std::vector<int> listWidths;
201                std::vector<int> listOrder;
202                std::vector<bool> listVisible;
203
204                listNames.assign(listNames_.begin(), listNames_.end());
205                listWidths.assign(listWidths_.begin(), listWidths_.end());
206                listOrder.assign(listOrder_.begin(), listOrder_.end());
207                listVisible.assign(listVisible_.begin(), listVisible_.end());
208
209                TBase* pT = static_cast<TBase*>(this);
210                if (!pT->load_from_ini() || !vectorSizePreConditions())
211                {
212                        listNames_.assign(listNames.begin(), listNames.end());
213                        listWidths_.assign(listWidths.begin(), listWidths.end());
214                        listOrder_.assign(listOrder.begin(), listOrder.end());
215                        listVisible_.assign(listVisible.begin(), listVisible.end());
216                }               
217        }
218       
219        void ApplyDetails()
220        {
221                vectorSizePreConditions();
222               
223                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
224               
225                if (!menu_)
226                {
227                        menu_.CreatePopupMenu();
228                }
229                else
230                {                               
231                        minfo.fMask = MIIM_SUBMENU;
232                        minfo.fType = MFT_SEPARATOR;
233                       
234                        menu_.InsertMenuItem(menu_.GetMenuItemCount(), true, &minfo);           
235                }
236
237                minfo.fMask = MIIM_STRING|MIIM_ID|MIIM_FTYPE|MIIM_STATE;
238                minfo.fType = MFT_STRING;
239                minfo.fState = autoSort_ ? MFS_CHECKED : MFS_UNCHECKED;
240                minfo.wID = ID_LVM_AUTOSORT;
241               
242                wstring autoarrange = hal::app().res_wstr(HAL_AUTOSORT);
243                minfo.dwTypeData = (LPWSTR)autoarrange.c_str();
244               
245                menu_.InsertMenuItem(menu_.GetMenuItemCount(), true, &minfo);
246               
247                header_.Attach(this->GetHeader());
248                header_.ModifyStyle(0, HDS_DRAGDROP|HDS_FULLDRAG);
249
250                header_.Menu().CreatePopupMenu();
251
252                for (int i=header_.GetItemCount(), e=int(listNames_.size()); i<e; ++i)
253                {
254                        MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
255                        minfo.fMask = MIIM_STRING|MIIM_ID|MIIM_FTYPE|MIIM_STATE;
256                        minfo.fType = MFT_STRING;
257                        minfo.dwTypeData = (LPWSTR)listNames_[i].c_str();
258                        minfo.wID = CHaliteHeaderCtrl::COL_MENU_NAMES+i;
259
260                        AddColumn(listNames_[i].c_str(), i);
261
262                        if (listVisible_[i])
263                        {
264                                minfo.fState = MFS_CHECKED;                     
265                                SetColumnWidth(i, listWidths_[i]);
266                        }
267                        else
268                        {
269                                minfo.fState = MFS_UNCHECKED;
270                                SetColumnWidth(i, 0);
271                        }
272
273                        header_.Menu().InsertMenuItem(header_.Menu().GetMenuItemCount(), false, &minfo);
274                }
275               
276                SetColumnOrderArray(listNames_.size(), &listOrder_[0]);
277               
278                m_bSortDescending = descending_;
279                if (sortCol_ >= 0 && sortCol_ < m_arrColSortType.GetSize())
280                        SetSortColumn(sortCol_);
281        }
282       
283        void GetListViewDetails()
284        {
285                vectorSizePreConditions();             
286               
287                for (size_t i=0; i<listNames_.size(); ++i)
288                {
289                        if (listVisible_[i])
290                                listWidths_[i] = GetColumnWidth(i);
291                }
292               
293                GetColumnOrderArray(listNames_.size(), &listOrder_[0]);
294               
295                sortCol_ = GetSortColumn();
296                descending_ = IsSortDescending();       
297        }
298       
299        LRESULT OnAutoSort(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
300        {
301                autoSort_ = !autoSort_;
302               
303                MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
304               
305                minfo.fMask = MIIM_STATE;
306                minfo.fState = autoSort_ ? MFS_CHECKED : MFS_UNCHECKED;
307               
308                menu_.SetMenuItemInfo(ID_LVM_AUTOSORT, false, &minfo);
309               
310                return 0;
311        }
312
313        bool OnNameChecked(int i)
314        {
315                if (!listVisible_[i])
316                {               
317                        GetColumnOrderArray(listNames_.size(), &listOrder_[0]);
318                        SetColumnWidth(i, listWidths_[i]);
319
320                        listOrder_.erase(std::find(listOrder_.begin(), listOrder_.end(), i));
321                       
322                        int index = i + std::count(listVisible_.begin()+i, listVisible_.end(), false) - 1;
323                        listOrder_.insert(listOrder_.begin()+index, i);
324
325                        SetColumnOrderArray(listNames_.size(), &listOrder_[0]);
326                        listVisible_[i] = true;
327                }
328                else
329                {
330                        listWidths_[i] = GetColumnWidth(i);     
331                        GetColumnOrderArray(listNames_.size(), &listOrder_[0]);
332
333                        SetColumnWidth(i, 0);
334
335                        listOrder_.erase(std::find(listOrder_.begin(), listOrder_.end(), i));
336                        listOrder_.insert(listOrder_.begin(), i);
337
338                        SetColumnOrderArray(listNames_.size(), &listOrder_[0]);
339                        listVisible_[i] = false;
340                }
341       
342                InvalidateRect(NULL, true);
343                return listVisible_[i];
344        }
345
346        LRESULT OnClick(int, LPNMHDR pnmh, BOOL&)
347        {
348                return 0;
349        }
350
351        LRESULT OnItemChanged(int, LPNMHDR pnmh, BOOL&)
352        {               
353                hal::try_update_lock<thisClass> lock(*this);
354               
355                if (lock) manager_.sync_list(true, true);
356               
357                return 0;
358        }
359
360        LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
361        {
362                LPNMITEMACTIVATE pia = (LPNMITEMACTIVATE)pnmh;
363                manager_.sync_list(true);
364               
365                if (menu_)
366                {
367                        assert (menu_.IsMenu());
368       
369                        POINT ptPoint;
370                        GetCursorPos(&ptPoint);
371                        menu_.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
372                }
373
374                return 0;
375        }
376
377        LRESULT OnColClick(int i, LPNMHDR pnmh, BOOL&)
378        {
379                LPNMLISTVIEW pnlv = (LPNMLISTVIEW)pnmh;
380               
381                MessageBox((lexical_cast<wstring>(pnlv->iSubItem)).c_str(), L"Hi", 0);
382                return 0;
383        }
384       
385        void SetColumnSortType(int iCol, WORD wType, ColumnAdapter* colAdapter=NULL)
386        {
387                parentClass::SetColumnSortType(iCol, wType);
388               
389                if (LVCOLSORT_CUSTOM == wType)
390                        regColumnAdapter(iCol, colAdapter);
391        }
392       
393        friend class boost::serialization::access;
394    template<class Archive>
395    void serialize(Archive & ar, const unsigned int version)
396    {
397                using boost::serialization::make_nvp;
398                if (version >= 1)
399                {
400                        ar & make_nvp("width", listWidths_);
401                        ar & make_nvp("order", listOrder_);
402                        ar & make_nvp("visible", listVisible_);
403                        ar & make_nvp("autoSort", autoSort_);
404                }
405                if (version >= 2)
406                {
407                        ar & make_nvp("descending", descending_);
408                        ar & make_nvp("sortCol", sortCol_);
409                }
410    }
411
412        const SelectionManager& manager() { return manager_; }
413               
414        std::vector<int>& listColumnWidth() { return listColumnWidth_; }
415        std::vector<int>& listColumnOrder() { return listColumnOrder_; }
416       
417        const std::vector<int>& listColumnWidth() const { return listColumnWidth_; }
418        const std::vector<int>& listColumnOrder() const { return listColumnOrder_; }
419       
420        bool canUpdate() const { return updateLock_ == 0; }
421       
422        void clearFocused() { manager_.clear(); }
423        void clearSelected() { manager_.clear_all_selected(); }
424        void clearAll() { manager_.clear_all(); }
425       
426        int CompareItemsCustom(LVCompareParam* pItem1, LVCompareParam* pItem2, int iSortCol)
427        {
428                hal::mutex_update_lock<thisClass> lock(*this);
429               
430                TBase* pT = static_cast<TBase*>(this);
431               
432                adapterType left = pT->CustomItemConversion(pItem1, iSortCol);
433                adapterType right = pT->CustomItemConversion(pItem2, iSortCol);
434               
435                return pT->CustomItemComparision(left, right, iSortCol);
436        }
437       
438        bool autoSort() { return autoSort_; }
439       
440        void ConditionallyDoAutoSort()
441        {
442                int iCol = GetSortColumn();
443                if (autoSort() && iCol >= 0 && iCol < m_arrColSortType.GetSize())
444                        DoSortItems(iCol, IsSortDescending()); 
445        }
446               
447        ColumnAdapter* getColumnAdapter(size_t index)
448        {
449                boost::ptr_map<size_t, ColumnAdapter>::iterator
450                        i = columnAdapters_.find(index);
451       
452                if (i != columnAdapters_.end())
453                {
454                        return i->second;
455                }               
456                return NULL;
457        }
458
459        static bool is_selected (const winstl::listview_sequence::sequence_value_type& v) 
460        { 
461                return (v.state() & LVIS_SELECTED) != 0; 
462        }
463
464protected:     
465        inline void* CustomItemConversion(LVCompareParam* param, int iSortCol)
466        {
467                assert(false);
468                return NULL;
469        }
470       
471        int CustomItemComparision(adapterType left, adapterType right, int iSortCol)
472        {
473                ColumnAdapter* pCA = getColumnAdapter(iSortCol);
474               
475                if (pCA)
476                        return pCA->compare(left, right);
477                else 
478                        return 0;
479        }
480       
481        void regColumnAdapter(size_t key, ColumnAdapter* colAdapter)
482        {
483                assert (colAdapter);
484                columnAdapters_.insert(key, colAdapter);
485        }
486       
487        SelectionManager manager_;
488       
489private:
490        bool vectorSizePreConditions()
491        {
492                bool ret = (listNames_.size() == listWidths_.size()) &&
493                        (listNames_.size() == listOrder_.size()) &&
494                        (listNames_.size() == listVisible_.size());
495
496                assert(ret);
497                return ret;
498        }
499       
500        WTL::CMenu menu_;
501        CHaliteHeaderCtrl header_;     
502       
503        std::vector<wstring> listNames_;
504        std::vector<int> listWidths_;
505        std::vector<int> listOrder_;
506        std::vector<bool> listVisible_;
507        bool autoSort_;
508        bool descending_;
509        int sortCol_;
510       
511        mutable int update_lock_;
512        mutable hal::mutex_t mutex_;
513
514        friend class hal::mutex_update_lock<thisClass>; 
515        friend class hal::try_update_lock<thisClass>;           
516       
517        boost::ptr_map<size_t, ColumnAdapter> columnAdapters_;
518       
519        WinAPIWaitableTimer syncTimer_;
520};
521
522template<>
523inline const std::wstring hal::to_wstr_shim<const winstl::listview_sequence::sequence_value_type>
524        (const winstl::listview_sequence::sequence_value_type& v)
525{
526        return std::wstring(winstl::c_str_ptr(v));
527}
528
529
530namespace boost {
531namespace serialization {
532template <class TBase, typename adapterType>
533struct version< CHaliteSortListViewCtrl<TBase, adapterType> >
534{
535    typedef mpl::int_<2> type;
536    typedef mpl::integral_c_tag tag;
537    BOOST_STATIC_CONSTANT(unsigned int, value = version::type::value);                                                             
538};
539}
540}
Note: See TracBrowser for help on using the repository browser.