implement column reordering support in wxMSW wxHeaderCtrl; use it in wxDataViewCtrl...
[wxWidgets.git] / src / common / headerctrlcmn.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/headerctrlcmn.cpp
3 // Purpose: implementation of wxHeaderCtrlBase
4 // Author: Vadim Zeitlin
5 // Created: 2008-12-02
6 // RCS-ID: $Id$
7 // Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #endif // WX_PRECOMP
28
29 #include "wx/headerctrl.h"
30
31 // ----------------------------------------------------------------------------
32 // constants
33 // ----------------------------------------------------------------------------
34
35 namespace
36 {
37
38 const unsigned int wxNO_COLUMN = static_cast<unsigned>(-1);
39
40 } // anonymous namespace
41
42 // ============================================================================
43 // wxHeaderCtrlBase implementation
44 // ============================================================================
45
46 extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr[] = "wxHeaderCtrl";
47
48 BEGIN_EVENT_TABLE(wxHeaderCtrlBase, wxControl)
49 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY, wxHeaderCtrlBase::OnSeparatorDClick)
50 END_EVENT_TABLE()
51
52 void wxHeaderCtrlBase::ScrollWindow(int dx,
53 int WXUNUSED_UNLESS_DEBUG(dy),
54 const wxRect * WXUNUSED_UNLESS_DEBUG(rect))
55
56 {
57 // this doesn't make sense at all
58 wxASSERT_MSG( !dy, "header window can't be scrolled vertically" );
59
60 // this would actually be nice to support for "frozen" headers but it isn't
61 // supported currently
62 wxASSERT_MSG( !rect, "header window can't be scrolled partially" );
63
64 DoScrollHorz(dx);
65 }
66
67 // ----------------------------------------------------------------------------
68 // wxHeaderCtrlBase event handling
69 // ----------------------------------------------------------------------------
70
71 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
72 {
73 const unsigned col = event.GetColumn();
74
75 int w = wxWindowBase::GetTextExtent(GetColumn(col).GetTitle()).x;
76 w += 2*GetCharWidth(); // add some arbitrary margins around text
77
78 if ( !UpdateColumnWidthToFit(col, w) )
79 event.Skip();
80 else
81 UpdateColumn(col);
82 }
83
84 // ----------------------------------------------------------------------------
85 // wxHeaderCtrlBase column reordering
86 // ----------------------------------------------------------------------------
87
88 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt& order)
89 {
90 const unsigned count = GetColumnCount();
91 wxCHECK_RET( order.size() == count, "wrong number of columns" );
92
93 // check the array validity
94 wxArrayInt seen(count, 0);
95 for ( unsigned n = 0; n < count; n++ )
96 {
97 const unsigned idx = order[n];
98 wxCHECK_RET( idx < count, "invalid column index" );
99 wxCHECK_RET( !seen[idx], "duplicate column index" );
100
101 seen[idx] = 1;
102 }
103
104 DoSetColumnsOrder(order);
105
106 // TODO-RTL: do we need to reverse the array?
107 }
108
109 wxArrayInt wxHeaderCtrlBase::GetColumnsOrder() const
110 {
111 const wxArrayInt order = DoGetColumnsOrder();
112
113 wxASSERT_MSG( order.size() == GetColumnCount(), "invalid order array" );
114
115 return order;
116 }
117
118 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos) const
119 {
120 wxCHECK_MSG( pos < GetColumnCount(), wxNO_COLUMN, "invalid position" );
121
122 return GetColumnsOrder()[pos];
123 }
124
125 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx) const
126 {
127 const unsigned count = GetColumnCount();
128
129 wxCHECK_MSG( idx < count, wxNO_COLUMN, "invalid index" );
130
131 const wxArrayInt order = GetColumnsOrder();
132 for ( unsigned n = 0; n < count; n++ )
133 {
134 if ( (unsigned)order[n] == idx )
135 return n;
136 }
137
138 wxFAIL_MSG( "column unexpectedly not displayed at all" );
139
140 return wxNO_COLUMN;
141 }
142
143 // ============================================================================
144 // wxHeaderCtrlSimple implementation
145 // ============================================================================
146
147 void wxHeaderCtrlSimple::Init()
148 {
149 m_sortKey = wxNO_COLUMN;
150 }
151
152 wxHeaderColumnBase& wxHeaderCtrlSimple::GetColumn(unsigned int idx)
153 {
154 return m_cols[idx];
155 }
156
157 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple& col, unsigned int idx)
158 {
159 m_cols.insert(m_cols.begin() + idx, col);
160
161 UpdateColumnCount();
162 }
163
164 void wxHeaderCtrlSimple::DoDelete(unsigned int idx)
165 {
166 m_cols.erase(m_cols.begin() + idx);
167 if ( idx == m_sortKey )
168 m_sortKey = wxNO_COLUMN;
169
170 UpdateColumnCount();
171 }
172
173 void wxHeaderCtrlSimple::DeleteAllColumns()
174 {
175 m_cols.clear();
176 m_sortKey = wxNO_COLUMN;
177
178 UpdateColumnCount();
179 }
180
181
182 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx, bool show)
183 {
184 if ( show != m_cols[idx].IsShown() )
185 {
186 m_cols[idx].SetHidden(!show);
187
188 UpdateColumn(idx);
189 }
190 }
191
192 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx, bool ascending)
193 {
194 RemoveSortIndicator();
195
196 m_cols[idx].SetAsSortKey(ascending);
197 m_sortKey = idx;
198
199 UpdateColumn(idx);
200 }
201
202 void wxHeaderCtrlSimple::RemoveSortIndicator()
203 {
204 if ( m_sortKey != wxNO_COLUMN )
205 {
206 const unsigned sortOld = m_sortKey;
207 m_sortKey = wxNO_COLUMN;
208
209 m_cols[sortOld].UnsetAsSortKey();
210
211 UpdateColumn(sortOld);
212 }
213 }
214
215 bool
216 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
217 {
218 const int widthContents = GetBestFittingWidth(idx);
219 if ( widthContents == -1 )
220 return false;
221
222 m_cols[idx].SetWidth(wxMax(widthContents, widthTitle));
223 UpdateColumn(idx);
224
225 return true;
226 }
227
228 // ============================================================================
229 // wxHeaderCtrlEvent implementation
230 // ============================================================================
231
232 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent, wxNotifyEvent)
233
234 const wxEventType wxEVT_COMMAND_HEADER_CLICK = wxNewEventType();
235 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK = wxNewEventType();
236 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK = wxNewEventType();
237
238 const wxEventType wxEVT_COMMAND_HEADER_DCLICK = wxNewEventType();
239 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK = wxNewEventType();
240 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK = wxNewEventType();
241
242 const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK = wxNewEventType();
243
244 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE = wxNewEventType();
245 const wxEventType wxEVT_COMMAND_HEADER_RESIZING = wxNewEventType();
246 const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE = wxNewEventType();
247
248 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER = wxNewEventType();
249 const wxEventType wxEVT_COMMAND_HEADER_END_REORDER = wxNewEventType();