added wxHeaderCtrl::ResetColumnsOrder() function; use it from wxGrid
[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 void wxHeaderCtrlBase::SetColumnCount(unsigned int count)
68 {
69 OnColumnCountChanging(count);
70
71 DoSetCount(count);
72 }
73
74 // ----------------------------------------------------------------------------
75 // wxHeaderCtrlBase event handling
76 // ----------------------------------------------------------------------------
77
78 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
79 {
80 const unsigned col = event.GetColumn();
81
82 int w = wxWindowBase::GetTextExtent(GetColumn(col).GetTitle()).x;
83 w += 4*GetCharWidth(); // add some arbitrary margins around text
84
85 if ( !UpdateColumnWidthToFit(col, w) )
86 event.Skip();
87 else
88 UpdateColumn(col);
89 }
90
91 // ----------------------------------------------------------------------------
92 // wxHeaderCtrlBase column reordering
93 // ----------------------------------------------------------------------------
94
95 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt& order)
96 {
97 const unsigned count = GetColumnCount();
98 wxCHECK_RET( order.size() == count, "wrong number of columns" );
99
100 // check the array validity
101 wxArrayInt seen(count, 0);
102 for ( unsigned n = 0; n < count; n++ )
103 {
104 const unsigned idx = order[n];
105 wxCHECK_RET( idx < count, "invalid column index" );
106 wxCHECK_RET( !seen[idx], "duplicate column index" );
107
108 seen[idx] = 1;
109 }
110
111 DoSetColumnsOrder(order);
112
113 // TODO-RTL: do we need to reverse the array?
114 }
115
116 void wxHeaderCtrlBase::ResetColumnsOrder()
117 {
118 const unsigned count = GetColumnCount();
119 wxArrayInt order(count);
120 for ( unsigned n = 0; n < count; n++ )
121 order[n] = n;
122
123 DoSetColumnsOrder(order);
124 }
125
126 wxArrayInt wxHeaderCtrlBase::GetColumnsOrder() const
127 {
128 const wxArrayInt order = DoGetColumnsOrder();
129
130 wxASSERT_MSG( order.size() == GetColumnCount(), "invalid order array" );
131
132 return order;
133 }
134
135 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos) const
136 {
137 wxCHECK_MSG( pos < GetColumnCount(), wxNO_COLUMN, "invalid position" );
138
139 return GetColumnsOrder()[pos];
140 }
141
142 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx) const
143 {
144 const unsigned count = GetColumnCount();
145
146 wxCHECK_MSG( idx < count, wxNO_COLUMN, "invalid index" );
147
148 const wxArrayInt order = GetColumnsOrder();
149 for ( unsigned n = 0; n < count; n++ )
150 {
151 if ( (unsigned)order[n] == idx )
152 return n;
153 }
154
155 wxFAIL_MSG( "column unexpectedly not displayed at all" );
156
157 return wxNO_COLUMN;
158 }
159
160 /* static */
161 void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt& order,
162 unsigned int idx,
163 unsigned int pos)
164 {
165 const unsigned count = order.size();
166
167 wxArrayInt orderNew;
168 orderNew.reserve(count);
169 for ( unsigned n = 0; ; n++ )
170 {
171 // NB: order of checks is important for this to work when the new
172 // column position is the same as the old one
173
174 // insert the column at its new position
175 if ( orderNew.size() == pos )
176 orderNew.push_back(idx);
177
178 if ( n == count )
179 break;
180
181 // delete the column from its old position
182 const unsigned idxOld = order[n];
183 if ( idxOld == idx )
184 continue;
185
186 orderNew.push_back(idxOld);
187 }
188
189 order.swap(orderNew);
190 }
191
192 // ============================================================================
193 // wxHeaderCtrlSimple implementation
194 // ============================================================================
195
196 void wxHeaderCtrlSimple::Init()
197 {
198 m_sortKey = wxNO_COLUMN;
199 }
200
201 wxHeaderColumn& wxHeaderCtrlSimple::GetColumn(unsigned int idx)
202 {
203 return m_cols[idx];
204 }
205
206 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple& col, unsigned int idx)
207 {
208 m_cols.insert(m_cols.begin() + idx, col);
209
210 UpdateColumnCount();
211 }
212
213 void wxHeaderCtrlSimple::DoDelete(unsigned int idx)
214 {
215 m_cols.erase(m_cols.begin() + idx);
216 if ( idx == m_sortKey )
217 m_sortKey = wxNO_COLUMN;
218
219 UpdateColumnCount();
220 }
221
222 void wxHeaderCtrlSimple::DeleteAllColumns()
223 {
224 m_cols.clear();
225 m_sortKey = wxNO_COLUMN;
226
227 UpdateColumnCount();
228 }
229
230
231 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx, bool show)
232 {
233 if ( show != m_cols[idx].IsShown() )
234 {
235 m_cols[idx].SetHidden(!show);
236
237 UpdateColumn(idx);
238 }
239 }
240
241 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx, bool ascending)
242 {
243 RemoveSortIndicator();
244
245 m_cols[idx].SetAsSortKey(ascending);
246 m_sortKey = idx;
247
248 UpdateColumn(idx);
249 }
250
251 void wxHeaderCtrlSimple::RemoveSortIndicator()
252 {
253 if ( m_sortKey != wxNO_COLUMN )
254 {
255 const unsigned sortOld = m_sortKey;
256 m_sortKey = wxNO_COLUMN;
257
258 m_cols[sortOld].UnsetAsSortKey();
259
260 UpdateColumn(sortOld);
261 }
262 }
263
264 bool
265 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
266 {
267 const int widthContents = GetBestFittingWidth(idx);
268 if ( widthContents == -1 )
269 return false;
270
271 m_cols[idx].SetWidth(wxMax(widthContents, widthTitle));
272
273 return true;
274 }
275
276 // ============================================================================
277 // wxHeaderCtrlEvent implementation
278 // ============================================================================
279
280 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent, wxNotifyEvent)
281
282 const wxEventType wxEVT_COMMAND_HEADER_CLICK = wxNewEventType();
283 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK = wxNewEventType();
284 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK = wxNewEventType();
285
286 const wxEventType wxEVT_COMMAND_HEADER_DCLICK = wxNewEventType();
287 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK = wxNewEventType();
288 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK = wxNewEventType();
289
290 const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK = wxNewEventType();
291
292 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE = wxNewEventType();
293 const wxEventType wxEVT_COMMAND_HEADER_RESIZING = wxNewEventType();
294 const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE = wxNewEventType();
295
296 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER = wxNewEventType();
297 const wxEventType wxEVT_COMMAND_HEADER_END_REORDER = wxNewEventType();
298
299 const wxEventType wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED = wxNewEventType();