source: src/HaliteSortListViewCtrl.hpp @ 268

Revision 268, 17.4 KB checked in by Eoin, 13 years ago (diff)

Added copyright eventually.

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