PCH compilation fix
[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 #include "wx/menu.h"
28 #endif // WX_PRECOMP
29
30 #include "wx/headerctrl.h"
31
32 // ----------------------------------------------------------------------------
33 // constants
34 // ----------------------------------------------------------------------------
35
36 namespace
37 {
38
39 const unsigned int wxNO_COLUMN = static_cast<unsigned>(-1);
40
41 } // anonymous namespace
42
43 // ============================================================================
44 // wxHeaderCtrlBase implementation
45 // ============================================================================
46
47 extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr[] = "wxHeaderCtrl";
48
49 BEGIN_EVENT_TABLE(wxHeaderCtrlBase, wxControl)
50 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY, wxHeaderCtrlBase::OnSeparatorDClick)
51 END_EVENT_TABLE()
52
53 void wxHeaderCtrlBase::ScrollWindow(int dx,
54 int WXUNUSED_UNLESS_DEBUG(dy),
55 const wxRect * WXUNUSED_UNLESS_DEBUG(rect))
56
57 {
58 // this doesn't make sense at all
59 wxASSERT_MSG( !dy, "header window can't be scrolled vertically" );
60
61 // this would actually be nice to support for "frozen" headers but it isn't
62 // supported currently
63 wxASSERT_MSG( !rect, "header window can't be scrolled partially" );
64
65 DoScrollHorz(dx);
66 }
67
68 void wxHeaderCtrlBase::SetColumnCount(unsigned int count)
69 {
70 if ( count == GetColumnCount() )
71 return;
72
73 OnColumnCountChanging(count);
74
75 DoSetCount(count);
76 }
77
78 // ----------------------------------------------------------------------------
79 // wxHeaderCtrlBase event handling
80 // ----------------------------------------------------------------------------
81
82 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
83 {
84 const unsigned col = event.GetColumn();
85
86 int w = wxWindowBase::GetTextExtent(GetColumn(col).GetTitle()).x;
87 w += 4*GetCharWidth(); // add some arbitrary margins around text
88
89 if ( !UpdateColumnWidthToFit(col, w) )
90 event.Skip();
91 else
92 UpdateColumn(col);
93 }
94
95 // ----------------------------------------------------------------------------
96 // wxHeaderCtrlBase column reordering
97 // ----------------------------------------------------------------------------
98
99 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt& order)
100 {
101 const unsigned count = GetColumnCount();
102 wxCHECK_RET( order.size() == count, "wrong number of columns" );
103
104 // check the array validity
105 wxArrayInt seen(count, 0);
106 for ( unsigned n = 0; n < count; n++ )
107 {
108 const unsigned idx = order[n];
109 wxCHECK_RET( idx < count, "invalid column index" );
110 wxCHECK_RET( !seen[idx], "duplicate column index" );
111
112 seen[idx] = 1;
113 }
114
115 DoSetColumnsOrder(order);
116
117 // TODO-RTL: do we need to reverse the array?
118 }
119
120 void wxHeaderCtrlBase::ResetColumnsOrder()
121 {
122 const unsigned count = GetColumnCount();
123 wxArrayInt order(count);
124 for ( unsigned n = 0; n < count; n++ )
125 order[n] = n;
126
127 DoSetColumnsOrder(order);
128 }
129
130 wxArrayInt wxHeaderCtrlBase::GetColumnsOrder() const
131 {
132 const wxArrayInt order = DoGetColumnsOrder();
133
134 wxASSERT_MSG( order.size() == GetColumnCount(), "invalid order array" );
135
136 return order;
137 }
138
139 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos) const
140 {
141 wxCHECK_MSG( pos < GetColumnCount(), wxNO_COLUMN, "invalid position" );
142
143 return GetColumnsOrder()[pos];
144 }
145
146 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx) const
147 {
148 const unsigned count = GetColumnCount();
149
150 wxCHECK_MSG( idx < count, wxNO_COLUMN, "invalid index" );
151
152 const wxArrayInt order = GetColumnsOrder();
153 for ( unsigned n = 0; n < count; n++ )
154 {
155 if ( (unsigned)order[n] == idx )
156 return n;
157 }
158
159 wxFAIL_MSG( "column unexpectedly not displayed at all" );
160
161 return wxNO_COLUMN;
162 }
163
164 /* static */
165 void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt& order,
166 unsigned int idx,
167 unsigned int pos)
168 {
169 const unsigned count = order.size();
170
171 wxArrayInt orderNew;
172 orderNew.reserve(count);
173 for ( unsigned n = 0; ; n++ )
174 {
175 // NB: order of checks is important for this to work when the new
176 // column position is the same as the old one
177
178 // insert the column at its new position
179 if ( orderNew.size() == pos )
180 orderNew.push_back(idx);
181
182 if ( n == count )
183 break;
184
185 // delete the column from its old position
186 const unsigned idxOld = order[n];
187 if ( idxOld == idx )
188 continue;
189
190 orderNew.push_back(idxOld);
191 }
192
193 order.swap(orderNew);
194 }
195
196 void
197 wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt& colIndices, unsigned int count)
198 {
199 // update the column indices array if necessary
200 const unsigned countOld = colIndices.size();
201 if ( count > countOld )
202 {
203 // all new columns have default positions equal to their indices
204 for ( unsigned n = countOld; n < count; n++ )
205 colIndices.push_back(n);
206 }
207 else if ( count < countOld )
208 {
209 // filter out all the positions which are invalid now while keeping the
210 // order of the remaining ones
211 wxArrayInt colIndicesNew;
212 colIndicesNew.reserve(count);
213 for ( unsigned n = 0; n < countOld; n++ )
214 {
215 const unsigned idx = colIndices[n];
216 if ( idx < count )
217 colIndicesNew.push_back(idx);
218 }
219
220 colIndices.swap(colIndicesNew);
221 }
222 else // count didn't really change, we shouldn't even be called
223 {
224 wxFAIL_MSG( "useless call to DoResizeColumnIndices()" );
225 }
226
227 wxASSERT_MSG( colIndices.size() == count, "logic error" );
228 }
229
230 // ----------------------------------------------------------------------------
231 // wxHeaderCtrl extra UI
232 // ----------------------------------------------------------------------------
233
234 int wxHeaderCtrlBase::ShowColumnsMenu(const wxString& title)
235 {
236 wxMenu menu;
237 if ( !title.empty() )
238 menu.SetTitle(title);
239
240 const unsigned count = GetColumnCount();
241 for ( unsigned n = 0; n < count; n++ )
242 {
243 const wxHeaderColumn& col = GetColumn(n);
244 menu.AppendCheckItem(n, col.GetTitle());
245 if ( col.IsShown() )
246 menu.Check(n, true);
247 }
248
249 return GetPopupMenuSelectionFromUser(menu,
250 ScreenToClient(wxGetMousePosition()));
251 }
252
253 // ============================================================================
254 // wxHeaderCtrlSimple implementation
255 // ============================================================================
256
257 void wxHeaderCtrlSimple::Init()
258 {
259 m_sortKey = wxNO_COLUMN;
260 }
261
262 wxHeaderColumn& wxHeaderCtrlSimple::GetColumn(unsigned int idx)
263 {
264 return m_cols[idx];
265 }
266
267 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple& col, unsigned int idx)
268 {
269 m_cols.insert(m_cols.begin() + idx, col);
270
271 UpdateColumnCount();
272 }
273
274 void wxHeaderCtrlSimple::DoDelete(unsigned int idx)
275 {
276 m_cols.erase(m_cols.begin() + idx);
277 if ( idx == m_sortKey )
278 m_sortKey = wxNO_COLUMN;
279
280 UpdateColumnCount();
281 }
282
283 void wxHeaderCtrlSimple::DeleteAllColumns()
284 {
285 m_cols.clear();
286 m_sortKey = wxNO_COLUMN;
287
288 UpdateColumnCount();
289 }
290
291
292 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx, bool show)
293 {
294 if ( show != m_cols[idx].IsShown() )
295 {
296 m_cols[idx].SetHidden(!show);
297
298 UpdateColumn(idx);
299 }
300 }
301
302 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx, bool ascending)
303 {
304 RemoveSortIndicator();
305
306 m_cols[idx].SetAsSortKey(ascending);
307 m_sortKey = idx;
308
309 UpdateColumn(idx);
310 }
311
312 void wxHeaderCtrlSimple::RemoveSortIndicator()
313 {
314 if ( m_sortKey != wxNO_COLUMN )
315 {
316 const unsigned sortOld = m_sortKey;
317 m_sortKey = wxNO_COLUMN;
318
319 m_cols[sortOld].UnsetAsSortKey();
320
321 UpdateColumn(sortOld);
322 }
323 }
324
325 bool
326 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
327 {
328 const int widthContents = GetBestFittingWidth(idx);
329 if ( widthContents == -1 )
330 return false;
331
332 m_cols[idx].SetWidth(wxMax(widthContents, widthTitle));
333
334 return true;
335 }
336
337 // ============================================================================
338 // wxHeaderCtrlEvent implementation
339 // ============================================================================
340
341 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent, wxNotifyEvent)
342
343 const wxEventType wxEVT_COMMAND_HEADER_CLICK = wxNewEventType();
344 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK = wxNewEventType();
345 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK = wxNewEventType();
346
347 const wxEventType wxEVT_COMMAND_HEADER_DCLICK = wxNewEventType();
348 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK = wxNewEventType();
349 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK = wxNewEventType();
350
351 const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK = wxNewEventType();
352
353 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE = wxNewEventType();
354 const wxEventType wxEVT_COMMAND_HEADER_RESIZING = wxNewEventType();
355 const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE = wxNewEventType();
356
357 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER = wxNewEventType();
358 const wxEventType wxEVT_COMMAND_HEADER_END_REORDER = wxNewEventType();
359
360 const wxEventType wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED = wxNewEventType();