source: src/HaliteListViewCtrl2.hpp @ 241

Revision 241, 12.6 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
10#include "stdAfx.hpp"
11#include "halTorrent.hpp"
12
13template<class T>
14class UpdateLock
15{
16public:
17        UpdateLock(T& window) :
18                window_(window)
19        {
20                ++window_.updateLock_;
21                window_.SetRedraw(false);
22        }
23       
24        ~UpdateLock()
25        {
26                if (!--window_.updateLock_)
27                        unlock();
28        }
29       
30        void unlock()
31        {
32                window_.SetRedraw(true);
33                window_.InvalidateRect(NULL, true);
34        }
35       
36private:
37        T& window_;
38};
39
40template <class TBase>
41class CHaliteListViewCtrl2 : public CWindowImpl<TBase, CListViewCtrl>
42{
43public:
44        typedef CHaliteListViewCtrl2<TBase> thisClass;
45       
46        template <typename L, typename S=std::string>
47        class selection_manager : 
48                private boost::noncopyable
49        {       
50        public:
51                selection_manager(L& m_list) :
52                        m_list_(m_list)
53                {}
54               
55                typedef const S& param_type;
56               
57                void sync_list(bool list_to_manager, bool signal_change=true)
58                {
59//                      hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"  A: %1%, %2% %3%") % hal::from_utf8(selected_) % list_to_manager % signal_change).str().c_str())));
60
61                        if (list_to_manager)
62                        {       
63                                if (listToManager() && signal_change) signal();
64                        }
65                        else
66                        {
67                                if (managerToList() && signal_change) signal();
68                        }
69                       
70//                      hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"  Z: %1%, %2% %3%") % hal::from_utf8(selected_) % list_to_manager % signal_change).str().c_str())));
71                }
72               
73                bool listToManager()
74                {
75                        boost::array<wchar_t, MAX_PATH> pathBuffer;
76                        std::set<S> all_selected;
77                        string selected = "";
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(hal::to_utf8(pathBuffer.data()));
91                                }
92                                if (flags && LVIS_FOCUSED)
93                                {
94                                        selected = hal::to_utf8(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<L> 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 temp_name = hal::to_utf8(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& name)
151                {
152                        LV_FINDINFO findInfo = { sizeof(LV_FINDINFO) }; 
153                        findInfo.flags = LVFI_STRING;
154                       
155                        wstring torrent_name = hal::from_utf8(name);           
156                        findInfo.psz = torrent_name.c_str();
157                       
158                        int itemPos = m_list_.FindItem(&findInfo, -1); 
159                       
160//                      hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"Two: %1%, %2%") % torrent_name % itemPos).str().c_str())));
161                       
162                        if (itemPos == -1)
163                                return itemPos;
164                               
165                        UINT flags = m_list_.GetItemState(itemPos, LVIS_SELECTED);
166                       
167                        //if (!flags && LVIS_SELECTED)
168                        {
169                                LVITEM lvi = { LVIF_STATE };
170                                lvi.state = LVIS_SELECTED;
171                                lvi.stateMask = LVIS_SELECTED;
172                                m_list_.SetItemState(itemPos, &lvi);
173                        }
174               
175                        return itemPos;
176                }
177               
178                param_type selected() const { return selected_; }
179               
180                int selectedIndex()
181                {
182                        wstring torrent_name = hal::from_utf8(selected_);       
183                        LV_FINDINFO findInfo = { sizeof(LV_FINDINFO) };         
184                        findInfo.psz = torrent_name.c_str();
185                       
186                        return m_list_.FindItem(&findInfo, -1);         
187                }
188               
189                const std::set<string>& allSelected() const { return all_selected_; }
190               
191                void setSelected(const string& sel) 
192                {
193                        selected_ = sel;
194                //      sync_list(false);
195                }
196               
197                void setSelected(int itemPos)
198                {               
199                        hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"Set Selected %1%") % itemPos).str().c_str())));
200
201                        LVITEM lvi = { LVIF_STATE };
202                        lvi.state = LVIS_SELECTED|LVIS_FOCUSED;
203                        lvi.stateMask = LVIS_SELECTED|LVIS_FOCUSED;
204                        m_list_.SetItemState(itemPos, &lvi);
205                       
206                        m_list_.SetSelectionMark(itemPos);
207                       
208                        sync_list(true);
209                }
210               
211                void clear()
212                {
213                        // Prevent changing states from signaling another sync
214                        UpdateLock<L> lock(m_list_);
215                       
216                        hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"Clear")).str().c_str())));
217       
218                        m_list_.DeleteItem(selectedIndex());
219                       
220                //      m_list_.SelectItem(0);
221                        sync_list(true);       
222                }
223               
224                void clear_all_selected()
225                {
226                        // Prevent changing states from signaling another sync
227                        UpdateLock<L> lock(m_list_);
228                       
229                        hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"ClearAllSelected")).str().c_str())));
230
231                        int total = m_list_.GetItemCount();
232                       
233                        for (int i=total-1; i>=0; --i)
234                        {
235                                UINT flags = m_list_.GetItemState(i, LVIS_SELECTED);
236                               
237                                if (flags && LVIS_SELECTED)
238                                        m_list_.DeleteItem(i);
239                        }
240                        all_selected_.clear();
241                       
242                //      m_list_.SelectItem(0);
243                        sync_list(true);       
244                }
245               
246                void clear_all()
247                {
248                        // Prevent changing states from signaling another sync
249                        UpdateLock<L> lock(m_list_);
250                       
251                        hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"ClearAll")).str().c_str())));
252
253                        m_list_.DeleteAllItems();
254                        all_selected_.clear();
255                       
256                        sync_list(true);               
257                }
258               
259                void attach(boost::function<void (param_type)> fn) const { selection_.connect(fn); }
260               
261                void signal() 
262                { 
263                        selection_(selected_); 
264                }
265               
266        private:
267                S selected_;
268                std::set<S> all_selected_;
269               
270                mutable boost::signal<void (param_type)> selection_;
271                L& m_list_;
272        };
273       
274        class CHaliteHeaderCtrl : public CWindowImpl<CHaliteHeaderCtrl, CHeaderCtrl>
275        {
276        public:
277                BEGIN_MSG_MAP(CHaliteHeaderCtrl)
278                        REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRClick)
279                END_MSG_MAP()
280               
281                void Attach(HWND hWndNew)
282                {
283                        ATLASSERT(::IsWindow(hWndNew));
284                        CWindowImpl<CHaliteHeaderCtrl, CHeaderCtrl>::SubclassWindow(hWndNew);
285
286                        menu_.CreatePopupMenu();
287                       
288                        MENUITEMINFO minfo = {sizeof(MENUITEMINFO)};
289                        minfo.fMask = MIIM_STRING|MIIM_ID|MIIM_FTYPE|MIIM_STATE;
290                        minfo.fType = MFT_STRING;
291                        minfo.fState = MFS_CHECKED;
292                        minfo.dwTypeData = L"Hello";
293                       
294                        menu_.InsertMenuItem(0, false, &minfo);
295//                      TBase* pT = static_cast<TBase*>(this);
296//                      pT->OnAttach();
297                }
298               
299                LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
300                {
301                        POINT ptPoint;
302                        GetCursorPos(&ptPoint);
303                        menu_.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
304
305                        return 0;
306                }
307               
308        private:
309                WTL::CMenu menu_;
310        };
311
312public:
313        typedef selection_manager<thisClass> selection_manage_class;
314       
315        enum sortDirection
316        {
317                none,
318                ascending,
319                descending
320        };
321       
322        CHaliteListViewCtrl2<TBase>() :
323                sortingDirection_(CHaliteListViewCtrl2<TBase>::none),
324                sortedColmun_(0),
325                manager_(*this),
326                updateLock_(0)
327        {
328                if (TBase::LISTVIEW_ID_MENU)
329                {
330                        BOOL menu_created = menu_.LoadMenu(TBase::LISTVIEW_ID_MENU);
331                        assert(menu_created);   
332                }
333
334                wstring column_names = hal::app().res_wstr(TBase::LISTVIEW_ID_COLUMNNAMES);
335                boost::split(names_, column_names, boost::is_any_of(L";"));
336               
337                wstring column_widths = hal::app().res_wstr(TBase::LISTVIEW_ID_COLUMNWIDTHS);
338                std::vector<wstring> widths;
339                boost::split(widths, column_widths, boost::is_any_of(L";"));
340                               
341                listColumnWidth_.reserve(names_.size());       
342                listColumnOrder_.reserve(names_.size());
343               
344                for (size_t i=0; i<names_.size(); ++i)
345                {
346                        listColumnWidth_.push_back(lexical_cast<int>(widths[i]));
347                        listColumnOrder_.push_back(i);
348                }       
349        }
350
351        BEGIN_MSG_MAP_EX(CHaliteListViewCtrl2<TBase>)
352                REFLECTED_NOTIFY_CODE_HANDLER(NM_CLICK, OnClick)
353                REFLECTED_NOTIFY_CODE_HANDLER(NM_RCLICK, OnRClick)
354                REFLECTED_NOTIFY_CODE_HANDLER(LVN_ITEMCHANGED, OnItemChanged)
355                REFLECTED_NOTIFY_CODE_HANDLER(LVN_COLUMNCLICK , OnColClick)
356
357                DEFAULT_REFLECTION_HANDLER()
358        END_MSG_MAP()
359
360        void Attach(HWND hWndNew)
361        {
362                ATLASSERT(::IsWindow(hWndNew));
363        CWindowImpl<TBase, CListViewCtrl>::SubclassWindow(hWndNew);
364
365                TBase* pT = static_cast<TBase*>(this);
366                pT->OnAttach();
367        }
368       
369        void SetListViewDetails()
370        {
371                vectorSizePreConditions();
372               
373                header_.Attach(this->GetHeader());
374                header_.ModifyStyle(0, HDS_DRAGDROP|HDS_FULLDRAG);
375                       
376                foreach (wstring name, names_)
377                {
378                        AddColumn(name.c_str(), header_.GetItemCount());
379                }               
380
381                for (unsigned i=0; i<names_.size(); ++i)
382                        SetColumnWidth(i, listColumnWidth_[i]);
383               
384                SetColumnOrderArray(names_.size(), &listColumnOrder_[0]);       
385        }
386       
387        template<std::size_t Size>
388        void SetDefaults(array<int, Size> a)
389        {
390                assert (Size == names_.size());
391                vectorSizePreConditions();
392               
393                for (size_t i=0; i<names_.size(); ++i)
394                {
395                        listColumnWidth_[i] = a[i];
396                        listColumnOrder_[i] = i;
397                }               
398        }
399       
400        // Should probably make this redundant!!
401        void GetListViewDetails()
402        {
403                vectorSizePreConditions();
404               
405                GetColumnOrderArray(names_.size(), &listColumnOrder_[0]);
406               
407                for (size_t i=0; i<names_.size(); ++i)
408                        listColumnWidth_[i] = GetColumnWidth(i);       
409        }
410
411        LRESULT OnClick(int, LPNMHDR pnmh, BOOL&)
412        {
413        //      manager().sync_list(true);
414
415                return 0;
416        }
417
418        LRESULT OnItemChanged(int, LPNMHDR pnmh, BOOL&)
419        {               
420                if (canUpdate()) 
421                {
422                        manager_.sync_list(true, true);
423                }
424               
425                return 0;
426        }
427
428
429        LRESULT OnRClick(int i, LPNMHDR pnmh, BOOL&)
430        {
431                hal::event().post(shared_ptr<hal::EventDetail>(new hal::EventDebug(hal::Event::info, (wformat(L"RClick %1%") % pnmh->code).str().c_str())));
432                LPNMITEMACTIVATE pia = (LPNMITEMACTIVATE)pnmh;
433                manager_.sync_list(true);
434               
435                if (TBase::LISTVIEW_ID_MENU)
436                {
437                        assert (menu_.IsMenu());
438                        CMenuHandle sMenu = menu_.GetSubMenu(0);
439                        assert (sMenu.IsMenu());
440       
441                        POINT ptPoint;
442                        GetCursorPos(&ptPoint);
443                        sMenu.TrackPopupMenu(0, ptPoint.x, ptPoint.y, m_hWnd);
444                }
445
446                return 0;
447        }
448
449        LRESULT OnColClick(int i, LPNMHDR pnmh, BOOL&)
450        {
451                LPNMLISTVIEW pnlv = (LPNMLISTVIEW)pnmh;
452               
453                MessageBox((lexical_cast<wstring>(pnlv->iSubItem)).c_str(), L"Hi", 0);
454                return 0;
455        }
456       
457        friend class boost::serialization::access;
458    template<class Archive>
459    void serialize(Archive & ar, const unsigned int version)
460    {
461        ar & boost::serialization::make_nvp("width", listColumnWidth_);
462        ar & boost::serialization::make_nvp("order", listColumnOrder_);
463    }
464
465        const selection_manager<CHaliteListViewCtrl2>& manager() { return manager_; }
466       
467        size_t sortedColmun() { return sortedColmun_; }
468        sortDirection sortingDirection() { return sortingDirection_; }
469       
470        std::vector<int>& listColumnWidth() { return listColumnWidth_; }
471        std::vector<int>& listColumnOrder() { return listColumnOrder_; }
472       
473        const std::vector<int>& listColumnWidth() const { return listColumnWidth_; }
474        const std::vector<int>& listColumnOrder() const { return listColumnOrder_; }
475       
476        bool canUpdate() const { return updateLock_ == 0; }
477       
478        void clearFocused() { manager_.clear(); }
479        void clearSelected() { manager_.clear_all_selected(); }
480        void clearAll() { manager_.clear(); }
481
482protected:
483        selection_manager<CHaliteListViewCtrl2> manager_;
484       
485private:
486        void vectorSizePreConditions()
487        {
488                if (listColumnWidth_.size() != names_.size())
489                {
490                        listColumnWidth_.clear();
491                        listColumnWidth_.insert(listColumnWidth_.end(), names_.size(), 50);     
492                }
493               
494                if (listColumnOrder_.size() != names_.size())
495                {               
496                        listColumnOrder_.clear();
497                        listColumnOrder_.insert(listColumnOrder_.end(), names_.size(), 0);
498                }               
499        }
500       
501        sortDirection sortingDirection_;
502        size_t sortedColmun_;
503       
504        WTL::CMenu menu_;
505        CHaliteHeaderCtrl header_;
506        std::vector<wstring> names_;
507        std::vector<int> listColumnWidth_;
508        std::vector<int> listColumnOrder_;
509       
510        int updateLock_;
511        friend class UpdateLock<thisClass>;
512};
Note: See TracBrowser for help on using the repository browser.