source: trunk/src/HaliteSortListViewCtrl.hpp @ 503

Revision 503, 13.8 KB checked in by Eoin, 11 years ago (diff)

Cleaned up trunk alert handler code.

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