source: src/HaliteSortListViewCtrl.hpp @ 252

Revision 252, 14.5 KB checked in by Eoin, 13 years ago (diff)
Line 
1
2#pragma once
3
4#include <boost/array.hpp>
5#include <boost/signals.hpp>
6#include <boost/algorithm/string/split.hpp>
7#include <boost/serialization/vector.hpp>
8#include <boost/serialization/split_free.hpp>
9#include <boost/ptr_container/ptr_map.hpp>
10
11#include "stdAfx.hpp"
12#include "halTorrent.hpp"
13#include "WinAPIWaitableTimer.hpp"
14
15template<class T>
16class UpdateLock
17{
18public:
19        UpdateLock(T& window) :
20                window_(window)
21        {
22                ++window_.updateLock_;
23                window_.SetRedraw(false);
24        }
25       
26        ~UpdateLock()
27        {
28                if (!--window_.updateLock_)
29                        unlock();
30        }
31       
32        void unlock()
33        {
34                window_.SetRedraw(true);
35                window_.InvalidateRect(NULL, true);
36        }
37       
38private:
39        T& window_;
40};
41
42template <class TBase, typename adapterType=void*, size_t N=-1>
43class CHaliteSortListViewCtrl : 
44        public CSortListViewCtrlImpl<CHaliteSortListViewCtrl<TBase, adapterType, N> >
45{
46public:
47        typedef CHaliteSortListViewCtrl<TBase, adapterType, N> thisClass;
48        typedef CSortListViewCtrlImpl<thisClass> parentClass;
49       
50        class selection_manager : 
51                private boost::noncopyable
52        {       
53        public:
54                selection_manager(thisClass& m_list) :
55                        m_list_(m_list)
56                {}
57               
58                typedef std::wstring string_t; 
59                typedef const string_t& param_type;
60               
61                void sync_list(bool list_to_manager, bool signal_change=true)
62                {
63                        if (list_to_manager)
64                        {       
65                                if (listToManager() && signal_change) signal();
66                        }
67                        else
68                        {
69                                if (managerToList() && signal_change) signal();
70                        }
71                }
72               
73                bool listToManager()
74                {
75                        boost::array<wchar_t, MAX_PATH> pathBuffer;
76                        std::set<string_t> all_selected;
77                        string_t selected = L"";
78                       
79                        bool do_signal = false;
80                       
81                        int total = m_list_.GetItemCount();
82                       
83                        for (int i=0; i<total; ++i)
84                        {
85                                UINT flags = m_list_.GetItemState(i, LVIS_SELECTED);
86                               
87                                if (flags && LVIS_SELECTED)
88                                {
89                                        m_list_.GetItemText(i, 0, pathBuffer.c_array(), pathBuffer.size());     
90                                        all_selected.insert(pathBuffer.data());
91                                }
92                                if (flags && LVIS_FOCUSED)
93                                {
94                                        selected = pathBuffer.data();
95                                }
96                        }
97
98                        if (all_selected != all_selected_)
99                        {
100                                std::swap(all_selected_, all_selected);
101                                do_signal = true;
102                        }
103                                       
104                        if (selected_ != selected)
105                        {
106                                std::swap(selected_, selected);
107                                do_signal = true;
108                        }
109                       
110                        return do_signal;
111                }
112               
113                bool managerToList()
114                {
115                        // Prevent changing states from signaling another sync
116                        UpdateLock<thisClass> lock(m_list_);
117                       
118                        boost::array<wchar_t, MAX_PATH> pathBuffer;
119                        LV_FINDINFO findInfo = { sizeof(LV_FINDINFO) }; 
120                        findInfo.flags = LVFI_STRING;
121                       
122                        bool do_signal = true; // Always signal for now!
123                       
124                        int total = m_list_.GetItemCount();
125                       
126                        for (int i=0; i<total; ++i)
127                        {
128                                m_list_.GetItemText(i, 0, pathBuffer.c_array(), pathBuffer.size());
129                                string_t temp_name = pathBuffer.data();
130                               
131                                LVITEM lvi = { LVIF_STATE };
132                                lvi.state = 0;
133                                lvi.stateMask = LVIS_SELECTED|LVIS_FOCUSED;
134                               
135                                if (temp_name == selected_)
136                                {
137                                        lvi.state |= LVIS_FOCUSED;
138                                }
139                                if (all_selected_.find(temp_name) != all_selected_.end())
140                                {
141                                        lvi.state |= LVIS_SELECTED;
142                                }
143                               
144                                m_list_.SetItemState(i, &lvi);
145                        }                       
146                       
147                        return do_signal;
148                }
149               
150                int selectMatch(const string_t& name)
151                {
152                        LV_FINDINFO findInfo = { sizeof(LV_FINDINFO) }; 
153                        findInfo.flags = LVFI_STRING;
154                                       
155                        findInfo.psz = name.c_str();
156                       
157                        int itemPos = m_list_.FindItem(&findInfo, -1); 
158                                               
159                        if (itemPos == -1)
160                                return itemPos;
161                               
162                        UINT flags = m_list_.GetItemState(itemPos, LVIS_SELECTED);
163                       
164                        //if (!flags && LVIS_SELECTED)
165                        {
166                                LVITEM lvi = { LVIF_STATE };
167                                lvi.state = LVIS_SELECTED;
168                                lvi.stateMask = LVIS_SELECTED;
169                                m_list_.SetItemState(itemPos, &lvi);
170                        }
171               
172                        return itemPos;
173                }
174               
175                param_type selected() const { return selected_; }
176               
177                int selectedIndex()
178                {
179                        LV_FINDINFO findInfo = { sizeof(LV_FINDINFO) };         
180                        findInfo.psz = selected_.c_str();
181                       
182                        return m_list_.FindItem(&findInfo, -1);         
183                }
184               
185                const std::set<string_t>& allSelected() const { return all_selected_; }
186               
187                void setSelected(const string_t& sel) 
188                {
189                        selected_ = sel;
190                }
191               
192                void setSelected(int itemPos)
193                {               
194                        hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"Set Selected %1%") % itemPos).str().c_str())));
195
196                        LVITEM lvi = { LVIF_STATE };
197                        lvi.state = LVIS_SELECTED|LVIS_FOCUSED;
198                        lvi.stateMask = LVIS_SELECTED|LVIS_FOCUSED;
199                        m_list_.SetItemState(itemPos, &lvi);
200                       
201                        m_list_.SetSelectionMark(itemPos);
202                       
203                        sync_list(true);
204                }
205               
206                void clear()
207                {
208                        // Prevent changing states from signaling another sync
209                        UpdateLock<thisClass> lock(m_list_);
210                       
211//                      hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"Clear")).str().c_str())));
212       
213                        m_list_.DeleteItem(selectedIndex());
214                       
215                        sync_list(true);       
216                }
217               
218                void clear_all_selected()
219                {
220                        // Prevent changing states from signaling another sync
221                        UpdateLock<thisClass> lock(m_list_);
222                       
223//                      hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"ClearAllSelected")).str().c_str())));
224
225                        int total = m_list_.GetItemCount();
226                       
227                        for (int i=total-1; i>=0; --i)
228                        {
229                                UINT flags = m_list_.GetItemState(i, LVIS_SELECTED);
230                               
231                                if (flags && LVIS_SELECTED)
232                                        m_list_.DeleteItem(i);
233                        }
234                        all_selected_.clear();
235                       
236                        sync_list(true);       
237                }
238               
239                void clear_all()
240                {
241                        // Prevent changing states from signaling another sync
242                        UpdateLock<thisClass> lock(m_list_);
243                       
244//                      hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"ClearAll")).str().c_str())));
245
246                        m_list_.DeleteAllItems();
247                        all_selected_.clear();
248                       
249                        sync_list(true);               
250                }
251               
252                void attach(boost::function<void (param_type)> fn) const { selection_.connect(fn); }
253               
254                void signal() 
255                { 
256                        selection_(selected_); 
257                }
258               
259        private:
260                string_t selected_;
261                std::set<string_t> all_selected_;
262               
263                mutable boost::signal<void (param_type)> selection_;
264                thisClass& m_list_;
265        };
266       
267        class CHaliteHeaderCtrl : public CWindowImpl<CHaliteHeaderCtrl, CHeaderCtrl>
268        {
269        public:
270                BEGIN_MSG_MAP(CHaliteHeaderCtrl)
271                        REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRClick)
272                END_MSG_MAP()
273               
274                void Attach(HWND hWndNew)
275                {
276                        ATLASSERT(::IsWindow(hWndNew));
277                        CWindowImpl<CHaliteHeaderCtrl, CHeaderCtrl>::SubclassWindow(hWndNew);
278
279                        menu_.CreatePopupMenu();
280                       
281                        MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
282                        minfo.fMask = MIIM_STRING|MIIM_ID|MIIM_FTYPE|MIIM_STATE;
283                        minfo.fType = MFT_STRING;
284                        minfo.fState = MFS_CHECKED;
285                        minfo.dwTypeData = L"Hello";
286                       
287                        menu_.InsertMenuItem(0, false, &minfo);
288                }
289               
290                LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
291                {
292                        POINT ptPoint;
293                        GetCursorPos(&ptPoint);
294                        menu_.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
295
296                        return 0;
297                }
298               
299        private:
300                WTL::CMenu menu_;
301        };
302       
303        struct ColumnAdapter
304        {
305                virtual bool less(adapterType& l, adapterType& r) = 0;
306                virtual std::wstring print(adapterType& t) = 0;
307        };
308
309public:
310        typedef selection_manager SelectionManager;
311        typedef SelectionManager selection_manager_class;
312       
313        thisClass(bool resMenu=true, bool resNames=true, bool resWidthsAndOrder=true) :
314                manager_(*this),
315                updateLock_(0)
316        {
317                if (resMenu && TBase::LISTVIEW_ID_MENU)
318                {
319                        BOOL menu_created = menu_.LoadMenu(TBase::LISTVIEW_ID_MENU);
320                        assert(menu_created);   
321                }
322
323                if (resNames)
324                {
325                        wstring column_names = hal::app().res_wstr(TBase::LISTVIEW_ID_COLUMNNAMES);
326                        boost::split(listNames_, column_names, boost::is_any_of(L";"));
327                }
328               
329                if (resWidthsAndOrder)
330                {
331                        wstring column_widths = hal::app().res_wstr(TBase::LISTVIEW_ID_COLUMNWIDTHS);
332                        std::vector<wstring> widths;
333                        boost::split(widths, column_widths, boost::is_any_of(L";"));
334                       
335                        listWidths_.reserve(listNames_.size()); 
336                        listOrder_.reserve(listNames_.size());
337                       
338                        for (size_t i=0; i<listNames_.size(); ++i)
339                        {
340                                listWidths_.push_back(lexical_cast<int>(widths[i]));
341                                listOrder_.push_back(i);
342                        }
343                }
344        }
345
346        BEGIN_MSG_MAP_EX(thisClass)
347        //      REFLECTED_NOTIFY_CODE_HANDLER(NM_CLICK, OnClick)
348                REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRClick)
349                REFLECTED_NOTIFY_CODE_HANDLER(LVN_ITEMCHANGED, OnItemChanged)
350        //      REFLECTED_NOTIFY_CODE_HANDLER(LVN_COLUMNCLICK , OnColClick)
351
352                DEFAULT_REFLECTION_HANDLER()
353                CHAIN_MSG_MAP(parentClass)
354        END_MSG_MAP()
355
356        void Attach(HWND hWndNew)
357        {
358                ATLASSERT(::IsWindow(hWndNew));
359        parentClass::SubclassWindow(hWndNew);
360
361                TBase* pT = static_cast<TBase*>(this);
362                pT->OnAttach();
363        }
364       
365        void SetListViewDetails()
366        {
367                vectorSizePreConditions();
368               
369                header_.Attach(this->GetHeader());
370                header_.ModifyStyle(0, HDS_DRAGDROP|HDS_FULLDRAG);
371                       
372                foreach (wstring name, listNames_)
373                {
374                        int i = header_.GetItemCount();
375                       
376                        AddColumn(name.c_str(), i);
377                }               
378
379                for (unsigned i=0; i<listNames_.size(); ++i)
380                        SetColumnWidth(i, listOrder_[i]);
381               
382                SetColumnOrderArray(listOrder_.size(), &listOrder_[0]); 
383        }
384       
385        template<typename N, typename W, typename O, typename P>
386        void SetDefaults(N nameList, W widthList, O orderList, P visibleList)
387        {
388                listNames_.assign(nameList.begin(), nameList.end());
389                listWidths_.assign(widthList.begin(), widthList.end());
390                listOrder_.assign(orderList.begin(), orderList.end());
391                listVisible_.assign(visibleList.begin(), visibleList.end());
392        }
393       
394        template<std::size_t Size>
395        void SetDefaults(array<int, Size> a)
396        {
397                assert (Size == listNames_.size());
398                vectorSizePreConditions();
399               
400                for (size_t i=0; i<listNames_.size(); ++i)
401                {
402                        listWidths_[i] = a[i];
403                        listOrder_[i] = i;
404                }               
405        }
406       
407        void ApplyDetails()
408        {
409                vectorSizePreConditions();
410               
411                header_.Attach(this->GetHeader());
412                header_.ModifyStyle(0, HDS_DRAGDROP|HDS_FULLDRAG);
413               
414                for (int i = header_.GetItemCount(); i<listNames_.size(); i = header_.GetItemCount())
415                {
416                        AddColumn(listNames_[i].c_str(), i);
417                        SetColumnWidth(i, listWidths_[i]);
418                }
419               
420                SetColumnOrderArray(listNames_.size(), &listOrder_[0]);
421        }
422       
423        void GetListViewDetails()
424        {
425                vectorSizePreConditions();             
426               
427                for (size_t i=0; i<listNames_.size(); ++i)
428                {
429                        listWidths_[i] = GetColumnWidth(i);
430                }
431               
432                GetColumnOrderArray(listNames_.size(), &listOrder_[0]);
433        }
434
435        LRESULT OnClick(int, LPNMHDR pnmh, BOOL&)
436        {
437                return 0;
438        }
439
440        LRESULT OnItemChanged(int, LPNMHDR pnmh, BOOL&)
441        {               
442                if (canUpdate()) 
443                {
444                        if (syncTimer_.reset(50, 0, bind(&thisClass::syncTimeout, this)))
445                        {
446                        //      hal::event().post(shared_ptr<hal::EventDetail>
447                        //              (new hal::EventDebug(hal::Event::info, (wformat(L"Set")).str().c_str())));
448                        }
449                }
450               
451                return 0;
452        }
453
454        LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
455        {
456                //hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"RClick %1%") % pnmh->code).str().c_str())));
457                LPNMITEMACTIVATE pia = (LPNMITEMACTIVATE)pnmh;
458                manager_.sync_list(true);
459               
460                if (TBase::LISTVIEW_ID_MENU)
461                {
462                        assert (menu_.IsMenu());
463                        CMenuHandle sMenu = menu_.GetSubMenu(0);
464                        assert (sMenu.IsMenu());
465       
466                        POINT ptPoint;
467                        GetCursorPos(&ptPoint);
468                        sMenu.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
469                }
470
471                return 0;
472        }
473
474        LRESULT OnColClick(int i, LPNMHDR pnmh, BOOL&)
475        {
476                LPNMLISTVIEW pnlv = (LPNMLISTVIEW)pnmh;
477               
478                MessageBox((lexical_cast<wstring>(pnlv->iSubItem)).c_str(), L"Hi", 0);
479                return 0;
480        }
481       
482        void SetColumnSortType(int iCol, WORD wType, ColumnAdapter* colAdapter=NULL)
483        {
484                parentClass::SetColumnSortType(iCol, wType);
485               
486                if (LVCOLSORT_CUSTOM == wType)
487                        regColumnAdapter(iCol, colAdapter);
488        }
489       
490        friend class boost::serialization::access;
491    template<class Archive>
492    void serialize(Archive & ar, const unsigned int version)
493    {
494        ar & boost::serialization::make_nvp("width", listWidths_);
495        ar & boost::serialization::make_nvp("order", listOrder_);
496    }
497
498        const SelectionManager& manager() { return manager_; }
499               
500        std::vector<int>& listColumnWidth() { return listColumnWidth_; }
501        std::vector<int>& listColumnOrder() { return listColumnOrder_; }
502       
503        const std::vector<int>& listColumnWidth() const { return listColumnWidth_; }
504        const std::vector<int>& listColumnOrder() const { return listColumnOrder_; }
505       
506        bool canUpdate() const { return updateLock_ == 0; }
507       
508        void clearFocused() { manager_.clear(); }
509        void clearSelected() { manager_.clear_all_selected(); }
510        void clearAll() { manager_.clear(); }
511       
512        int CompareItemsCustom(LVCompareParam* pItem1, LVCompareParam* pItem2, int iSortCol)
513        {
514                //return 1;
515                //hal::mutex_t::scoped_lock l(mutex_);
516               
517                TBase* pT = static_cast<TBase*>(this);
518               
519                adapterType left = pT->CustomItemConversion(pItem1, iSortCol);
520                adapterType right = pT->CustomItemConversion(pItem2, iSortCol);
521               
522                return pT->CustomItemComparision(left, right, iSortCol);
523        }
524       
525protected:     
526        inline void* CustomItemConversion(LVCompareParam* param, int iSortCol)
527        {
528                assert(false);
529                return NULL;
530        }
531       
532        int CustomItemComparision(adapterType left, adapterType right, int iSortCol)
533        {
534                ColumnAdapter* pCA = getColumnAdapter(iSortCol);
535               
536                if (pCA)
537                        return (pCA->less(left, right)) ? 1 : -1;
538                else 
539                        return 0;
540        }
541       
542        void regColumnAdapter(size_t key, ColumnAdapter* colAdapter)
543        {
544                assert (colAdapter);
545                columnAdapters_.insert(key, colAdapter);
546        }
547       
548        ColumnAdapter* getColumnAdapter(size_t index)
549        {
550                boost::ptr_map<size_t, ColumnAdapter>::iterator
551                        i = columnAdapters_.find(index);
552       
553                if (i != columnAdapters_.end())
554                {
555                        return i->second;
556                }               
557                return NULL;
558        }
559       
560        SelectionManager manager_;
561
562        //hal::mutex_t mutex_;
563       
564private:
565        void vectorSizePreConditions()
566        {
567/*              if (listColumnWidth_.size() != names_.size())
568                {
569                        listColumnWidth_.clear();
570                        listColumnWidth_.insert(listColumnWidth_.end(), names_.size(), 50);     
571                }
572               
573                if (listColumnOrder_.size() != names_.size())
574                {               
575                        listColumnOrder_.clear();
576                        listColumnOrder_.insert(listColumnOrder_.end(), names_.size(), 0);
577                }
578*/
579        }
580       
581        void syncTimeout()
582        {
583        //      hal::event().post(shared_ptr<hal::EventDetail>
584        //              (new hal::EventDebug(hal::Event::info, (wformat(L"Signaled")).str().c_str())));
585               
586                manager_.sync_list(true, true);
587        }
588       
589        WTL::CMenu menu_;
590        CHaliteHeaderCtrl header_;     
591       
592        std::vector<wstring> listNames_;
593        std::vector<int> listWidths_;
594        std::vector<int> listOrder_;
595        std::vector<bool> listVisible_;
596       
597        int updateLock_;
598        friend class UpdateLock<thisClass>;             
599       
600        boost::ptr_map<size_t, ColumnAdapter> columnAdapters_;
601       
602        WinAPIWaitableTimer syncTimer_;
603};
Note: See TracBrowser for help on using the repository browser.