source: filezilla/trunk/fuentes/src/interface/listctrlex.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: 24.7 KB
Line 
1#include <filezilla.h>
2#include "listctrlex.h"
3#include "filezillaapp.h"
4#include <wx/renderer.h>
5#include <wx/tokenzr.h>
6#include "Options.h"
7#include "dialogex.h"
8#ifdef __WXMSW__
9#include "commctrl.h"
10
11#ifndef HDF_SORTUP
12#define HDF_SORTUP                              0x0400
13#endif
14#ifndef HDF_SORTDOWN
15#define HDF_SORTDOWN                    0x0200
16#endif
17#else
18#include "themeprovider.h"
19#endif
20
21DECLARE_EVENT_TYPE(fzEVT_POSTSCROLL, -1)
22DEFINE_EVENT_TYPE(fzEVT_POSTSCROLL)
23
24BEGIN_EVENT_TABLE(wxListCtrlEx, wxListCtrlExBase)
25EVT_COMMAND(wxID_ANY, fzEVT_POSTSCROLL, wxListCtrlEx::OnPostScrollEvent)
26EVT_SCROLLWIN(wxListCtrlEx::OnScrollEvent)
27EVT_MOUSEWHEEL(wxListCtrlEx::OnMouseWheel)
28EVT_LIST_ITEM_FOCUSED(wxID_ANY, wxListCtrlEx::OnSelectionChanged)
29EVT_LIST_ITEM_SELECTED(wxID_ANY, wxListCtrlEx::OnSelectionChanged)
30EVT_KEY_DOWN(wxListCtrlEx::OnKeyDown)
31EVT_LIST_BEGIN_LABEL_EDIT(wxID_ANY, wxListCtrlEx::OnBeginLabelEdit)
32EVT_LIST_END_LABEL_EDIT(wxID_ANY, wxListCtrlEx::OnEndLabelEdit)
33#ifndef __WXMSW__
34EVT_LIST_COL_DRAGGING(wxID_ANY, wxListCtrlEx::OnColumnDragging)
35#endif
36END_EVENT_TABLE()
37
38#define MIN_COLUMN_WIDTH 12
39
40wxListCtrlEx::wxListCtrlEx(wxWindow *parent,
41                                                   wxWindowID id,
42                                                   const wxPoint& pos,
43                                                   const wxSize& size,
44                                                   long style,
45                                                   const wxValidator& validator,
46                                                   const wxString& name)
47{
48        Create(parent, id, pos, size, style, validator, name);
49
50#ifndef __WXMSW__
51        m_editing = false;
52#else
53        m_columnDragging = false;
54#endif
55        m_blockedLabelEditing = false;
56}
57
58wxListCtrlEx::~wxListCtrlEx()
59{
60#ifdef __WXMSW__
61        delete m_pHeaderImageList;
62#endif
63        delete [] m_pVisibleColumnMapping;
64}
65
66wxWindow* wxListCtrlEx::GetMainWindow()
67{
68#ifdef __WXMSW__
69        return this;
70#else
71        return reinterpret_cast<wxWindow*>(m_mainWin);
72#endif
73}
74
75wxWindow const* wxListCtrlEx::GetMainWindow() const
76{
77#ifdef __WXMSW__
78        return this;
79#else
80        return reinterpret_cast<wxWindow const*>(m_mainWin);
81#endif
82}
83
84void wxListCtrlEx::OnPostScroll()
85{
86}
87
88void wxListCtrlEx::OnPostScrollEvent(wxCommandEvent&)
89{
90        OnPostScroll();
91}
92
93void wxListCtrlEx::OnPreEmitPostScrollEvent()
94{
95        EmitPostScrollEvent();
96}
97
98void wxListCtrlEx::EmitPostScrollEvent()
99{
100        QueueEvent(new wxCommandEvent(fzEVT_POSTSCROLL, wxID_ANY));
101}
102
103void wxListCtrlEx::OnScrollEvent(wxScrollWinEvent& event)
104{
105        event.Skip();
106        OnPreEmitPostScrollEvent();
107}
108
109void wxListCtrlEx::OnMouseWheel(wxMouseEvent& event)
110{
111        event.Skip();
112        OnPreEmitPostScrollEvent();
113}
114
115void wxListCtrlEx::OnSelectionChanged(wxListEvent& event)
116{
117        event.Skip();
118        OnPreEmitPostScrollEvent();
119}
120
121void wxListCtrlEx::ScrollTopItem(int item)
122{
123        if (!GetItemCount()) {
124                return;
125        }
126
127        if (item < 0) {
128                item = 0;
129        }
130        else if (item >= GetItemCount()) {
131                item = GetItemCount() - 1;
132        }
133       
134        const int current = GetTopItem();
135
136        int delta = item - current;
137
138        if (!delta)
139                return;
140
141        wxRect rect;
142        GetItemRect(current, rect, wxLIST_RECT_BOUNDS);
143
144        delta *= rect.GetHeight();
145        ScrollList(0, delta);
146}
147
148
149void wxListCtrlEx::HandlePrefixSearch(wxChar character)
150{
151        wxASSERT(character);
152
153        // Keyboard navigation within items
154        fz::datetime now = fz::datetime::now();
155        if (m_prefixSearch_lastKeyPress.empty()) {
156                fz::duration span = now - m_prefixSearch_lastKeyPress;
157                if (span.get_seconds() >= 1) {
158                        m_prefixSearch_prefix = _T("");
159                }
160        }
161        m_prefixSearch_lastKeyPress = now;
162
163        wxString newPrefix = m_prefixSearch_prefix + character;
164
165        int item;
166#ifndef __WXMSW__
167        // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
168        if (!GetSelectedItemCount())
169                item = -1;
170        else
171#endif
172        {
173                item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
174        }
175
176        bool beep = false;
177        if (item != -1) {
178                wxString text = GetItemText(item, 0);
179                if (text.Length() >= m_prefixSearch_prefix.Length() && !m_prefixSearch_prefix.CmpNoCase(text.Left(m_prefixSearch_prefix.Length())))
180                        beep = true;
181        }
182        else if (m_prefixSearch_prefix.empty())
183                beep = true;
184
185        int start = item;
186        if (start < 0)
187                start = 0;
188
189        int newPos = FindItemWithPrefix(newPrefix, start);
190
191        if (newPos == -1 && (m_prefixSearch_prefix.Len() == 1 && m_prefixSearch_prefix[0] == character) && item != -1 && beep) {
192                // Search the next item that starts with the same letter
193                newPrefix = m_prefixSearch_prefix;
194                newPos = FindItemWithPrefix(newPrefix, item + 1);
195        }
196
197        m_prefixSearch_prefix = newPrefix;
198        if (newPos == -1)
199        {
200                if (beep)
201                        wxBell();
202                return;
203        }
204
205        while (item != -1)
206        {
207                SetItemState(item, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
208                item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
209        }
210        SetItemState(newPos, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
211
212#ifdef __WXMSW__
213        // SetItemState does not move the selection mark, that is the item from
214        // which a multiple selection starts (e.g. shift+up/down)
215        HWND hWnd = (HWND)GetHandle();
216        ::SendMessage(hWnd, LVM_SETSELECTIONMARK, 0, newPos);
217#endif
218
219        EnsureVisible(newPos);
220}
221
222void wxListCtrlEx::OnKeyDown(wxKeyEvent& event)
223{
224        if (!m_prefixSearch_enabled)
225        {
226                event.Skip();
227                return;
228        }
229
230        int code = event.GetKeyCode();
231        if (code == WXK_LEFT ||
232                code == WXK_RIGHT ||
233                code == WXK_UP ||
234                code == WXK_DOWN ||
235                code == WXK_HOME ||
236                code == WXK_END)
237        {
238                ResetSearchPrefix();
239                event.Skip();
240                return;
241        }
242
243        if (event.AltDown() && !event.ControlDown()) // Alt but not AltGr
244        {
245                event.Skip();
246                return;
247        }
248
249        wxChar key;
250
251        switch (code)
252        {
253        case WXK_NUMPAD0:
254        case WXK_NUMPAD1:
255        case WXK_NUMPAD2:
256        case WXK_NUMPAD3:
257        case WXK_NUMPAD4:
258        case WXK_NUMPAD5:
259        case WXK_NUMPAD6:
260        case WXK_NUMPAD7:
261        case WXK_NUMPAD8:
262        case WXK_NUMPAD9:
263                key = '0' + code - WXK_NUMPAD0;
264                break;
265        case WXK_NUMPAD_ADD:
266                key = '+';
267                break;
268        case WXK_NUMPAD_SUBTRACT:
269                key = '-';
270                break;
271        case WXK_NUMPAD_MULTIPLY:
272                key = '*';
273                break;
274        case WXK_NUMPAD_DIVIDE:
275                key = '/';
276                break;
277        default:
278                key = 0;
279                break;
280        }
281        if (key)
282        {
283                if (event.GetModifiers())
284                {
285                        // Numpad keys can not have modifiers
286                        event.Skip();
287                }
288                HandlePrefixSearch(key);
289                return;
290        }
291
292#if defined(__WXMSW__)
293
294        if (code >= 300 && code != WXK_NUMPAD_DECIMAL)
295        {
296                event.Skip();
297                return;
298        }
299
300        // Get the actual key
301        BYTE state[256];
302        if (!GetKeyboardState(state)) {
303                event.Skip();
304                return;
305        }
306        wxChar buffer[1];
307        int res = ToUnicode(event.GetRawKeyCode(), 0, state, buffer, 1, 0);
308        if (res != 1)
309        {
310                event.Skip();
311                return;
312        }
313
314        key = buffer[0];
315
316        if (key < 32)
317        {
318                event.Skip();
319                return;
320        }
321        if (key == 32 && event.HasModifiers())
322        {
323                event.Skip();
324                return;
325        }
326        HandlePrefixSearch(key);
327        return;
328#else
329        if (code > 32 && code < 300 && !event.HasModifiers())
330        {
331                int unicodeKey = event.GetUnicodeKey();
332                if (unicodeKey)
333                        code = unicodeKey;
334                HandlePrefixSearch(code);
335        }
336        else
337                event.Skip();
338#endif //defined(__WXMSW__)
339}
340
341// Declared const due to design error in wxWidgets.
342// Won't be fixed since a fix would break backwards compatibility
343// Both functions use a const_cast<CLocalListView *>(this) and modify
344// the instance.
345wxString wxListCtrlEx::OnGetItemText(long item, long column) const
346{
347        wxListCtrlEx *pThis = const_cast<wxListCtrlEx *>(this);
348        return pThis->GetItemText(item, (unsigned int)m_pVisibleColumnMapping[column]);
349}
350
351int wxListCtrlEx::FindItemWithPrefix(const wxString& searchPrefix, int start)
352{
353        const int count = GetItemCount();
354        for (int i = start; i < (count + start); i++)
355        {
356                int item = i % count;
357                wxString namePrefix = GetItemText(item, 0).Left(searchPrefix.Length());
358                if (!namePrefix.CmpNoCase(searchPrefix))
359                        return i % count;
360        }
361        return -1;
362}
363
364void wxListCtrlEx::SaveSetItemCount(long count)
365{
366#ifndef __WXMSW__
367        if (count < GetItemCount())
368        {
369                int focused = GetNextItem(count - 1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
370                if (focused != -1)
371                        SetItemState(focused, 0, wxLIST_STATE_FOCUSED);
372        }
373#endif //__WXMSW__
374        SetItemCount(count);
375}
376
377void wxListCtrlEx::ResetSearchPrefix()
378{
379        m_prefixSearch_prefix = _T("");
380}
381
382void wxListCtrlEx::ShowColumn(unsigned int col, bool show)
383{
384        if (col >= m_columnInfo.size())
385                return;
386
387        if (m_columnInfo[col].shown == show)
388                return;
389
390        m_columnInfo[col].shown = show;
391
392        if (show)
393        {
394                // Insert new column
395                int pos = 0;
396                for (unsigned int i = 0; i < m_columnInfo.size(); i++)
397                {
398                        if (i == col)
399                                continue;
400                        t_columnInfo& info = m_columnInfo[i];
401                        if (info.shown && info.order < m_columnInfo[col].order)
402                                pos++;
403                }
404                for (int i = GetColumnCount() - 1; i >= pos; i--)
405                        m_pVisibleColumnMapping[i + 1] = m_pVisibleColumnMapping[i];
406                m_pVisibleColumnMapping[pos] = col;
407
408                t_columnInfo& info = m_columnInfo[col];
409                InsertColumn(pos, info.name, info.align, info.width);
410        }
411        else
412        {
413                int i;
414                for (i = 0; i < GetColumnCount(); i++)
415                {
416                        if (m_pVisibleColumnMapping[i] == col)
417                                break;
418                }
419                wxASSERT(m_columnInfo[col].order >= (unsigned int)i);
420                for (int j = i + 1; j < GetColumnCount(); j++)
421                        m_pVisibleColumnMapping[j - 1] = m_pVisibleColumnMapping[j];
422
423                wxASSERT(i < GetColumnCount());
424
425                m_columnInfo[col].width = GetColumnWidth(i);
426                DeleteColumn(i);
427        }
428}
429
430void wxListCtrlEx::LoadColumnSettings(int widthsOptionId, int visibilityOptionId, int sortOptionId)
431{
432        wxASSERT(!GetColumnCount());
433
434        if (widthsOptionId != -1)
435                ReadColumnWidths(widthsOptionId);
436
437        delete [] m_pVisibleColumnMapping;
438        m_pVisibleColumnMapping = new unsigned int[m_columnInfo.size()];
439
440        if (visibilityOptionId != -1) {
441                wxString visibleColumns = COptions::Get()->GetOption(visibilityOptionId);
442                if (visibleColumns.Len() >= m_columnInfo.size()) {
443                        for (unsigned int i = 0; i < m_columnInfo.size(); ++i) {
444                                if (!m_columnInfo[i].fixed)
445                                        m_columnInfo[i].shown = visibleColumns[i] == '1';
446                        }
447                }
448        }
449
450        if (sortOptionId != -1) {
451                wxString strorder = COptions::Get()->GetOption(sortOptionId);
452                wxStringTokenizer tokens(strorder, _T(","));
453
454                unsigned int count = tokens.CountTokens();
455                if (count == m_columnInfo.size()) {
456                        unsigned long *order = new unsigned long[count];
457                        bool *order_set = new bool[count];
458                        memset(order_set, 0, sizeof(bool) * count);
459
460                        unsigned int i = 0;
461                        while (tokens.HasMoreTokens()) {
462                                if (!tokens.GetNextToken().ToULong(&order[i]))
463                                        break;
464                                if (order[i] >= count || order_set[order[i]])
465                                        break;
466                                order_set[order[i]] = true;
467                                i++;
468                        }
469                        if (i == count) {
470                                bool valid = true;
471                                for (size_t j = 0; j < m_columnInfo.size(); ++j) {
472                                        if (!m_columnInfo[j].fixed)
473                                                continue;
474
475                                        if (j != order[j]) {
476                                                valid = false;
477                                                break;
478                                        }
479                                }
480
481                                if (valid) {
482                                        for (size_t j = 0; j < m_columnInfo.size(); ++j) {
483                                                m_columnInfo[j].order = order[j];
484                                        }
485                                }
486                        }
487
488                        delete [] order;
489                        delete [] order_set;
490                }
491        }
492
493        CreateVisibleColumnMapping();
494}
495
496void wxListCtrlEx::SaveColumnSettings(int widthsOptionId, int visibilityOptionId, int sortOptionId)
497{
498        if (widthsOptionId != -1)
499                SaveColumnWidths(widthsOptionId);
500
501        if (visibilityOptionId != -1)
502        {
503                wxString visibleColumns;
504                for (unsigned int i = 0; i < m_columnInfo.size(); i++)
505                {
506                        if (m_columnInfo[i].shown)
507                                visibleColumns += _T("1");
508                        else
509                                visibleColumns += _T("0");
510                }
511                COptions::Get()->SetOption(visibilityOptionId, visibleColumns);
512        }
513
514        if (sortOptionId != -1)
515        {
516                wxString order;
517                for (unsigned int i = 0; i < m_columnInfo.size(); i++)
518                {
519                        if (i)
520                                order += _T(",");
521                        order += wxString::Format(_T("%d"), m_columnInfo[i].order);
522                }
523                COptions::Get()->SetOption(sortOptionId, order);
524        }
525}
526
527bool wxListCtrlEx::ReadColumnWidths(unsigned int optionId)
528{
529        wxASSERT(!GetColumnCount());
530
531        if (wxGetKeyState(WXK_SHIFT) &&
532                wxGetKeyState(WXK_ALT) &&
533                wxGetKeyState(WXK_CONTROL))
534        {
535                return true;
536        }
537
538        size_t const columnCount = m_columnInfo.size();
539
540        wxString savedWidths = COptions::Get()->GetOption(optionId);
541        wxStringTokenizer tokens(savedWidths, _T(" "));
542
543        size_t const tokenCount = tokens.CountTokens();
544
545        for (size_t i = 0; i < columnCount; ++i) {
546                unsigned long newWidth{};
547                if (i < tokenCount) {
548                        wxString token = tokens.GetNextToken();
549                        if (token.ToULong(&newWidth) && newWidth < 10000) {
550                                m_columnInfo[i].width = newWidth;
551                        }
552                }
553        }
554
555        return true;
556}
557
558void wxListCtrlEx::SaveColumnWidths(unsigned int optionId)
559{
560        const unsigned int count = m_columnInfo.size();
561
562        wxString widths;
563        for (unsigned int i = 0; i < count; ++i) {
564                int width = 0;
565
566                bool found = false;
567                for (int j = 0; j < GetColumnCount(); ++j) {
568                        if (m_pVisibleColumnMapping[j] != i)
569                                continue;
570
571                        found = true;
572                        width = GetColumnWidth(j);
573                }
574                if (!found)
575                        width = m_columnInfo[i].width;
576                widths += wxString::Format(_T("%d "), width);
577        }
578        widths.RemoveLast();
579
580        COptions::Get()->SetOption(optionId, widths);
581}
582
583
584void wxListCtrlEx::AddColumn(const wxString& name, int align, int initialWidth, bool fixed)
585{
586        wxASSERT(!GetColumnCount());
587
588        t_columnInfo info;
589        info.name = name;
590        info.align = align;
591        info.width = initialWidth;
592        info.shown = true;
593        info.order = m_columnInfo.size();
594        info.fixed = fixed;
595
596        m_columnInfo.push_back(info);
597}
598
599// Moves column. Target position includes both hidden
600// as well as shown columns
601void wxListCtrlEx::MoveColumn(unsigned int col, unsigned int before)
602{
603        if (m_columnInfo[col].order == before)
604                return;
605
606        for (unsigned int i = 0; i < m_columnInfo.size(); ++i) {
607                if (i == col)
608                        continue;
609
610                t_columnInfo& info = m_columnInfo[i];
611                if (info.order > col)
612                        --info.order;
613                if (info.order >= before)
614                        ++info.order;
615        }
616
617        t_columnInfo& info = m_columnInfo[col];
618
619        if (info.shown) {
620                int icon = -1;
621                // Remove old column
622                for (unsigned int i = 0; i < (unsigned int)GetColumnCount(); ++i) {
623                        if (m_pVisibleColumnMapping[i] != col)
624                                continue;
625
626                        for (unsigned int j = i + 1; j < (unsigned int)GetColumnCount(); ++j)
627                                m_pVisibleColumnMapping[j - 1] = m_pVisibleColumnMapping[j];
628                        info.width = GetColumnWidth(i);
629
630                        icon = GetHeaderSortIconIndex(i);
631                        DeleteColumn(i);
632
633                        break;
634                }
635
636                // Insert new column
637                unsigned int pos = 0;
638                for (unsigned int i = 0; i < m_columnInfo.size(); ++i) {
639                        if (i == col)
640                                continue;
641                        t_columnInfo& info2 = m_columnInfo[i];
642                        if (info2.shown && info2.order < before) {
643                                ++pos;
644                        }
645                }
646                for (unsigned int i = (int)GetColumnCount(); i > pos; --i)
647                        m_pVisibleColumnMapping[i] = m_pVisibleColumnMapping[i - 1];
648                m_pVisibleColumnMapping[pos] = col;
649
650                InsertColumn(pos, info.name, info.align, info.width);
651
652                SetHeaderSortIconIndex(pos, icon);
653        }
654        m_columnInfo[col].order = before;
655}
656
657void wxListCtrlEx::CreateVisibleColumnMapping()
658{
659        int pos = 0;
660        for (unsigned int j = 0; j < m_columnInfo.size(); j++)
661        {
662                for (unsigned int i = 0; i < m_columnInfo.size(); i++)
663                {
664                        const t_columnInfo &column = m_columnInfo[i];
665
666                        if (!column.shown)
667                                continue;
668
669                        if (column.order != j)
670                                continue;
671
672                        m_pVisibleColumnMapping[pos] = i;
673                        InsertColumn(pos++, column.name, column.align, column.width);
674                }
675        }
676}
677
678class CColumnEditDialog : public wxDialogEx
679{
680public:
681        int *m_order;
682        DECLARE_EVENT_TABLE()
683
684protected:
685        void OnUp(wxCommandEvent&)
686        {
687                wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox);
688                int sel = pListBox->GetSelection();
689                if (sel < 2)
690                        return;
691
692                int tmp;
693                tmp = m_order[sel - 1];
694                m_order[sel - 1] = m_order[sel];
695                m_order[sel] = tmp;
696
697                wxString name = pListBox->GetString(sel);
698                bool checked = pListBox->IsChecked(sel);
699                pListBox->Delete(sel);
700                pListBox->Insert(name, sel - 1);
701                pListBox->Check(sel - 1, checked);
702                pListBox->SetSelection(sel - 1);
703
704                wxCommandEvent evt;
705                OnSelChanged(evt);
706        }
707
708        void OnDown(wxCommandEvent&)
709        {
710                wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox);
711                int sel = pListBox->GetSelection();
712                if (sel < 1)
713                        return;
714                if (sel >= (int)pListBox->GetCount() - 1)
715                        return;
716
717                int tmp;
718                tmp = m_order[sel + 1];
719                m_order[sel + 1] = m_order[sel];
720                m_order[sel] = tmp;
721
722                wxString name = pListBox->GetString(sel);
723                bool checked = pListBox->IsChecked(sel);
724                pListBox->Delete(sel);
725                pListBox->Insert(name, sel + 1);
726                pListBox->Check(sel + 1, checked);
727                pListBox->SetSelection(sel + 1);
728
729                wxCommandEvent evt;
730                OnSelChanged(evt);
731        }
732
733        void OnSelChanged(wxCommandEvent&)
734        {
735                wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox);
736                int sel = pListBox->GetSelection();
737                XRCCTRL(*this, "ID_UP", wxButton)->Enable(sel > 1);
738                XRCCTRL(*this, "ID_DOWN", wxButton)->Enable(sel > 0 && sel < (int)pListBox->GetCount() - 1);
739        }
740
741        void OnCheck(wxCommandEvent& event)
742        {
743                if (!event.GetSelection() && !event.IsChecked())
744                {
745                        wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox);
746                        pListBox->Check(0);
747                        wxMessageBoxEx(_("The filename column can neither be hidden nor moved."), _("Column properties"));
748                }
749        }
750};
751
752BEGIN_EVENT_TABLE(CColumnEditDialog, wxDialogEx)
753EVT_BUTTON(XRCID("ID_UP"), CColumnEditDialog::OnUp)
754EVT_BUTTON(XRCID("ID_DOWN"), CColumnEditDialog::OnDown)
755EVT_LISTBOX(wxID_ANY, CColumnEditDialog::OnSelChanged)
756EVT_CHECKLISTBOX(wxID_ANY, CColumnEditDialog::OnCheck)
757END_EVENT_TABLE()
758
759void wxListCtrlEx::ShowColumnEditor()
760{
761        CColumnEditDialog dlg;
762
763        if (!dlg.Load(this, _T("ID_COLUMN_SETUP"))) {
764                wxBell();
765                return;
766        }
767
768        wxCheckListBox* pListBox = XRCCTRL(dlg, "ID_ACTIVE", wxCheckListBox);
769
770        dlg.m_order = new int[m_columnInfo.size()];
771        for (unsigned int j = 0; j < m_columnInfo.size(); j++)
772        {
773                for (unsigned int i = 0; i < m_columnInfo.size(); i++)
774                {
775                        if (m_columnInfo[i].order != j)
776                                continue;
777                        dlg.m_order[j] = i;
778                        pListBox->Append(m_columnInfo[i].name);
779                        if (m_columnInfo[i].shown)
780                                pListBox->Check(j);
781                }
782        }
783        wxASSERT(pListBox->GetCount() == m_columnInfo.size());
784
785        dlg.GetSizer()->Fit(&dlg);
786
787        if (dlg.ShowModal() != wxID_OK)
788        {
789                delete [] dlg.m_order;
790                return;
791        }
792
793        for (unsigned int i = 0; i < m_columnInfo.size(); i++)
794        {
795                int col = dlg.m_order[i];
796                bool isChecked = pListBox->IsChecked(i);
797                if (!isChecked && !col)
798                {
799                        isChecked = true;
800                        wxMessageBoxEx(_("The filename column cannot be hidden."));
801                }
802                MoveColumn(col, i);
803                if (m_columnInfo[col].shown != isChecked)
804                        ShowColumn(col, isChecked);
805        }
806
807        delete [] dlg.m_order;
808
809        // Generic wxListCtrl needs manual refresh
810        Refresh();
811}
812
813int wxListCtrlEx::GetColumnVisibleIndex(int col)
814{
815        if (!m_pVisibleColumnMapping)
816                return -1;
817
818        for (int i = 0; i < GetColumnCount(); i++)
819        {
820                if (m_pVisibleColumnMapping[i] == (unsigned int)col)
821                        return i;
822        }
823
824        return -1;
825}
826
827int wxListCtrlEx::GetHeaderSortIconIndex(int col)
828{
829        if (col < 0 || col >= GetColumnCount())
830                return -1;
831
832#ifdef __WXMSW__
833        HWND hWnd = (HWND)GetHandle();
834        HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
835
836        HDITEM item;
837        item.mask = HDI_IMAGE | HDI_FORMAT;
838        SendMessage(header, HDM_GETITEM, col, (LPARAM)&item);
839
840        if (!(item.fmt & HDF_IMAGE))
841                return -1;
842
843        return item.iImage;
844#else
845        wxListItem item;
846        if (!GetColumn(col, item))
847                return -1;
848
849        return item.GetImage();
850#endif
851}
852
853void wxListCtrlEx::InitHeaderSortImageList()
854{
855#ifdef __WXMSW__
856        if (wxPlatformInfo::Get().GetOSMajorVersion() >= 6)
857                return;
858
859        // Initialize imagelist for list header
860        m_pHeaderImageList = new wxImageListEx(8, 8, true, 3);
861
862        wxBitmap bmp;
863
864        bmp.LoadFile(wxGetApp().GetResourceDir().GetPath() + _T("up.png"), wxBITMAP_TYPE_PNG);
865        m_pHeaderImageList->Add(bmp);
866        bmp.LoadFile(wxGetApp().GetResourceDir().GetPath() + _T("down.png"), wxBITMAP_TYPE_PNG);
867        m_pHeaderImageList->Add(bmp);
868
869        HWND hWnd = (HWND)GetHandle();
870        if (!hWnd)
871        {
872                delete m_pHeaderImageList;
873                m_pHeaderImageList = 0;
874                return;
875        }
876
877        HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
878        if (!header)
879        {
880                delete m_pHeaderImageList;
881                m_pHeaderImageList = 0;
882                return;
883        }
884
885        TCHAR buffer[1000] = {0};
886        HDITEM item;
887        item.mask = HDI_TEXT;
888        item.pszText = buffer;
889        item.cchTextMax = 999;
890        SendMessage(header, HDM_GETITEM, 0, (LPARAM)&item);
891
892        SendMessage(header, HDM_SETIMAGELIST, 0, (LPARAM)m_pHeaderImageList->GetHandle());
893
894        m_header_icon_index.up = 0;
895        m_header_icon_index.down = 1;
896#else
897
898        wxColour colour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
899
900        wxString lightness;
901        if (colour.Red() + colour.Green() + colour.Blue() > 3 * 128)
902                lightness = _T("DARK");
903        else
904                lightness = _T("LIGHT");
905
906        wxBitmap bmp;
907
908        bmp = wxArtProvider::GetBitmap(_T("ART_SORT_UP_") + lightness, wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall));
909        m_header_icon_index.up = GetSystemImageList()->Add(bmp);
910        bmp = wxArtProvider::GetBitmap(_T("ART_SORT_DOWN_") + lightness, wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall));
911        m_header_icon_index.down = GetSystemImageList()->Add(bmp);
912#endif
913}
914
915void wxListCtrlEx::SetHeaderSortIconIndex(int col, int icon)
916{
917        if (col < 0 || col >= GetColumnCount())
918                return;
919
920#ifdef __WXMSW__
921        HWND hWnd = (HWND)GetHandle();
922        HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
923
924        wxChar buffer[100];
925        HDITEM item = {0};
926        item.mask = HDI_TEXT | HDI_FORMAT;
927        item.pszText = buffer;
928        item.cchTextMax = 99;
929        SendMessage(header, HDM_GETITEM, col, (LPARAM)&item);
930        if (icon != -1)
931        {
932                if (wxPlatformInfo::Get().GetOSMajorVersion() >= 6)
933                {
934                        item.fmt &= ~(HDF_IMAGE | HDF_BITMAP_ON_RIGHT | HDF_SORTUP | HDF_SORTDOWN);
935                        item.iImage = -1;
936                        if (icon)
937                                item.fmt |= HDF_SORTDOWN;
938                        else
939                                item.fmt |= HDF_SORTUP;
940                }
941                else
942                {
943                        item.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
944                        item.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
945                        item.iImage = icon ? m_header_icon_index.down : m_header_icon_index.up;
946                        item.mask |= HDI_IMAGE;
947                }
948        }
949        else
950        {
951                item.fmt &= ~(HDF_IMAGE | HDF_BITMAP_ON_RIGHT | HDF_SORTUP | HDF_SORTDOWN);
952                item.iImage = -1;
953        }
954        SendMessage(header, HDM_SETITEM, col, (LPARAM)&item);
955#else
956        wxListItem item;
957        if (!GetColumn(col, item))
958                return;
959
960        if( icon != -1 )
961                icon = icon ? m_header_icon_index.down : m_header_icon_index.up;
962
963        item.SetImage(icon);
964        SetColumn(col, item);
965#endif
966}
967
968void wxListCtrlEx::RefreshListOnly(bool eraseBackground /*=true*/)
969{
970        // See comment in wxGenericListCtrl::Refresh
971        GetMainWindow()->Refresh(eraseBackground);
972}
973
974void wxListCtrlEx::CancelLabelEdit()
975{
976#ifdef __WXMSW__
977        if (GetEditControl())
978                ListView_CancelEditLabel((HWND)GetHandle());
979#else
980        m_editing = false;
981        wxTextCtrl* pEdit = GetEditControl();
982        if (pEdit)
983        {
984                wxKeyEvent evt(wxEVT_CHAR);
985                evt.m_keyCode = WXK_ESCAPE;
986                pEdit->GetEventHandler()->ProcessEvent(evt);
987        }
988#endif
989
990}
991
992void wxListCtrlEx::OnBeginLabelEdit(wxListEvent& event)
993{
994#ifndef __WXMSW__
995        if (m_editing)
996        {
997                event.Veto();
998                return;
999        }
1000#endif
1001        if (m_blockedLabelEditing)
1002        {
1003                event.Veto();
1004                return;
1005        }
1006
1007        if (!OnBeginRename(event))
1008                event.Veto();
1009#ifndef __WXMSW__
1010        else
1011                m_editing = true;
1012#endif
1013}
1014
1015void wxListCtrlEx::OnEndLabelEdit(wxListEvent& event)
1016{
1017#ifdef __WXMAC__
1018        int item = event.GetIndex();
1019        if (item != -1)
1020        {
1021                int to = item + 1;
1022                if (to < GetItemCount())
1023                {
1024                        int from = item;
1025                        if (from)
1026                                from--;
1027                        RefreshItems(from, to);
1028                }
1029                else
1030                        RefreshListOnly();
1031        }
1032#endif
1033
1034#ifndef __WXMSW__
1035        if (!m_editing)
1036        {
1037                event.Veto();
1038                return;
1039        }
1040        m_editing = false;
1041#endif
1042
1043        if (event.IsEditCancelled())
1044                return;
1045
1046        if (!OnAcceptRename(event))
1047                event.Veto();
1048}
1049
1050bool wxListCtrlEx::OnBeginRename(const wxListEvent&)
1051{
1052        return false;
1053}
1054
1055bool wxListCtrlEx::OnAcceptRename(const wxListEvent&)
1056{
1057        return false;
1058}
1059
1060void wxListCtrlEx::SetLabelEditBlock(bool block)
1061{
1062        if (block)
1063        {
1064                CancelLabelEdit();
1065                ++m_blockedLabelEditing;
1066        }
1067        else
1068        {
1069                wxASSERT(m_blockedLabelEditing);
1070                if (m_blockedLabelEditing > 0)
1071                        m_blockedLabelEditing--;
1072        }
1073}
1074
1075CLabelEditBlocker::CLabelEditBlocker(wxListCtrlEx& listCtrl)
1076        : m_listCtrl(listCtrl)
1077{
1078        m_listCtrl.SetLabelEditBlock(true);
1079}
1080
1081CLabelEditBlocker::~CLabelEditBlocker()
1082{
1083        m_listCtrl.SetLabelEditBlock(false);
1084}
1085
1086void wxListCtrlEx::OnColumnDragging(wxListEvent& event)
1087{
1088        if (event.GetItem().GetWidth() < MIN_COLUMN_WIDTH)
1089                event.Veto();
1090}
1091
1092#ifdef __WXMSW__
1093bool wxListCtrlEx::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1094{
1095        // MSW doesn't generate HDN_TRACK on all header styles, so handle it
1096        // ourselves using HDN_ITEMCHANGING.
1097        NMHDR *nmhdr = (NMHDR *)lParam;
1098        HWND hwndHdr = ListView_GetHeader((HWND)GetHandle());
1099
1100        if (nmhdr->hwndFrom != hwndHdr)
1101                return wxListCtrl::MSWOnNotify(idCtrl, lParam, result);
1102
1103        HD_NOTIFY *nmHDR = (HD_NOTIFY *)nmhdr;
1104
1105        switch ( nmhdr->code )
1106        {
1107                // See comment in src/msw/listctrl.cpp of wx why both A and W are needed
1108        case HDN_BEGINTRACKA:
1109        case HDN_BEGINTRACKW:
1110                m_columnDragging = true;
1111                break;
1112        case HDN_ENDTRACKA:
1113        case HDN_ENDTRACKW:
1114                m_columnDragging = true;
1115                break;
1116        case HDN_ITEMCHANGINGA:
1117        case HDN_ITEMCHANGINGW:
1118                if (m_columnDragging) {
1119                        if (nmHDR->pitem->mask & HDI_WIDTH && nmHDR->pitem->cxy < MIN_COLUMN_WIDTH) {
1120                                *result = 1;
1121                                return true;
1122                        }
1123                        else {
1124                                *result = 0;
1125                                return false;
1126                        }
1127                }
1128                else
1129                        return false;
1130        case HDN_DIVIDERDBLCLICK:
1131                {
1132                        wxListEvent event(wxEVT_LIST_COL_END_DRAG, GetId());
1133                        event.SetEventObject(this);
1134                        AddPendingEvent(event);
1135                }
1136                break;
1137        }
1138
1139        return wxListCtrl::MSWOnNotify(idCtrl, lParam, result);
1140}
1141#endif
1142
1143bool wxListCtrlEx::HasSelection() const
1144{
1145#ifndef __WXMSW__
1146        // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
1147        return GetSelectedItemCount() != 0;
1148#else
1149        return GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1;
1150#endif
1151}
1152
1153
1154wxRect wxListCtrlEx::GetActualClientRect() const
1155{
1156        wxRect windowRect = GetMainWindow()->GetClientRect();
1157#ifdef __WXMSW__
1158        wxRect topRect;
1159        if (GetItemRect(0, topRect)) {
1160                windowRect.height -= topRect.y;
1161                windowRect.y += topRect.y;
1162        }
1163#endif
1164
1165        return windowRect;
1166}
Note: See TracBrowser for help on using the repository browser.