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