source: src/HaliteSortListViewCtrl.hpp @ 259

Revision 259, 17.1 KB checked in by Eoin, 13 years ago (diff)

Overview Tab completely reworking.

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