source: src/HaliteSortListViewCtrl.hpp @ 253

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