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