source: filezilla/trunk/fuentes/src/interface/listctrlex.cpp @ 130

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

First release to xenial

File size: 24.8 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        CDateTime now = CDateTime::Now();
155        if (m_prefixSearch_lastKeyPress.IsValid()) {
156                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        const unsigned int count = m_columnInfo.size();
539
540        wxString savedWidths = COptions::Get()->GetOption(optionId);
541        wxStringTokenizer tokens(savedWidths, _T(" "));
542        if (tokens.CountTokens() < count)
543                return false;
544
545        unsigned long* newWidths = new unsigned long[count];
546        for (unsigned int i = 0; i < count; i++)
547        {
548                wxString token = tokens.GetNextToken();
549                if (!token.ToULong(newWidths + i) || newWidths[i] > 5000)
550                {
551                        delete [] newWidths;
552                        return false;
553                }
554                else if (newWidths[i] < MIN_COLUMN_WIDTH)
555                        newWidths[i] = MIN_COLUMN_WIDTH;
556        }
557
558        for (unsigned int i = 0; i < count; i++)
559                m_columnInfo[i].width = newWidths[i];
560
561        delete [] newWidths;
562        return true;
563}
564
565void wxListCtrlEx::SaveColumnWidths(unsigned int optionId)
566{
567        const unsigned int count = m_columnInfo.size();
568
569        wxString widths;
570        for (unsigned int i = 0; i < count; i++)
571        {
572                int width = 0;
573
574                bool found = false;
575                for (int j = 0; j < GetColumnCount(); j++)
576                {
577                        if (m_pVisibleColumnMapping[j] != i)
578                                continue;
579
580                        found = true;
581                        width = GetColumnWidth(j);
582                }
583                if (!found)
584                        width = m_columnInfo[i].width;
585                widths += wxString::Format(_T("%d "), width);
586        }
587        widths.RemoveLast();
588
589        COptions::Get()->SetOption(optionId, widths);
590}
591
592
593void wxListCtrlEx::AddColumn(const wxString& name, int align, int initialWidth, bool fixed /*=false*/)
594{
595        wxASSERT(!GetColumnCount());
596
597        t_columnInfo info;
598        info.name = name;
599        info.align = align;
600        info.width = initialWidth;
601        info.shown = true;
602        info.order = m_columnInfo.size();
603        info.fixed = fixed;
604
605        m_columnInfo.push_back(info);
606}
607
608// Moves column. Target position includes both hidden
609// as well as shown columns
610void wxListCtrlEx::MoveColumn(unsigned int col, unsigned int before)
611{
612        if (m_columnInfo[col].order == before)
613                return;
614
615        for (unsigned int i = 0; i < m_columnInfo.size(); ++i) {
616                if (i == col)
617                        continue;
618
619                t_columnInfo& info = m_columnInfo[i];
620                if (info.order > col)
621                        --info.order;
622                if (info.order >= before)
623                        ++info.order;
624        }
625
626        t_columnInfo& info = m_columnInfo[col];
627
628        if (info.shown) {
629                int icon = -1;
630                // Remove old column
631                for (unsigned int i = 0; i < (unsigned int)GetColumnCount(); ++i) {
632                        if (m_pVisibleColumnMapping[i] != col)
633                                continue;
634
635                        for (unsigned int j = i + 1; j < (unsigned int)GetColumnCount(); ++j)
636                                m_pVisibleColumnMapping[j - 1] = m_pVisibleColumnMapping[j];
637                        info.width = GetColumnWidth(i);
638
639                        icon = GetHeaderSortIconIndex(i);
640                        DeleteColumn(i);
641
642                        break;
643                }
644
645                // Insert new column
646                unsigned int pos = 0;
647                for (unsigned int i = 0; i < m_columnInfo.size(); ++i) {
648                        if (i == col)
649                                continue;
650                        t_columnInfo& info2 = m_columnInfo[i];
651                        if (info2.shown && info2.order < before) {
652                                ++pos;
653                        }
654                }
655                for (unsigned int i = (int)GetColumnCount(); i > pos; --i)
656                        m_pVisibleColumnMapping[i] = m_pVisibleColumnMapping[i - 1];
657                m_pVisibleColumnMapping[pos] = col;
658
659                InsertColumn(pos, info.name, info.align, info.width);
660
661                SetHeaderSortIconIndex(pos, icon);
662        }
663        m_columnInfo[col].order = before;
664}
665
666void wxListCtrlEx::CreateVisibleColumnMapping()
667{
668        int pos = 0;
669        for (unsigned int j = 0; j < m_columnInfo.size(); j++)
670        {
671                for (unsigned int i = 0; i < m_columnInfo.size(); i++)
672                {
673                        const t_columnInfo &column = m_columnInfo[i];
674
675                        if (!column.shown)
676                                continue;
677
678                        if (column.order != j)
679                                continue;
680
681                        m_pVisibleColumnMapping[pos] = i;
682                        InsertColumn(pos++, column.name, column.align, column.width);
683                }
684        }
685}
686
687class CColumnEditDialog : public wxDialogEx
688{
689public:
690        int *m_order;
691        DECLARE_EVENT_TABLE()
692
693protected:
694        void OnUp(wxCommandEvent& event)
695        {
696                wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox);
697                int sel = pListBox->GetSelection();
698                if (sel < 2)
699                        return;
700
701                int tmp;
702                tmp = m_order[sel - 1];
703                m_order[sel - 1] = m_order[sel];
704                m_order[sel] = tmp;
705
706                wxString name = pListBox->GetString(sel);
707                bool checked = pListBox->IsChecked(sel);
708                pListBox->Delete(sel);
709                pListBox->Insert(name, sel - 1);
710                pListBox->Check(sel - 1, checked);
711                pListBox->SetSelection(sel - 1);
712
713                wxCommandEvent evt;
714                OnSelChanged(evt);
715        }
716
717        void OnDown(wxCommandEvent& event)
718        {
719                wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox);
720                int sel = pListBox->GetSelection();
721                if (sel < 1)
722                        return;
723                if (sel >= (int)pListBox->GetCount() - 1)
724                        return;
725
726                int tmp;
727                tmp = m_order[sel + 1];
728                m_order[sel + 1] = m_order[sel];
729                m_order[sel] = tmp;
730
731                wxString name = pListBox->GetString(sel);
732                bool checked = pListBox->IsChecked(sel);
733                pListBox->Delete(sel);
734                pListBox->Insert(name, sel + 1);
735                pListBox->Check(sel + 1, checked);
736                pListBox->SetSelection(sel + 1);
737
738                wxCommandEvent evt;
739                OnSelChanged(evt);
740        }
741
742        void OnSelChanged(wxCommandEvent& event)
743        {
744                wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox);
745                int sel = pListBox->GetSelection();
746                XRCCTRL(*this, "ID_UP", wxButton)->Enable(sel > 1);
747                XRCCTRL(*this, "ID_DOWN", wxButton)->Enable(sel > 0 && sel < (int)pListBox->GetCount() - 1);
748        }
749
750        void OnCheck(wxCommandEvent& event)
751        {
752                if (!event.GetSelection() && !event.IsChecked())
753                {
754                        wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox);
755                        pListBox->Check(0);
756                        wxMessageBoxEx(_("The filename column can neither be hidden nor moved."), _("Column properties"));
757                }
758        }
759};
760
761BEGIN_EVENT_TABLE(CColumnEditDialog, wxDialogEx)
762EVT_BUTTON(XRCID("ID_UP"), CColumnEditDialog::OnUp)
763EVT_BUTTON(XRCID("ID_DOWN"), CColumnEditDialog::OnDown)
764EVT_LISTBOX(wxID_ANY, CColumnEditDialog::OnSelChanged)
765EVT_CHECKLISTBOX(wxID_ANY, CColumnEditDialog::OnCheck)
766END_EVENT_TABLE()
767
768void wxListCtrlEx::ShowColumnEditor()
769{
770        CColumnEditDialog dlg;
771
772        if (!dlg.Load(this, _T("ID_COLUMN_SETUP"))) {
773                wxBell();
774                return;
775        }
776
777        wxCheckListBox* pListBox = XRCCTRL(dlg, "ID_ACTIVE", wxCheckListBox);
778
779        dlg.m_order = new int[m_columnInfo.size()];
780        for (unsigned int j = 0; j < m_columnInfo.size(); j++)
781        {
782                for (unsigned int i = 0; i < m_columnInfo.size(); i++)
783                {
784                        if (m_columnInfo[i].order != j)
785                                continue;
786                        dlg.m_order[j] = i;
787                        pListBox->Append(m_columnInfo[i].name);
788                        if (m_columnInfo[i].shown)
789                                pListBox->Check(j);
790                }
791        }
792        wxASSERT(pListBox->GetCount() == m_columnInfo.size());
793
794        dlg.GetSizer()->Fit(&dlg);
795
796        if (dlg.ShowModal() != wxID_OK)
797        {
798                delete [] dlg.m_order;
799                return;
800        }
801
802        for (unsigned int i = 0; i < m_columnInfo.size(); i++)
803        {
804                int col = dlg.m_order[i];
805                bool isChecked = pListBox->IsChecked(i);
806                if (!isChecked && !col)
807                {
808                        isChecked = true;
809                        wxMessageBoxEx(_("The filename column cannot be hidden."));
810                }
811                MoveColumn(col, i);
812                if (m_columnInfo[col].shown != isChecked)
813                        ShowColumn(col, isChecked);
814        }
815
816        delete [] dlg.m_order;
817
818        // Generic wxListCtrl needs manual refresh
819        Refresh();
820}
821
822int wxListCtrlEx::GetColumnVisibleIndex(int col)
823{
824        if (!m_pVisibleColumnMapping)
825                return -1;
826
827        for (int i = 0; i < GetColumnCount(); i++)
828        {
829                if (m_pVisibleColumnMapping[i] == (unsigned int)col)
830                        return i;
831        }
832
833        return -1;
834}
835
836int wxListCtrlEx::GetHeaderSortIconIndex(int col)
837{
838        if (col < 0 || col >= GetColumnCount())
839                return -1;
840
841#ifdef __WXMSW__
842        HWND hWnd = (HWND)GetHandle();
843        HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
844
845        HDITEM item;
846        item.mask = HDI_IMAGE | HDI_FORMAT;
847        SendMessage(header, HDM_GETITEM, col, (LPARAM)&item);
848
849        if (!(item.fmt & HDF_IMAGE))
850                return -1;
851
852        return item.iImage;
853#else
854        wxListItem item;
855        if (!GetColumn(col, item))
856                return -1;
857
858        return item.GetImage();
859#endif
860}
861
862void wxListCtrlEx::InitHeaderSortImageList()
863{
864#ifdef __WXMSW__
865        if (wxPlatformInfo::Get().GetOSMajorVersion() >= 6)
866                return;
867
868        // Initialize imagelist for list header
869        m_pHeaderImageList = new wxImageListEx(8, 8, true, 3);
870
871        wxBitmap bmp;
872
873        bmp.LoadFile(wxGetApp().GetResourceDir().GetPath() + _T("up.png"), wxBITMAP_TYPE_PNG);
874        m_pHeaderImageList->Add(bmp);
875        bmp.LoadFile(wxGetApp().GetResourceDir().GetPath() + _T("down.png"), wxBITMAP_TYPE_PNG);
876        m_pHeaderImageList->Add(bmp);
877
878        HWND hWnd = (HWND)GetHandle();
879        if (!hWnd)
880        {
881                delete m_pHeaderImageList;
882                m_pHeaderImageList = 0;
883                return;
884        }
885
886        HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
887        if (!header)
888        {
889                delete m_pHeaderImageList;
890                m_pHeaderImageList = 0;
891                return;
892        }
893
894        TCHAR buffer[1000] = {0};
895        HDITEM item;
896        item.mask = HDI_TEXT;
897        item.pszText = buffer;
898        item.cchTextMax = 999;
899        SendMessage(header, HDM_GETITEM, 0, (LPARAM)&item);
900
901        SendMessage(header, HDM_SETIMAGELIST, 0, (LPARAM)m_pHeaderImageList->GetHandle());
902
903        m_header_icon_index.up = 0;
904        m_header_icon_index.down = 1;
905#else
906
907        wxColour colour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
908
909        wxString lightness;
910        if (colour.Red() + colour.Green() + colour.Blue() > 3 * 128)
911                lightness = _T("DARK");
912        else
913                lightness = _T("LIGHT");
914
915        wxBitmap bmp;
916
917        bmp = wxArtProvider::GetBitmap(_T("ART_SORT_UP_") + lightness, wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall));
918        m_header_icon_index.up = GetSystemImageList()->Add(bmp);
919        bmp = wxArtProvider::GetBitmap(_T("ART_SORT_DOWN_") + lightness, wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall));
920        m_header_icon_index.down = GetSystemImageList()->Add(bmp);
921#endif
922}
923
924void wxListCtrlEx::SetHeaderSortIconIndex(int col, int icon)
925{
926        if (col < 0 || col >= GetColumnCount())
927                return;
928
929#ifdef __WXMSW__
930        HWND hWnd = (HWND)GetHandle();
931        HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
932
933        wxChar buffer[100];
934        HDITEM item = {0};
935        item.mask = HDI_TEXT | HDI_FORMAT;
936        item.pszText = buffer;
937        item.cchTextMax = 99;
938        SendMessage(header, HDM_GETITEM, col, (LPARAM)&item);
939        if (icon != -1)
940        {
941                if (wxPlatformInfo::Get().GetOSMajorVersion() >= 6)
942                {
943                        item.fmt &= ~(HDF_IMAGE | HDF_BITMAP_ON_RIGHT | HDF_SORTUP | HDF_SORTDOWN);
944                        item.iImage = -1;
945                        if (icon)
946                                item.fmt |= HDF_SORTDOWN;
947                        else
948                                item.fmt |= HDF_SORTUP;
949                }
950                else
951                {
952                        item.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
953                        item.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
954                        item.iImage = icon ? m_header_icon_index.down : m_header_icon_index.up;
955                        item.mask |= HDI_IMAGE;
956                }
957        }
958        else
959        {
960                item.fmt &= ~(HDF_IMAGE | HDF_BITMAP_ON_RIGHT | HDF_SORTUP | HDF_SORTDOWN);
961                item.iImage = -1;
962        }
963        SendMessage(header, HDM_SETITEM, col, (LPARAM)&item);
964#else
965        wxListItem item;
966        if (!GetColumn(col, item))
967                return;
968
969        if( icon != -1 )
970                icon = icon ? m_header_icon_index.down : m_header_icon_index.up;
971
972        item.SetImage(icon);
973        SetColumn(col, item);
974#endif
975}
976
977void wxListCtrlEx::RefreshListOnly(bool eraseBackground /*=true*/)
978{
979        // See comment in wxGenericListCtrl::Refresh
980        GetMainWindow()->Refresh(eraseBackground);
981}
982
983void wxListCtrlEx::CancelLabelEdit()
984{
985#ifdef __WXMSW__
986        if (GetEditControl())
987                ListView_CancelEditLabel((HWND)GetHandle());
988#else
989        m_editing = false;
990        wxTextCtrl* pEdit = GetEditControl();
991        if (pEdit)
992        {
993                wxKeyEvent evt(wxEVT_CHAR);
994                evt.m_keyCode = WXK_ESCAPE;
995                pEdit->GetEventHandler()->ProcessEvent(evt);
996        }
997#endif
998
999}
1000
1001void wxListCtrlEx::OnBeginLabelEdit(wxListEvent& event)
1002{
1003#ifndef __WXMSW__
1004        if (m_editing)
1005        {
1006                event.Veto();
1007                return;
1008        }
1009#endif
1010        if (m_blockedLabelEditing)
1011        {
1012                event.Veto();
1013                return;
1014        }
1015
1016        if (!OnBeginRename(event))
1017                event.Veto();
1018#ifndef __WXMSW__
1019        else
1020                m_editing = true;
1021#endif
1022}
1023
1024void wxListCtrlEx::OnEndLabelEdit(wxListEvent& event)
1025{
1026#ifdef __WXMAC__
1027        int item = event.GetIndex();
1028        if (item != -1)
1029        {
1030                int to = item + 1;
1031                if (to < GetItemCount())
1032                {
1033                        int from = item;
1034                        if (from)
1035                                from--;
1036                        RefreshItems(from, to);
1037                }
1038                else
1039                        RefreshListOnly();
1040        }
1041#endif
1042
1043#ifndef __WXMSW__
1044        if (!m_editing)
1045        {
1046                event.Veto();
1047                return;
1048        }
1049        m_editing = false;
1050#endif
1051
1052        if (event.IsEditCancelled())
1053                return;
1054
1055        if (!OnAcceptRename(event))
1056                event.Veto();
1057}
1058
1059bool wxListCtrlEx::OnBeginRename(const wxListEvent& event)
1060{
1061        return false;
1062}
1063
1064bool wxListCtrlEx::OnAcceptRename(const wxListEvent& event)
1065{
1066        return false;
1067}
1068
1069void wxListCtrlEx::SetLabelEditBlock(bool block)
1070{
1071        if (block)
1072        {
1073                CancelLabelEdit();
1074                ++m_blockedLabelEditing;
1075        }
1076        else
1077        {
1078                wxASSERT(m_blockedLabelEditing);
1079                if (m_blockedLabelEditing > 0)
1080                        m_blockedLabelEditing--;
1081        }
1082}
1083
1084CLabelEditBlocker::CLabelEditBlocker(wxListCtrlEx& listCtrl)
1085        : m_listCtrl(listCtrl)
1086{
1087        m_listCtrl.SetLabelEditBlock(true);
1088}
1089
1090CLabelEditBlocker::~CLabelEditBlocker()
1091{
1092        m_listCtrl.SetLabelEditBlock(false);
1093}
1094
1095void wxListCtrlEx::OnColumnDragging(wxListEvent& event)
1096{
1097        if (event.GetItem().GetWidth() < MIN_COLUMN_WIDTH)
1098                event.Veto();
1099}
1100
1101#ifdef __WXMSW__
1102bool wxListCtrlEx::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1103{
1104        // MSW doesn't generate HDN_TRACK on all header styles, so handle it
1105        // ourselves using HDN_ITEMCHANGING.
1106        NMHDR *nmhdr = (NMHDR *)lParam;
1107        HWND hwndHdr = ListView_GetHeader((HWND)GetHandle());
1108
1109        if (nmhdr->hwndFrom != hwndHdr)
1110                return wxListCtrl::MSWOnNotify(idCtrl, lParam, result);
1111
1112        HD_NOTIFY *nmHDR = (HD_NOTIFY *)nmhdr;
1113
1114        switch ( nmhdr->code )
1115        {
1116                // See comment in src/msw/listctrl.cpp of wx why both A and W are needed
1117        case HDN_BEGINTRACKA:
1118        case HDN_BEGINTRACKW:
1119                m_columnDragging = true;
1120                break;
1121        case HDN_ENDTRACKA:
1122        case HDN_ENDTRACKW:
1123                m_columnDragging = true;
1124                break;
1125        case HDN_ITEMCHANGINGA:
1126        case HDN_ITEMCHANGINGW:
1127                if (m_columnDragging)
1128                {
1129                        if (nmHDR->pitem->mask & HDI_WIDTH && nmHDR->pitem->cxy < MIN_COLUMN_WIDTH)
1130                        {
1131                                *result = 1;
1132                                return true;
1133                        }
1134                        else
1135                        {
1136                                *result = 0;
1137                                return false;
1138                        }
1139                }
1140                else
1141                        return false;
1142        }
1143
1144        return wxListCtrl::MSWOnNotify(idCtrl, lParam, result);
1145}
1146#endif
1147
1148bool wxListCtrlEx::HasSelection() const
1149{
1150#ifndef __WXMSW__
1151        // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
1152        return GetSelectedItemCount() != 0;
1153#else
1154        return GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1;
1155#endif
1156}
1157
1158
1159wxRect wxListCtrlEx::GetActualClientRect() const
1160{
1161        wxRect windowRect = GetMainWindow()->GetClientRect();
1162#ifdef __WXMSW__
1163        wxRect topRect;
1164        if (GetItemRect(0, topRect)) {
1165                windowRect.height -= topRect.y;
1166                windowRect.y += topRect.y;
1167        }
1168#endif
1169
1170        return windowRect;
1171}
Note: See TracBrowser for help on using the repository browser.