#include #include "listctrlex.h" #include "filezillaapp.h" #include #include #include "Options.h" #include "dialogex.h" #ifdef __WXMSW__ #include "commctrl.h" #ifndef HDF_SORTUP #define HDF_SORTUP 0x0400 #endif #ifndef HDF_SORTDOWN #define HDF_SORTDOWN 0x0200 #endif #else #include "themeprovider.h" #endif DECLARE_EVENT_TYPE(fzEVT_POSTSCROLL, -1) DEFINE_EVENT_TYPE(fzEVT_POSTSCROLL) BEGIN_EVENT_TABLE(wxListCtrlEx, wxListCtrlExBase) EVT_COMMAND(wxID_ANY, fzEVT_POSTSCROLL, wxListCtrlEx::OnPostScrollEvent) EVT_SCROLLWIN(wxListCtrlEx::OnScrollEvent) EVT_MOUSEWHEEL(wxListCtrlEx::OnMouseWheel) EVT_LIST_ITEM_FOCUSED(wxID_ANY, wxListCtrlEx::OnSelectionChanged) EVT_LIST_ITEM_SELECTED(wxID_ANY, wxListCtrlEx::OnSelectionChanged) EVT_KEY_DOWN(wxListCtrlEx::OnKeyDown) EVT_LIST_BEGIN_LABEL_EDIT(wxID_ANY, wxListCtrlEx::OnBeginLabelEdit) EVT_LIST_END_LABEL_EDIT(wxID_ANY, wxListCtrlEx::OnEndLabelEdit) #ifndef __WXMSW__ EVT_LIST_COL_DRAGGING(wxID_ANY, wxListCtrlEx::OnColumnDragging) #endif END_EVENT_TABLE() #define MIN_COLUMN_WIDTH 12 wxListCtrlEx::wxListCtrlEx(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator, const wxString& name) { Create(parent, id, pos, size, style, validator, name); #ifndef __WXMSW__ m_editing = false; #else m_columnDragging = false; #endif m_blockedLabelEditing = false; } wxListCtrlEx::~wxListCtrlEx() { #ifdef __WXMSW__ delete m_pHeaderImageList; #endif delete [] m_pVisibleColumnMapping; } wxWindow* wxListCtrlEx::GetMainWindow() { #ifdef __WXMSW__ return this; #else return reinterpret_cast(m_mainWin); #endif } wxWindow const* wxListCtrlEx::GetMainWindow() const { #ifdef __WXMSW__ return this; #else return reinterpret_cast(m_mainWin); #endif } void wxListCtrlEx::OnPostScroll() { } void wxListCtrlEx::OnPostScrollEvent(wxCommandEvent&) { OnPostScroll(); } void wxListCtrlEx::OnPreEmitPostScrollEvent() { EmitPostScrollEvent(); } void wxListCtrlEx::EmitPostScrollEvent() { QueueEvent(new wxCommandEvent(fzEVT_POSTSCROLL, wxID_ANY)); } void wxListCtrlEx::OnScrollEvent(wxScrollWinEvent& event) { event.Skip(); OnPreEmitPostScrollEvent(); } void wxListCtrlEx::OnMouseWheel(wxMouseEvent& event) { event.Skip(); OnPreEmitPostScrollEvent(); } void wxListCtrlEx::OnSelectionChanged(wxListEvent& event) { event.Skip(); OnPreEmitPostScrollEvent(); } void wxListCtrlEx::ScrollTopItem(int item) { if (!GetItemCount()) { return; } if (item < 0) { item = 0; } else if (item >= GetItemCount()) { item = GetItemCount() - 1; } const int current = GetTopItem(); int delta = item - current; if (!delta) return; wxRect rect; GetItemRect(current, rect, wxLIST_RECT_BOUNDS); delta *= rect.GetHeight(); ScrollList(0, delta); } void wxListCtrlEx::HandlePrefixSearch(wxChar character) { wxASSERT(character); // Keyboard navigation within items fz::datetime now = fz::datetime::now(); if (m_prefixSearch_lastKeyPress.empty()) { fz::duration span = now - m_prefixSearch_lastKeyPress; if (span.get_seconds() >= 1) { m_prefixSearch_prefix = _T(""); } } m_prefixSearch_lastKeyPress = now; wxString newPrefix = m_prefixSearch_prefix + character; int item; #ifndef __WXMSW__ // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1) if (!GetSelectedItemCount()) item = -1; else #endif { item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); } bool beep = false; if (item != -1) { wxString text = GetItemText(item, 0); if (text.Length() >= m_prefixSearch_prefix.Length() && !m_prefixSearch_prefix.CmpNoCase(text.Left(m_prefixSearch_prefix.Length()))) beep = true; } else if (m_prefixSearch_prefix.empty()) beep = true; int start = item; if (start < 0) start = 0; int newPos = FindItemWithPrefix(newPrefix, start); if (newPos == -1 && (m_prefixSearch_prefix.Len() == 1 && m_prefixSearch_prefix[0] == character) && item != -1 && beep) { // Search the next item that starts with the same letter newPrefix = m_prefixSearch_prefix; newPos = FindItemWithPrefix(newPrefix, item + 1); } m_prefixSearch_prefix = newPrefix; if (newPos == -1) { if (beep) wxBell(); return; } while (item != -1) { SetItemState(item, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED); item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); } SetItemState(newPos, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED); #ifdef __WXMSW__ // SetItemState does not move the selection mark, that is the item from // which a multiple selection starts (e.g. shift+up/down) HWND hWnd = (HWND)GetHandle(); ::SendMessage(hWnd, LVM_SETSELECTIONMARK, 0, newPos); #endif EnsureVisible(newPos); } void wxListCtrlEx::OnKeyDown(wxKeyEvent& event) { if (!m_prefixSearch_enabled) { event.Skip(); return; } int code = event.GetKeyCode(); if (code == WXK_LEFT || code == WXK_RIGHT || code == WXK_UP || code == WXK_DOWN || code == WXK_HOME || code == WXK_END) { ResetSearchPrefix(); event.Skip(); return; } if (event.AltDown() && !event.ControlDown()) // Alt but not AltGr { event.Skip(); return; } wxChar key; switch (code) { case WXK_NUMPAD0: case WXK_NUMPAD1: case WXK_NUMPAD2: case WXK_NUMPAD3: case WXK_NUMPAD4: case WXK_NUMPAD5: case WXK_NUMPAD6: case WXK_NUMPAD7: case WXK_NUMPAD8: case WXK_NUMPAD9: key = '0' + code - WXK_NUMPAD0; break; case WXK_NUMPAD_ADD: key = '+'; break; case WXK_NUMPAD_SUBTRACT: key = '-'; break; case WXK_NUMPAD_MULTIPLY: key = '*'; break; case WXK_NUMPAD_DIVIDE: key = '/'; break; default: key = 0; break; } if (key) { if (event.GetModifiers()) { // Numpad keys can not have modifiers event.Skip(); } HandlePrefixSearch(key); return; } #if defined(__WXMSW__) if (code >= 300 && code != WXK_NUMPAD_DECIMAL) { event.Skip(); return; } // Get the actual key BYTE state[256]; if (!GetKeyboardState(state)) { event.Skip(); return; } wxChar buffer[1]; int res = ToUnicode(event.GetRawKeyCode(), 0, state, buffer, 1, 0); if (res != 1) { event.Skip(); return; } key = buffer[0]; if (key < 32) { event.Skip(); return; } if (key == 32 && event.HasModifiers()) { event.Skip(); return; } HandlePrefixSearch(key); return; #else if (code > 32 && code < 300 && !event.HasModifiers()) { int unicodeKey = event.GetUnicodeKey(); if (unicodeKey) code = unicodeKey; HandlePrefixSearch(code); } else event.Skip(); #endif //defined(__WXMSW__) } // Declared const due to design error in wxWidgets. // Won't be fixed since a fix would break backwards compatibility // Both functions use a const_cast(this) and modify // the instance. wxString wxListCtrlEx::OnGetItemText(long item, long column) const { wxListCtrlEx *pThis = const_cast(this); return pThis->GetItemText(item, (unsigned int)m_pVisibleColumnMapping[column]); } int wxListCtrlEx::FindItemWithPrefix(const wxString& searchPrefix, int start) { const int count = GetItemCount(); for (int i = start; i < (count + start); i++) { int item = i % count; wxString namePrefix = GetItemText(item, 0).Left(searchPrefix.Length()); if (!namePrefix.CmpNoCase(searchPrefix)) return i % count; } return -1; } void wxListCtrlEx::SaveSetItemCount(long count) { #ifndef __WXMSW__ if (count < GetItemCount()) { int focused = GetNextItem(count - 1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED); if (focused != -1) SetItemState(focused, 0, wxLIST_STATE_FOCUSED); } #endif //__WXMSW__ SetItemCount(count); } void wxListCtrlEx::ResetSearchPrefix() { m_prefixSearch_prefix = _T(""); } void wxListCtrlEx::ShowColumn(unsigned int col, bool show) { if (col >= m_columnInfo.size()) return; if (m_columnInfo[col].shown == show) return; m_columnInfo[col].shown = show; if (show) { // Insert new column int pos = 0; for (unsigned int i = 0; i < m_columnInfo.size(); i++) { if (i == col) continue; t_columnInfo& info = m_columnInfo[i]; if (info.shown && info.order < m_columnInfo[col].order) pos++; } for (int i = GetColumnCount() - 1; i >= pos; i--) m_pVisibleColumnMapping[i + 1] = m_pVisibleColumnMapping[i]; m_pVisibleColumnMapping[pos] = col; t_columnInfo& info = m_columnInfo[col]; InsertColumn(pos, info.name, info.align, info.width); } else { int i; for (i = 0; i < GetColumnCount(); i++) { if (m_pVisibleColumnMapping[i] == col) break; } wxASSERT(m_columnInfo[col].order >= (unsigned int)i); for (int j = i + 1; j < GetColumnCount(); j++) m_pVisibleColumnMapping[j - 1] = m_pVisibleColumnMapping[j]; wxASSERT(i < GetColumnCount()); m_columnInfo[col].width = GetColumnWidth(i); DeleteColumn(i); } } void wxListCtrlEx::LoadColumnSettings(int widthsOptionId, int visibilityOptionId, int sortOptionId) { wxASSERT(!GetColumnCount()); if (widthsOptionId != -1) ReadColumnWidths(widthsOptionId); delete [] m_pVisibleColumnMapping; m_pVisibleColumnMapping = new unsigned int[m_columnInfo.size()]; if (visibilityOptionId != -1) { wxString visibleColumns = COptions::Get()->GetOption(visibilityOptionId); if (visibleColumns.Len() >= m_columnInfo.size()) { for (unsigned int i = 0; i < m_columnInfo.size(); ++i) { if (!m_columnInfo[i].fixed) m_columnInfo[i].shown = visibleColumns[i] == '1'; } } } if (sortOptionId != -1) { wxString strorder = COptions::Get()->GetOption(sortOptionId); wxStringTokenizer tokens(strorder, _T(",")); unsigned int count = tokens.CountTokens(); if (count == m_columnInfo.size()) { unsigned long *order = new unsigned long[count]; bool *order_set = new bool[count]; memset(order_set, 0, sizeof(bool) * count); unsigned int i = 0; while (tokens.HasMoreTokens()) { if (!tokens.GetNextToken().ToULong(&order[i])) break; if (order[i] >= count || order_set[order[i]]) break; order_set[order[i]] = true; i++; } if (i == count) { bool valid = true; for (size_t j = 0; j < m_columnInfo.size(); ++j) { if (!m_columnInfo[j].fixed) continue; if (j != order[j]) { valid = false; break; } } if (valid) { for (size_t j = 0; j < m_columnInfo.size(); ++j) { m_columnInfo[j].order = order[j]; } } } delete [] order; delete [] order_set; } } CreateVisibleColumnMapping(); } void wxListCtrlEx::SaveColumnSettings(int widthsOptionId, int visibilityOptionId, int sortOptionId) { if (widthsOptionId != -1) SaveColumnWidths(widthsOptionId); if (visibilityOptionId != -1) { wxString visibleColumns; for (unsigned int i = 0; i < m_columnInfo.size(); i++) { if (m_columnInfo[i].shown) visibleColumns += _T("1"); else visibleColumns += _T("0"); } COptions::Get()->SetOption(visibilityOptionId, visibleColumns); } if (sortOptionId != -1) { wxString order; for (unsigned int i = 0; i < m_columnInfo.size(); i++) { if (i) order += _T(","); order += wxString::Format(_T("%d"), m_columnInfo[i].order); } COptions::Get()->SetOption(sortOptionId, order); } } bool wxListCtrlEx::ReadColumnWidths(unsigned int optionId) { wxASSERT(!GetColumnCount()); if (wxGetKeyState(WXK_SHIFT) && wxGetKeyState(WXK_ALT) && wxGetKeyState(WXK_CONTROL)) { return true; } size_t const columnCount = m_columnInfo.size(); wxString savedWidths = COptions::Get()->GetOption(optionId); wxStringTokenizer tokens(savedWidths, _T(" ")); size_t const tokenCount = tokens.CountTokens(); for (size_t i = 0; i < columnCount; ++i) { unsigned long newWidth{}; if (i < tokenCount) { wxString token = tokens.GetNextToken(); if (token.ToULong(&newWidth) && newWidth < 10000) { m_columnInfo[i].width = newWidth; } } } return true; } void wxListCtrlEx::SaveColumnWidths(unsigned int optionId) { const unsigned int count = m_columnInfo.size(); wxString widths; for (unsigned int i = 0; i < count; ++i) { int width = 0; bool found = false; for (int j = 0; j < GetColumnCount(); ++j) { if (m_pVisibleColumnMapping[j] != i) continue; found = true; width = GetColumnWidth(j); } if (!found) width = m_columnInfo[i].width; widths += wxString::Format(_T("%d "), width); } widths.RemoveLast(); COptions::Get()->SetOption(optionId, widths); } void wxListCtrlEx::AddColumn(const wxString& name, int align, int initialWidth, bool fixed) { wxASSERT(!GetColumnCount()); t_columnInfo info; info.name = name; info.align = align; info.width = initialWidth; info.shown = true; info.order = m_columnInfo.size(); info.fixed = fixed; m_columnInfo.push_back(info); } // Moves column. Target position includes both hidden // as well as shown columns void wxListCtrlEx::MoveColumn(unsigned int col, unsigned int before) { if (m_columnInfo[col].order == before) return; for (unsigned int i = 0; i < m_columnInfo.size(); ++i) { if (i == col) continue; t_columnInfo& info = m_columnInfo[i]; if (info.order > col) --info.order; if (info.order >= before) ++info.order; } t_columnInfo& info = m_columnInfo[col]; if (info.shown) { int icon = -1; // Remove old column for (unsigned int i = 0; i < (unsigned int)GetColumnCount(); ++i) { if (m_pVisibleColumnMapping[i] != col) continue; for (unsigned int j = i + 1; j < (unsigned int)GetColumnCount(); ++j) m_pVisibleColumnMapping[j - 1] = m_pVisibleColumnMapping[j]; info.width = GetColumnWidth(i); icon = GetHeaderSortIconIndex(i); DeleteColumn(i); break; } // Insert new column unsigned int pos = 0; for (unsigned int i = 0; i < m_columnInfo.size(); ++i) { if (i == col) continue; t_columnInfo& info2 = m_columnInfo[i]; if (info2.shown && info2.order < before) { ++pos; } } for (unsigned int i = (int)GetColumnCount(); i > pos; --i) m_pVisibleColumnMapping[i] = m_pVisibleColumnMapping[i - 1]; m_pVisibleColumnMapping[pos] = col; InsertColumn(pos, info.name, info.align, info.width); SetHeaderSortIconIndex(pos, icon); } m_columnInfo[col].order = before; } void wxListCtrlEx::CreateVisibleColumnMapping() { int pos = 0; for (unsigned int j = 0; j < m_columnInfo.size(); j++) { for (unsigned int i = 0; i < m_columnInfo.size(); i++) { const t_columnInfo &column = m_columnInfo[i]; if (!column.shown) continue; if (column.order != j) continue; m_pVisibleColumnMapping[pos] = i; InsertColumn(pos++, column.name, column.align, column.width); } } } class CColumnEditDialog : public wxDialogEx { public: int *m_order; DECLARE_EVENT_TABLE() protected: void OnUp(wxCommandEvent&) { wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox); int sel = pListBox->GetSelection(); if (sel < 2) return; int tmp; tmp = m_order[sel - 1]; m_order[sel - 1] = m_order[sel]; m_order[sel] = tmp; wxString name = pListBox->GetString(sel); bool checked = pListBox->IsChecked(sel); pListBox->Delete(sel); pListBox->Insert(name, sel - 1); pListBox->Check(sel - 1, checked); pListBox->SetSelection(sel - 1); wxCommandEvent evt; OnSelChanged(evt); } void OnDown(wxCommandEvent&) { wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox); int sel = pListBox->GetSelection(); if (sel < 1) return; if (sel >= (int)pListBox->GetCount() - 1) return; int tmp; tmp = m_order[sel + 1]; m_order[sel + 1] = m_order[sel]; m_order[sel] = tmp; wxString name = pListBox->GetString(sel); bool checked = pListBox->IsChecked(sel); pListBox->Delete(sel); pListBox->Insert(name, sel + 1); pListBox->Check(sel + 1, checked); pListBox->SetSelection(sel + 1); wxCommandEvent evt; OnSelChanged(evt); } void OnSelChanged(wxCommandEvent&) { wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox); int sel = pListBox->GetSelection(); XRCCTRL(*this, "ID_UP", wxButton)->Enable(sel > 1); XRCCTRL(*this, "ID_DOWN", wxButton)->Enable(sel > 0 && sel < (int)pListBox->GetCount() - 1); } void OnCheck(wxCommandEvent& event) { if (!event.GetSelection() && !event.IsChecked()) { wxCheckListBox* pListBox = XRCCTRL(*this, "ID_ACTIVE", wxCheckListBox); pListBox->Check(0); wxMessageBoxEx(_("The filename column can neither be hidden nor moved."), _("Column properties")); } } }; BEGIN_EVENT_TABLE(CColumnEditDialog, wxDialogEx) EVT_BUTTON(XRCID("ID_UP"), CColumnEditDialog::OnUp) EVT_BUTTON(XRCID("ID_DOWN"), CColumnEditDialog::OnDown) EVT_LISTBOX(wxID_ANY, CColumnEditDialog::OnSelChanged) EVT_CHECKLISTBOX(wxID_ANY, CColumnEditDialog::OnCheck) END_EVENT_TABLE() void wxListCtrlEx::ShowColumnEditor() { CColumnEditDialog dlg; if (!dlg.Load(this, _T("ID_COLUMN_SETUP"))) { wxBell(); return; } wxCheckListBox* pListBox = XRCCTRL(dlg, "ID_ACTIVE", wxCheckListBox); dlg.m_order = new int[m_columnInfo.size()]; for (unsigned int j = 0; j < m_columnInfo.size(); j++) { for (unsigned int i = 0; i < m_columnInfo.size(); i++) { if (m_columnInfo[i].order != j) continue; dlg.m_order[j] = i; pListBox->Append(m_columnInfo[i].name); if (m_columnInfo[i].shown) pListBox->Check(j); } } wxASSERT(pListBox->GetCount() == m_columnInfo.size()); dlg.GetSizer()->Fit(&dlg); if (dlg.ShowModal() != wxID_OK) { delete [] dlg.m_order; return; } for (unsigned int i = 0; i < m_columnInfo.size(); i++) { int col = dlg.m_order[i]; bool isChecked = pListBox->IsChecked(i); if (!isChecked && !col) { isChecked = true; wxMessageBoxEx(_("The filename column cannot be hidden.")); } MoveColumn(col, i); if (m_columnInfo[col].shown != isChecked) ShowColumn(col, isChecked); } delete [] dlg.m_order; // Generic wxListCtrl needs manual refresh Refresh(); } int wxListCtrlEx::GetColumnVisibleIndex(int col) { if (!m_pVisibleColumnMapping) return -1; for (int i = 0; i < GetColumnCount(); i++) { if (m_pVisibleColumnMapping[i] == (unsigned int)col) return i; } return -1; } int wxListCtrlEx::GetHeaderSortIconIndex(int col) { if (col < 0 || col >= GetColumnCount()) return -1; #ifdef __WXMSW__ HWND hWnd = (HWND)GetHandle(); HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0); HDITEM item; item.mask = HDI_IMAGE | HDI_FORMAT; SendMessage(header, HDM_GETITEM, col, (LPARAM)&item); if (!(item.fmt & HDF_IMAGE)) return -1; return item.iImage; #else wxListItem item; if (!GetColumn(col, item)) return -1; return item.GetImage(); #endif } void wxListCtrlEx::InitHeaderSortImageList() { #ifdef __WXMSW__ if (wxPlatformInfo::Get().GetOSMajorVersion() >= 6) return; // Initialize imagelist for list header m_pHeaderImageList = new wxImageListEx(8, 8, true, 3); wxBitmap bmp; bmp.LoadFile(wxGetApp().GetResourceDir().GetPath() + _T("up.png"), wxBITMAP_TYPE_PNG); m_pHeaderImageList->Add(bmp); bmp.LoadFile(wxGetApp().GetResourceDir().GetPath() + _T("down.png"), wxBITMAP_TYPE_PNG); m_pHeaderImageList->Add(bmp); HWND hWnd = (HWND)GetHandle(); if (!hWnd) { delete m_pHeaderImageList; m_pHeaderImageList = 0; return; } HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0); if (!header) { delete m_pHeaderImageList; m_pHeaderImageList = 0; return; } TCHAR buffer[1000] = {0}; HDITEM item; item.mask = HDI_TEXT; item.pszText = buffer; item.cchTextMax = 999; SendMessage(header, HDM_GETITEM, 0, (LPARAM)&item); SendMessage(header, HDM_SETIMAGELIST, 0, (LPARAM)m_pHeaderImageList->GetHandle()); m_header_icon_index.up = 0; m_header_icon_index.down = 1; #else wxColour colour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE); wxString lightness; if (colour.Red() + colour.Green() + colour.Blue() > 3 * 128) lightness = _T("DARK"); else lightness = _T("LIGHT"); wxBitmap bmp; bmp = wxArtProvider::GetBitmap(_T("ART_SORT_UP_") + lightness, wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)); m_header_icon_index.up = GetSystemImageList()->Add(bmp); bmp = wxArtProvider::GetBitmap(_T("ART_SORT_DOWN_") + lightness, wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)); m_header_icon_index.down = GetSystemImageList()->Add(bmp); #endif } void wxListCtrlEx::SetHeaderSortIconIndex(int col, int icon) { if (col < 0 || col >= GetColumnCount()) return; #ifdef __WXMSW__ HWND hWnd = (HWND)GetHandle(); HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0); wxChar buffer[100]; HDITEM item = {0}; item.mask = HDI_TEXT | HDI_FORMAT; item.pszText = buffer; item.cchTextMax = 99; SendMessage(header, HDM_GETITEM, col, (LPARAM)&item); if (icon != -1) { if (wxPlatformInfo::Get().GetOSMajorVersion() >= 6) { item.fmt &= ~(HDF_IMAGE | HDF_BITMAP_ON_RIGHT | HDF_SORTUP | HDF_SORTDOWN); item.iImage = -1; if (icon) item.fmt |= HDF_SORTDOWN; else item.fmt |= HDF_SORTUP; } else { item.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); item.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT; item.iImage = icon ? m_header_icon_index.down : m_header_icon_index.up; item.mask |= HDI_IMAGE; } } else { item.fmt &= ~(HDF_IMAGE | HDF_BITMAP_ON_RIGHT | HDF_SORTUP | HDF_SORTDOWN); item.iImage = -1; } SendMessage(header, HDM_SETITEM, col, (LPARAM)&item); #else wxListItem item; if (!GetColumn(col, item)) return; if( icon != -1 ) icon = icon ? m_header_icon_index.down : m_header_icon_index.up; item.SetImage(icon); SetColumn(col, item); #endif } void wxListCtrlEx::RefreshListOnly(bool eraseBackground /*=true*/) { // See comment in wxGenericListCtrl::Refresh GetMainWindow()->Refresh(eraseBackground); } void wxListCtrlEx::CancelLabelEdit() { #ifdef __WXMSW__ if (GetEditControl()) ListView_CancelEditLabel((HWND)GetHandle()); #else m_editing = false; wxTextCtrl* pEdit = GetEditControl(); if (pEdit) { wxKeyEvent evt(wxEVT_CHAR); evt.m_keyCode = WXK_ESCAPE; pEdit->GetEventHandler()->ProcessEvent(evt); } #endif } void wxListCtrlEx::OnBeginLabelEdit(wxListEvent& event) { #ifndef __WXMSW__ if (m_editing) { event.Veto(); return; } #endif if (m_blockedLabelEditing) { event.Veto(); return; } if (!OnBeginRename(event)) event.Veto(); #ifndef __WXMSW__ else m_editing = true; #endif } void wxListCtrlEx::OnEndLabelEdit(wxListEvent& event) { #ifdef __WXMAC__ int item = event.GetIndex(); if (item != -1) { int to = item + 1; if (to < GetItemCount()) { int from = item; if (from) from--; RefreshItems(from, to); } else RefreshListOnly(); } #endif #ifndef __WXMSW__ if (!m_editing) { event.Veto(); return; } m_editing = false; #endif if (event.IsEditCancelled()) return; if (!OnAcceptRename(event)) event.Veto(); } bool wxListCtrlEx::OnBeginRename(const wxListEvent&) { return false; } bool wxListCtrlEx::OnAcceptRename(const wxListEvent&) { return false; } void wxListCtrlEx::SetLabelEditBlock(bool block) { if (block) { CancelLabelEdit(); ++m_blockedLabelEditing; } else { wxASSERT(m_blockedLabelEditing); if (m_blockedLabelEditing > 0) m_blockedLabelEditing--; } } CLabelEditBlocker::CLabelEditBlocker(wxListCtrlEx& listCtrl) : m_listCtrl(listCtrl) { m_listCtrl.SetLabelEditBlock(true); } CLabelEditBlocker::~CLabelEditBlocker() { m_listCtrl.SetLabelEditBlock(false); } void wxListCtrlEx::OnColumnDragging(wxListEvent& event) { if (event.GetItem().GetWidth() < MIN_COLUMN_WIDTH) event.Veto(); } #ifdef __WXMSW__ bool wxListCtrlEx::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { // MSW doesn't generate HDN_TRACK on all header styles, so handle it // ourselves using HDN_ITEMCHANGING. NMHDR *nmhdr = (NMHDR *)lParam; HWND hwndHdr = ListView_GetHeader((HWND)GetHandle()); if (nmhdr->hwndFrom != hwndHdr) return wxListCtrl::MSWOnNotify(idCtrl, lParam, result); HD_NOTIFY *nmHDR = (HD_NOTIFY *)nmhdr; switch ( nmhdr->code ) { // See comment in src/msw/listctrl.cpp of wx why both A and W are needed case HDN_BEGINTRACKA: case HDN_BEGINTRACKW: m_columnDragging = true; break; case HDN_ENDTRACKA: case HDN_ENDTRACKW: m_columnDragging = true; break; case HDN_ITEMCHANGINGA: case HDN_ITEMCHANGINGW: if (m_columnDragging) { if (nmHDR->pitem->mask & HDI_WIDTH && nmHDR->pitem->cxy < MIN_COLUMN_WIDTH) { *result = 1; return true; } else { *result = 0; return false; } } else return false; case HDN_DIVIDERDBLCLICK: { wxListEvent event(wxEVT_LIST_COL_END_DRAG, GetId()); event.SetEventObject(this); AddPendingEvent(event); } break; } return wxListCtrl::MSWOnNotify(idCtrl, lParam, result); } #endif bool wxListCtrlEx::HasSelection() const { #ifndef __WXMSW__ // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1) return GetSelectedItemCount() != 0; #else return GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1; #endif } wxRect wxListCtrlEx::GetActualClientRect() const { wxRect windowRect = GetMainWindow()->GetClientRect(); #ifdef __WXMSW__ wxRect topRect; if (GetItemRect(0, topRect)) { windowRect.height -= topRect.y; windowRect.y += topRect.y; } #endif return windowRect; }