source: filezilla/trunk/fuentes/src/interface/filelistctrl.cpp @ 3185

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 3 years ago

Update new version: 3.15.02

File size: 25.1 KB
Line 
1#ifndef FILELISTCTRL_INCLUDE_TEMPLATE_DEFINITION
2// This works around a bug in GCC, appears to be [Bug pch/12707]
3#include <filezilla.h>
4#endif
5#include "filelistctrl.h"
6#include "filezillaapp.h"
7#include "Options.h"
8#include "conditionaldialog.h"
9#include <algorithm>
10#include "filelist_statusbar.h"
11#if defined(__WXGTK__) && !defined(__WXGTK3__)
12#include <gtk/gtk.h>
13#endif
14
15#ifndef __WXMSW__
16DECLARE_EVENT_TYPE(fz_EVT_FILELIST_FOCUSCHANGE, -1)
17DECLARE_EVENT_TYPE(fz_EVT_DEFERRED_MOUSEEVENT, -1)
18#ifndef FILELISTCTRL_INCLUDE_TEMPLATE_DEFINITION
19DEFINE_EVENT_TYPE(fz_EVT_FILELIST_FOCUSCHANGE)
20DEFINE_EVENT_TYPE(fz_EVT_DEFERRED_MOUSEEVENT)
21#endif
22#endif
23
24BEGIN_EVENT_TABLE_TEMPLATE1(CFileListCtrl, wxListCtrlEx, CFileData)
25EVT_LIST_COL_CLICK(wxID_ANY, CFileListCtrl<CFileData>::OnColumnClicked)
26EVT_LIST_COL_RIGHT_CLICK(wxID_ANY, CFileListCtrl<CFileData>::OnColumnRightClicked)
27EVT_LIST_ITEM_SELECTED(wxID_ANY, CFileListCtrl<CFileData>::OnItemSelected)
28EVT_LIST_ITEM_DESELECTED(wxID_ANY, CFileListCtrl<CFileData>::OnItemDeselected)
29#ifndef __WXMSW__
30EVT_LIST_ITEM_FOCUSED(wxID_ANY, CFileListCtrl<CFileData>::OnFocusChanged)
31EVT_COMMAND(wxID_ANY, fz_EVT_FILELIST_FOCUSCHANGE, CFileListCtrl<CFileData>::OnProcessFocusChange)
32EVT_LEFT_DOWN(CFileListCtrl<CFileData>::OnLeftDown)
33EVT_COMMAND(wxID_ANY, fz_EVT_DEFERRED_MOUSEEVENT, CFileListCtrl<CFileData>::OnProcessMouseEvent)
34#endif
35EVT_KEY_DOWN(CFileListCtrl<CFileData>::OnKeyDown)
36END_EVENT_TABLE()
37
38#ifdef __WXMSW__
39// wxWidgets does not handle LVN_ODSTATECHANGED, work around it
40
41#pragma pack(push, 1)
42typedef struct fz_tagNMLVODSTATECHANGE
43{
44        NMHDR hdr;
45        int iFrom;
46        int iTo;
47        UINT uNewState;
48        UINT uOldState;
49} fzNMLVODSTATECHANGE;
50#pragma pack(pop)
51
52// MinGW lacks these constants and macros
53#ifndef LVN_MARQUEEBEGIN
54#define LVN_MARQUEEBEGIN (LVN_FIRST-56)
55#endif
56#ifndef APPCOMMAND_BROWSER_FORWARD
57#define APPCOMMAND_BROWSER_FORWARD 2
58#endif
59#ifndef APPCOMMAND_BROWSER_BACKWARD
60#define APPCOMMAND_BROWSER_BACKWARD 1
61#endif
62#ifndef GET_APPCOMMAND_LPARAM
63#define GET_APPCOMMAND_LPARAM(x) (HIWORD(x)&~0xF000)
64#endif
65
66template<class CFileData> WXLRESULT CFileListCtrl<CFileData>::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
67{
68        if (nMsg == WM_APPCOMMAND) {
69                DWORD cmd = GET_APPCOMMAND_LPARAM(lParam);
70                if (cmd == APPCOMMAND_BROWSER_FORWARD) {
71                        OnNavigationEvent(true);
72                        return true;
73                }
74                else if (cmd == APPCOMMAND_BROWSER_BACKWARD) {
75                        OnNavigationEvent(false);
76                        return true;
77                }
78
79                return wxListCtrlEx::MSWWindowProc(nMsg, wParam, lParam);
80        }
81
82        return wxListCtrlEx::MSWWindowProc(nMsg, wParam, lParam);
83}
84
85template<class CFileData> bool CFileListCtrl<CFileData>::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
86{
87        if (!m_pFilelistStatusBar)
88                return wxListCtrlEx::MSWOnNotify(idCtrl, lParam, result);
89
90        *result = 0;
91
92        NMHDR* pNmhdr = (NMHDR*)lParam;
93        if (pNmhdr->code == LVN_ODSTATECHANGED) {
94                // A range of items got (de)selected
95
96                if (m_insideSetSelection)
97                        return true;
98
99                if (!m_pFilelistStatusBar)
100                        return true;
101
102                if (wxGetKeyState(WXK_CONTROL) && wxGetKeyState(WXK_SHIFT))
103                {
104                        // The behavior of Ctrl+Shift+Click is highly erratic.
105                        // Even though it is very slow, we need to manually recount.
106                        m_pFilelistStatusBar->UnselectAll();
107                        int item = -1;
108                        while ((item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1) {
109                                if (m_hasParent && !item)
110                                        continue;
111
112                                const int index = m_indexMapping[item];
113                                const CFileData& data = m_fileData[index];
114                                if (data.comparison_flags == fill)
115                                        continue;
116
117                                if (ItemIsDir(index))
118                                        m_pFilelistStatusBar->SelectDirectory();
119                                else
120                                        m_pFilelistStatusBar->SelectFile(ItemGetSize(index));
121                        }
122                }
123                else {
124                        fzNMLVODSTATECHANGE* pNmOdStateChange = (fzNMLVODSTATECHANGE*)lParam;
125
126                        wxASSERT(pNmOdStateChange->iFrom <= pNmOdStateChange->iTo);
127                        for (int i = pNmOdStateChange->iFrom; i <= pNmOdStateChange->iTo; ++i) {
128                                if (m_hasParent && !i)
129                                        continue;
130
131                                const int index = m_indexMapping[i];
132                                const CFileData& data = m_fileData[index];
133                                if (data.comparison_flags == fill)
134                                        continue;
135
136                                if (ItemIsDir(index))
137                                        m_pFilelistStatusBar->SelectDirectory();
138                                else
139                                        m_pFilelistStatusBar->SelectFile(ItemGetSize(index));
140                        }
141                }
142                return true;
143        }
144        else if (pNmhdr->code == LVN_ITEMCHANGED) {
145                if (m_insideSetSelection)
146                        return true;
147
148                NMLISTVIEW* pNmListView = (NMLISTVIEW*)lParam;
149
150                // Item of -1 means change applied to all items
151                if (pNmListView->iItem == -1 && !(pNmListView->uNewState & LVIS_SELECTED))
152                {
153                        m_pFilelistStatusBar->UnselectAll();
154                }
155        }
156        else if (pNmhdr->code == LVN_MARQUEEBEGIN) {
157                SetFocus();
158        }
159        else if (pNmhdr->code == LVN_GETDISPINFO) {
160                // Handle this manually instead of using wx for it
161                // so that we can set the overlay image
162                LV_DISPINFO *info = (LV_DISPINFO *)lParam;
163
164                LV_ITEM& lvi = info->item;
165                long item = lvi.iItem;
166
167                int column = m_pVisibleColumnMapping[lvi.iSubItem];
168
169                if (lvi.mask & LVIF_TEXT) {
170                        wxString text = GetItemText(item, column);
171                        wxStrncpy(lvi.pszText, text, lvi.cchTextMax - 1);
172                        lvi.pszText[lvi.cchTextMax - 1] = 0;
173                }
174
175                if (lvi.mask & LVIF_IMAGE) {
176                        if (!lvi.iSubItem)
177                                lvi.iImage = OnGetItemImage(item);
178                        else
179                                lvi.iImage = -1;
180                }
181
182                if (!lvi.iSubItem)
183                        lvi.state = INDEXTOOVERLAYMASK(GetOverlayIndex(lvi.iItem));
184
185                return true;
186        }
187
188        return wxListCtrlEx::MSWOnNotify(idCtrl, lParam, result);
189}
190#endif
191
192#if defined(__WXGTK__) && !defined(__WXGTK3__)
193// Need to call a member function of a C++ template class
194// from a C function.
195// Sadly template functions with C linkage aren't possible,
196// so use some proxy object.
197class CGtkEventCallbackProxyBase
198{
199public:
200        virtual void OnNavigationEvent(bool forward) = 0;
201        virtual ~CGtkEventCallbackProxyBase() {}
202};
203
204template <class CFileData> class CGtkEventCallbackProxy : public CGtkEventCallbackProxyBase
205{
206public:
207        CGtkEventCallbackProxy(CFileListCtrl<CFileData> *pData) : m_pData(pData) {}
208
209        virtual void OnNavigationEvent(bool forward)
210        {
211                m_pData->OnNavigationEvent(forward);
212        }
213protected:
214        CFileListCtrl<CFileData> *m_pData;
215};
216
217extern "C" {
218static gboolean gtk_button_release_event(GtkWidget*, void *gdk_event, CGtkEventCallbackProxyBase *proxy)
219{
220        GdkEventButton* button_event = (GdkEventButton*)gdk_event;
221
222        // 8 is back, 9 is forward.
223        if (button_event->button != 8 && button_event->button != 9)
224                return FALSE;
225
226        proxy->OnNavigationEvent(button_event->button == 9);
227
228        return FALSE;
229}
230}
231#endif
232
233template<class CFileData> CFileListCtrl<CFileData>::CFileListCtrl(wxWindow* pParent, CState*, CQueueView* pQueue, bool border /*=false*/)
234: wxListCtrlEx(pParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxLC_VIRTUAL | wxLC_REPORT | wxLC_EDIT_LABELS | (border ? wxBORDER_SUNKEN : wxNO_BORDER)),
235        CComparableListing(this)
236{
237        CreateSystemImageList(16);
238        m_pQueue = pQueue;
239
240        m_sortColumn = 0;
241        m_sortDirection = 0;
242
243        m_hasParent = true;
244
245        m_comparisonIndex = -1;
246
247#ifndef __WXMSW__
248        m_dropHighlightAttribute.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW));
249#endif
250
251        m_pFilelistStatusBar = 0;
252
253        m_insideSetSelection = false;
254#ifdef __WXMSW__
255        // Enable use of overlay images
256        DWORD mask = ListView_GetCallbackMask((HWND)GetHandle()) | LVIS_OVERLAYMASK;
257        ListView_SetCallbackMask((HWND)GetHandle(), mask);
258#else
259        m_pending_focus_processing = 0;
260        m_focusItem = -1;
261#endif
262
263#if defined(__WXGTK__) && !defined(__WXGTK3__)
264        m_gtkEventCallbackProxy.reset(new CGtkEventCallbackProxy<CFileData>(this));
265
266        GtkWidget* widget = GetMainWindow()->GetConnectWidget();
267        g_signal_connect(widget, "button_release_event", G_CALLBACK(gtk_button_release_event), m_gtkEventCallbackProxy.get());
268#endif
269
270        m_genericTypes[genericTypes::file] = _("File");
271        m_genericTypes[genericTypes::directory] = _("Directory");
272
273        SetBackgroundStyle(wxBG_STYLE_SYSTEM);
274#ifndef __WXMSW__
275        GetMainWindow()->SetBackgroundStyle(wxBG_STYLE_SYSTEM);
276#endif
277}
278
279template<class CFileData> CFileListCtrl<CFileData>::~CFileListCtrl()
280{
281}
282
283template<class CFileData> void CFileListCtrl<CFileData>::SortList(int column /*=-1*/, int direction /*=-1*/, bool updateSelections /*=true*/)
284{
285        CancelLabelEdit();
286
287        if (column != -1) {
288                if (column != m_sortColumn)
289                {
290                        const int oldVisibleColumn = GetColumnVisibleIndex(m_sortColumn);
291                        if (oldVisibleColumn != -1)
292                                SetHeaderSortIconIndex(oldVisibleColumn, -1);
293                }
294        }
295        else
296                column = m_sortColumn;
297
298        if (direction == -1)
299                direction = m_sortDirection;
300
301        int newVisibleColumn = GetColumnVisibleIndex(column);
302        if (newVisibleColumn == -1) {
303                newVisibleColumn = 0;
304                column = 0;
305        }
306
307        SetHeaderSortIconIndex(newVisibleColumn, direction);
308
309        // Remember which files are selected
310        bool *selected = 0;
311        int focused_item = -1;
312        unsigned int focused_index{};
313
314        if (updateSelections) {
315#ifndef __WXMSW__
316                // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
317                if (GetSelectedItemCount())
318#endif
319                {
320                        int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
321                        if (item != -1) {
322                                selected = new bool[m_fileData.size()];
323                                memset(selected, 0, sizeof(bool) * m_fileData.size());
324                       
325                                do {
326                                        selected[m_indexMapping[item]] = 1;
327                                        item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
328                                } while (item != -1);
329                        }
330                }
331                focused_item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
332                if (focused_item != -1)
333                        focused_index = m_indexMapping[focused_item];
334        }
335
336        const int dirSortOption = COptions::Get()->GetOptionVal(OPTION_FILELIST_DIRSORT);
337
338        if (column == m_sortColumn && direction != m_sortDirection && !m_indexMapping.empty() &&
339                dirSortOption != 1)
340        {
341                // Simply reverse everything
342                m_sortDirection = direction;
343                m_sortColumn = column;
344                std::vector<unsigned int>::iterator start = m_indexMapping.begin();
345                if (m_hasParent)
346                        ++start;
347                std::reverse(start, m_indexMapping.end());
348
349                if (updateSelections) {
350                        SortList_UpdateSelections(selected, focused_item, focused_index);
351                        delete [] selected;
352                }
353
354                return;
355        }
356
357        m_sortDirection = direction;
358        m_sortColumn = column;
359
360        const unsigned int minsize = m_hasParent ? 3 : 2;
361        if (m_indexMapping.size() < minsize) {
362                delete [] selected;
363                return;
364        }
365
366        std::vector<unsigned int>::iterator start = m_indexMapping.begin();
367        if (m_hasParent)
368                ++start;
369        CSortComparisonObject object = GetSortComparisonObject();
370        std::sort(start, m_indexMapping.end(), object);
371        object.Destroy();
372
373        if (updateSelections) {
374                SortList_UpdateSelections(selected, focused_item, focused_index);
375                delete [] selected;
376        }
377}
378
379template<class CFileData> void CFileListCtrl<CFileData>::SortList_UpdateSelections(bool* selections, int focused_item, unsigned int focused_index)
380{
381        if (focused_item >= 0) {
382                if (m_indexMapping[focused_item] != focused_index) {
383                        SetItemState(focused_item, 0, wxLIST_STATE_FOCUSED);
384
385                        for (unsigned int i = m_hasParent ? 1 : 0; i < m_indexMapping.size(); ++i) {
386                                if (m_indexMapping[i] == focused_index) {
387                                        SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
388                                }
389                        }
390                }
391        }
392       
393        if (selections) {
394                for (unsigned int i = m_hasParent ? 1 : 0; i < m_indexMapping.size(); ++i) {
395                        const int state = GetItemState(i, wxLIST_STATE_SELECTED);
396                        const bool selected = (state & wxLIST_STATE_SELECTED) != 0;
397
398                        int item = m_indexMapping[i];
399                        if (selections[item] != selected)
400                                SetSelection(i, selections[item]);
401                }
402        }
403}
404
405template<class CFileData> CFileListCtrlSortBase::DirSortMode CFileListCtrl<CFileData>::GetDirSortMode()
406{
407        const int dirSortOption = COptions::Get()->GetOptionVal(OPTION_FILELIST_DIRSORT);
408
409        enum CFileListCtrlSortBase::DirSortMode dirSortMode;
410        switch (dirSortOption)
411        {
412        case 0:
413        default:
414                dirSortMode = CFileListCtrlSortBase::dirsort_ontop;
415                break;
416        case 1:
417                if (m_sortDirection)
418                        dirSortMode = CFileListCtrlSortBase::dirsort_onbottom;
419                else
420                        dirSortMode = CFileListCtrlSortBase::dirsort_ontop;
421                break;
422        case 2:
423                dirSortMode = CFileListCtrlSortBase::dirsort_inline;
424                break;
425        }
426
427        return dirSortMode;
428}
429
430template<class CFileData> CFileListCtrlSortBase::NameSortMode CFileListCtrl<CFileData>::GetNameSortMode()
431{
432        const int nameSortOption = COptions::Get()->GetOptionVal(OPTION_FILELIST_NAMESORT);
433
434        enum CFileListCtrlSortBase::NameSortMode nameSortMode;
435        switch (nameSortOption)
436        {
437        case 0:
438        default:
439                nameSortMode = CFileListCtrlSortBase::namesort_caseinsensitive;
440                break;
441        case 1:
442                nameSortMode = CFileListCtrlSortBase::namesort_casesensitive;
443                break;
444        case 2:
445                nameSortMode = CFileListCtrlSortBase::namesort_natural;
446                break;
447        }
448
449        return nameSortMode;
450}
451
452template<class CFileData> void CFileListCtrl<CFileData>::OnColumnClicked(wxListEvent &event)
453{
454        int col = m_pVisibleColumnMapping[event.GetColumn()];
455        if (col == -1)
456                return;
457
458        if (IsComparing()) {
459#ifdef __WXMSW__
460                ReleaseCapture();
461                Refresh();
462#endif
463                CConditionalDialog dlg(this, CConditionalDialog::compare_changesorting, CConditionalDialog::yesno);
464                dlg.SetTitle(_("Directory comparison"));
465                dlg.AddText(_("Sort order cannot be changed if comparing directories."));
466                dlg.AddText(_("End comparison and change sorting order?"));
467                if (!dlg.Run())
468                        return;
469                ExitComparisonMode();
470        }
471
472        int dir;
473        if (col == m_sortColumn)
474                dir = m_sortDirection ? 0 : 1;
475        else
476                dir = m_sortDirection;
477
478        SortList(col, dir);
479        RefreshListOnly(false);
480}
481
482#ifdef __WXMSW__
483namespace {
484wxString GetExt(const wxString& file)
485{
486        wxString ret;
487
488        int pos = file.Find('.', true);
489        if (pos > 0 && (static_cast<size_t>(pos) + 1) < file.size()) // Does neither starts nor end with dot
490                ret = file.Mid(pos + 1);
491
492        return ret;
493}
494}
495#endif
496
497template<class CFileData> wxString CFileListCtrl<CFileData>::GetType(wxString name, bool dir, const wxString& path /*=_T("")*/)
498{
499#ifdef __WXMSW__
500        wxString ext = GetExt(name);
501        ext.MakeLower();
502        std::map<wxString, wxString>::iterator typeIter = m_fileTypeMap.find(ext);
503        if (typeIter != m_fileTypeMap.end())
504                return typeIter->second;
505
506        wxString type;
507        int flags = SHGFI_TYPENAME;
508        if (path.empty())
509                flags |= SHGFI_USEFILEATTRIBUTES;
510        else if (path == _T("\\"))
511                name += _T("\\");
512        else
513                name = path + name;
514
515        SHFILEINFO shFinfo;
516        memset(&shFinfo, 0, sizeof(SHFILEINFO));
517        if (SHGetFileInfo(name.wc_str(),
518                dir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL,
519                &shFinfo,
520                sizeof(shFinfo),
521                flags))
522        {
523                if (!*shFinfo.szTypeName)
524                {
525                        if (!ext.empty())
526                        {
527                                type = ext;
528                                type.MakeUpper();
529                                type += _T("-");
530                                type += _("file");
531                        }
532                        else
533                                type = m_genericTypes[genericTypes::file];
534                }
535                else
536                {
537                        type = shFinfo.szTypeName;
538                        if (!dir && !ext.empty())
539                                m_fileTypeMap[ext.MakeLower()] = type;
540                }
541        }
542        else
543        {
544                if (!ext.empty())
545                {
546                        type = ext;
547                        type.MakeUpper();
548                        type += _T("-");
549                        type += _("file");
550                }
551                else
552                        type = m_genericTypes[genericTypes::file];
553        }
554        return type;
555#else
556        (void)path;
557
558        if (dir)
559                return m_genericTypes[genericTypes::directory];
560
561        int pos = name.Find('.', true);
562        if (pos < 1 || !name[pos + 1]) // Starts or ends with dot
563                return m_genericTypes[genericTypes::file];
564        wxString ext = name.Mid(pos + 1);
565        wxString lower_ext = ext.Lower();
566
567        std::map<wxString, wxString>::iterator typeIter = m_fileTypeMap.find(lower_ext);
568        if (typeIter != m_fileTypeMap.end())
569                return typeIter->second;
570
571        wxFileType *pType = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
572        if (!pType)
573        {
574                wxString desc = ext;
575                desc += _T("-");
576                desc += _("file");
577                m_fileTypeMap[ext] = desc;
578                return desc;
579        }
580
581        wxString desc;
582        if (pType->GetDescription(&desc) && !desc.empty())
583        {
584                delete pType;
585                m_fileTypeMap[ext] = desc;
586                return desc;
587        }
588        delete pType;
589
590        desc = ext;
591        desc += _T("-");
592        desc += _("file");
593        m_fileTypeMap[lower_ext] = desc;
594        return desc;
595#endif
596}
597
598template<class CFileData> void CFileListCtrl<CFileData>::ScrollTopItem(int item)
599{
600        wxListCtrlEx::ScrollTopItem(item);
601}
602
603template<class CFileData> void CFileListCtrl<CFileData>::OnPostScroll()
604{
605        if (!IsComparing())
606                return;
607
608        CComparableListing* pOther = GetOther();
609        if (!pOther)
610                return;
611
612        pOther->ScrollTopItem(GetTopItem());
613}
614
615template<class CFileData> void CFileListCtrl<CFileData>::OnExitComparisonMode()
616{
617        if (m_originalIndexMapping.empty()) {
618                return;
619        }
620
621        ComparisonRememberSelections();
622
623        m_indexMapping.clear();
624        m_indexMapping.swap(m_originalIndexMapping);
625
626        for (unsigned int i = 0; i < m_fileData.size() - 1; i++)
627                m_fileData[i].comparison_flags = normal;
628
629        SetItemCount(m_indexMapping.size());
630
631        ComparisonRestoreSelections();
632
633        RefreshListOnly();
634}
635
636template<class CFileData> void CFileListCtrl<CFileData>::CompareAddFile(t_fileEntryFlags flags)
637{
638        if (flags == fill) {
639                m_indexMapping.push_back(m_fileData.size() - 1);
640                return;
641        }
642
643        int index = m_originalIndexMapping[m_comparisonIndex];
644        m_fileData[index].comparison_flags = flags;
645
646        m_indexMapping.push_back(index);
647}
648
649template<class CFileData> void CFileListCtrl<CFileData>::ComparisonRememberSelections()
650{
651        m_comparisonSelections.clear();
652
653        if (GetItemCount() != (int)m_indexMapping.size())
654                return;
655
656        int focus = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
657        if (focus != -1) {
658                SetItemState(focus, 0, wxLIST_STATE_FOCUSED);
659                int index = m_indexMapping[focus];
660                if (m_fileData[index].comparison_flags == fill)
661                        focus = -1;
662                else
663                        focus = index;
664        }
665        m_comparisonSelections.push_back(focus);
666
667#ifndef __WXMSW__
668        // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
669        if (GetSelectedItemCount())
670#endif
671        {
672                int item = -1;
673                while ((item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1)
674                {
675                        int index = m_indexMapping[item];
676                        if (m_fileData[index].comparison_flags == fill)
677                                continue;
678                        m_comparisonSelections.push_back(index);
679                }
680        }
681}
682
683template<class CFileData> void CFileListCtrl<CFileData>::ComparisonRestoreSelections()
684{
685        if (m_comparisonSelections.empty())
686                return;
687
688        int focus = m_comparisonSelections.front();
689        m_comparisonSelections.pop_front();
690
691        int item = -1;
692        if (!m_comparisonSelections.empty())
693        {
694                item = m_comparisonSelections.front();
695                m_comparisonSelections.pop_front();
696        }
697        if (focus == -1)
698                focus = item;
699
700        for (unsigned int i = 0; i < m_indexMapping.size(); i++)
701        {
702                int index = m_indexMapping[i];
703                if (focus == index)
704                {
705                        SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
706                        focus = -1;
707                }
708
709                bool isSelected = GetItemState(i, wxLIST_STATE_SELECTED) == wxLIST_STATE_SELECTED;
710                bool shouldSelected = item == index;
711                if (isSelected != shouldSelected)
712                        SetSelection(i, shouldSelected);
713
714                if (shouldSelected)
715                {
716                        if (m_comparisonSelections.empty())
717                                item = -1;
718                        else
719                        {
720                                item = m_comparisonSelections.front();
721                                m_comparisonSelections.pop_front();
722                        }
723                }
724        }
725}
726
727template<class CFileData> void CFileListCtrl<CFileData>::OnColumnRightClicked(wxListEvent&)
728{
729        ShowColumnEditor();
730}
731
732template<class CFileData> void CFileListCtrl<CFileData>::InitSort(int optionID)
733{
734        wxString sortInfo = COptions::Get()->GetOption(optionID);
735        if( !sortInfo.empty() ) {
736                m_sortDirection = sortInfo[0] - '0';
737        }
738        else {
739                m_sortDirection = 0;
740        }
741        if (m_sortDirection < 0 || m_sortDirection > 1)
742                m_sortDirection = 0;
743
744        if (sortInfo.Len() == 3)
745        {
746                m_sortColumn = sortInfo[2] - '0';
747                if (GetColumnVisibleIndex(m_sortColumn) == -1)
748                        m_sortColumn = 0;
749        }
750        else
751                m_sortColumn = 0;
752}
753
754template<class CFileData> void CFileListCtrl<CFileData>::OnItemSelected(wxListEvent& event)
755{
756#ifndef __WXMSW__
757        // On MSW this is done in the subclassed window proc
758        if (m_insideSetSelection)
759                return;
760        if (m_pending_focus_processing)
761                return;
762#endif
763
764        const int item = event.GetIndex();
765
766#ifndef __WXMSW__
767        if (m_selections[item])
768                return;
769        m_selections[item] = true;
770#endif
771
772        if (!m_pFilelistStatusBar)
773                return;
774
775        if (item < 0 || item >= (int)m_indexMapping.size())
776                return;
777
778        if (m_hasParent && !item)
779                return;
780
781        const int index = m_indexMapping[item];
782        const CFileData& data = m_fileData[index];
783        if (data.comparison_flags == fill)
784                return;
785
786        if (ItemIsDir(index))
787                m_pFilelistStatusBar->SelectDirectory();
788        else
789                m_pFilelistStatusBar->SelectFile(ItemGetSize(index));
790}
791
792template<class CFileData> void CFileListCtrl<CFileData>::OnItemDeselected(wxListEvent& event)
793{
794#ifndef __WXMSW__
795        // On MSW this is done in the subclassed window proc
796        if (m_insideSetSelection)
797                return;
798#endif
799
800        const int item = event.GetIndex();
801
802#ifndef __WXMSW__
803        if (!m_selections[item])
804                return;
805        m_selections[item] = false;
806#endif
807
808        if (!m_pFilelistStatusBar)
809                return;
810
811        if (item < 0 || item >= (int)m_indexMapping.size())
812                return;
813
814        if (m_hasParent && !item)
815                return;
816
817        const int index = m_indexMapping[item];
818        const CFileData& data = m_fileData[index];
819        if (data.comparison_flags == fill)
820                return;
821
822        if (ItemIsDir(index))
823                m_pFilelistStatusBar->UnselectDirectory();
824        else
825                m_pFilelistStatusBar->UnselectFile(ItemGetSize(index));
826}
827
828template<class CFileData> void CFileListCtrl<CFileData>::SetSelection(int item, bool select)
829{
830        m_insideSetSelection = true;
831        SetItemState(item, select ? wxLIST_STATE_SELECTED : 0, wxLIST_STATE_SELECTED);
832        m_insideSetSelection = false;
833#ifndef __WXMSW__
834        m_selections[item] = select;
835#endif
836}
837
838#ifndef __WXMSW__
839template<class CFileData> void CFileListCtrl<CFileData>::OnFocusChanged(wxListEvent& event)
840{
841        const int focusItem = event.GetIndex();
842
843        // Need to defer processing, as focus it set before selection by wxWidgets internally
844        wxCommandEvent *evt = new wxCommandEvent();
845        evt->SetEventType(fz_EVT_FILELIST_FOCUSCHANGE);
846        evt->SetInt(m_focusItem);
847        evt->SetExtraLong((long)focusItem);
848        m_pending_focus_processing++;
849        QueueEvent(evt);
850
851        m_focusItem = focusItem;
852}
853
854template<class CFileData> void CFileListCtrl<CFileData>::SetItemCount(int count)
855{
856        m_selections.resize(count, false);
857        if (m_focusItem >= count)
858                m_focusItem = -1;
859        wxListCtrlEx::SetItemCount(count);
860}
861
862template<class CFileData> void CFileListCtrl<CFileData>::OnProcessFocusChange(wxCommandEvent& event)
863{
864        m_pending_focus_processing--;
865        int old_focus = event.GetInt();
866        int new_focus = (int)event.GetExtraLong();
867
868        if (old_focus >= GetItemCount())
869                return;
870
871        if (old_focus != -1)
872        {
873                bool selected = GetItemState(old_focus, wxLIST_STATE_SELECTED) == wxLIST_STATE_SELECTED;
874                if (!selected && m_selections[old_focus])
875                {
876                        // Need to deselect all
877                        if (m_pFilelistStatusBar)
878                                m_pFilelistStatusBar->UnselectAll();
879                        for (unsigned int i = 0; i < m_selections.size(); i++)
880                                m_selections[i] = 0;
881                }
882        }
883
884        int min;
885        int max;
886        if (new_focus > old_focus)
887        {
888                min = old_focus;
889                max = new_focus;
890        }
891        else
892        {
893                min = new_focus;
894                max = old_focus;
895        }
896        if (min == -1)
897                min++;
898        if (max == -1)
899                return;
900
901        if (max >= GetItemCount())
902                return;
903
904        for (int i = min; i <= max; i++)
905        {
906                bool selected = GetItemState(i, wxLIST_STATE_SELECTED) == wxLIST_STATE_SELECTED;
907                if (selected == m_selections[i])
908                        continue;
909
910                m_selections[i] = selected;
911
912                if (!m_pFilelistStatusBar)
913                        continue;
914
915                if (m_hasParent && !i)
916                        continue;
917
918                const int index = m_indexMapping[i];
919                const CFileData& data = m_fileData[index];
920                if (data.comparison_flags == fill)
921                        continue;
922
923                if (selected)
924                {
925                        if (ItemIsDir(index))
926                                m_pFilelistStatusBar->SelectDirectory();
927                        else
928                                m_pFilelistStatusBar->SelectFile(ItemGetSize(index));
929                }
930                else
931                {
932                        if (ItemIsDir(index))
933                                m_pFilelistStatusBar->UnselectDirectory();
934                        else
935                                m_pFilelistStatusBar->UnselectFile(ItemGetSize(index));
936                }
937        }
938}
939
940template<class CFileData> void CFileListCtrl<CFileData>::OnLeftDown(wxMouseEvent& event)
941{
942        // Left clicks in the whitespace around the items deselect everything
943        // but does not change focus. Defer event.
944        event.Skip();
945        wxCommandEvent *evt = new wxCommandEvent();
946        evt->SetEventType(fz_EVT_DEFERRED_MOUSEEVENT);
947        QueueEvent(evt);
948}
949
950template<class CFileData> void CFileListCtrl<CFileData>::OnProcessMouseEvent(wxCommandEvent&)
951{
952        if (m_pending_focus_processing)
953                return;
954
955        if (m_focusItem >= GetItemCount())
956                return;
957        if (m_focusItem == -1)
958                return;
959
960        bool selected = GetItemState(m_focusItem, wxLIST_STATE_SELECTED) == wxLIST_STATE_SELECTED;
961        if (!selected && m_selections[m_focusItem])
962        {
963                // Need to deselect all
964                if (m_pFilelistStatusBar)
965                        m_pFilelistStatusBar->UnselectAll();
966                for (unsigned int i = 0; i < m_selections.size(); i++)
967                        m_selections[i] = 0;
968        }
969}
970#endif
971
972template<class CFileData> void CFileListCtrl<CFileData>::ClearSelection()
973{
974        // Clear selection
975#ifndef __WXMSW__
976        // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
977        if (GetSelectedItemCount())
978#endif
979        {
980                int item = -1;
981                while ((item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1)
982                {
983                        SetSelection(item, false);
984                }
985        }
986
987        int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
988        if (item != -1)
989                SetItemState(item, 0, wxLIST_STATE_FOCUSED);
990}
991
992template<class CFileData> void CFileListCtrl<CFileData>::OnKeyDown(wxKeyEvent& event)
993{
994#ifdef __WXMAC__
995#define CursorModifierKey wxMOD_CMD
996#else
997#define CursorModifierKey wxMOD_ALT
998#endif
999
1000        const int code = event.GetKeyCode();
1001        const int mods = event.GetModifiers();
1002        if (code == 'A' && (mods == wxMOD_CMD || mods == (wxMOD_CONTROL | wxMOD_META)))
1003        {
1004                for (unsigned int i = m_hasParent ? 1 : 0; i < m_indexMapping.size(); i++)
1005                {
1006                        const CFileData& data = m_fileData[m_indexMapping[i]];
1007                        if (data.comparison_flags != fill)
1008                                SetSelection(i, true);
1009                        else
1010                                SetSelection(i, false);
1011                }
1012                if (m_hasParent)
1013                        SetSelection(0, false);
1014                if (m_pFilelistStatusBar)
1015                        m_pFilelistStatusBar->SelectAll();
1016        }
1017        else if (code == WXK_BACK ||
1018                        (code == WXK_UP && event.GetModifiers() == CursorModifierKey) ||
1019                        (code == WXK_LEFT && event.GetModifiers() == CursorModifierKey)
1020                )
1021        {
1022                OnNavigationEvent(false);
1023        }
1024        else
1025                event.Skip();
1026}
Note: See TracBrowser for help on using the repository browser.