]> git.saurik.com Git - wxWidgets.git/blame - src/common/headerctrlcmn.cpp
fix dereferencing end() iterator in ParseFormat() and constructing out of bound itera...
[wxWidgets.git] / src / common / headerctrlcmn.cpp
CommitLineData
56873923
VZ
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
e721a2a2
VZ
26#if wxUSE_HEADERCTRL
27
56873923 28#ifndef WX_PRECOMP
c4789bf6 29 #include "wx/menu.h"
56873923
VZ
30#endif // WX_PRECOMP
31
32#include "wx/headerctrl.h"
af67f39d
VZ
33#include "wx/rearrangectrl.h"
34
35namespace
36{
56873923 37
e2bfe673
VZ
38// ----------------------------------------------------------------------------
39// constants
40// ----------------------------------------------------------------------------
41
e2bfe673
VZ
42const unsigned int wxNO_COLUMN = static_cast<unsigned>(-1);
43
af67f39d
VZ
44// ----------------------------------------------------------------------------
45// wxHeaderColumnsRearrangeDialog: dialog for customizing our columns
46// ----------------------------------------------------------------------------
47
a632b93c
VZ
48#if wxUSE_REARRANGECTRL
49
af67f39d
VZ
50class wxHeaderColumnsRearrangeDialog : public wxRearrangeDialog
51{
52public:
53 wxHeaderColumnsRearrangeDialog(wxWindow *parent,
54 const wxArrayInt& order,
55 const wxArrayString& items)
56 : wxRearrangeDialog
57 (
58 parent,
59 _("Please select the columns to show and define their order:"),
60 _("Customize Columns"),
61 order,
62 items
63 )
64 {
65 }
66};
67
a632b93c
VZ
68#endif // wxUSE_REARRANGECTRL
69
e2bfe673
VZ
70} // anonymous namespace
71
56873923
VZ
72// ============================================================================
73// wxHeaderCtrlBase implementation
74// ============================================================================
75
9a382f1f 76extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr[] = "wxHeaderCtrl";
56873923 77
3bfaa5a7
VZ
78BEGIN_EVENT_TABLE(wxHeaderCtrlBase, wxControl)
79 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY, wxHeaderCtrlBase::OnSeparatorDClick)
8a2e3f80 80#if wxUSE_MENUS
613de0e8 81 EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxHeaderCtrlBase::OnRClick)
8a2e3f80 82#endif // wxUSE_MENUS
3bfaa5a7
VZ
83END_EVENT_TABLE()
84
d8fc3398
VZ
85void wxHeaderCtrlBase::ScrollWindow(int dx,
86 int WXUNUSED_UNLESS_DEBUG(dy),
87 const wxRect * WXUNUSED_UNLESS_DEBUG(rect))
88
89{
90 // this doesn't make sense at all
91 wxASSERT_MSG( !dy, "header window can't be scrolled vertically" );
92
93 // this would actually be nice to support for "frozen" headers but it isn't
94 // supported currently
95 wxASSERT_MSG( !rect, "header window can't be scrolled partially" );
96
97 DoScrollHorz(dx);
98}
99
4635abac
VZ
100void wxHeaderCtrlBase::SetColumnCount(unsigned int count)
101{
613de0e8
VZ
102 if ( count != GetColumnCount() )
103 OnColumnCountChanging(count);
4635abac 104
613de0e8
VZ
105 // still call DoSetCount() even if the count didn't really change in order
106 // to update all the columns
4635abac
VZ
107 DoSetCount(count);
108}
109
702f5349
VZ
110// ----------------------------------------------------------------------------
111// wxHeaderCtrlBase event handling
112// ----------------------------------------------------------------------------
113
3bfaa5a7
VZ
114void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
115{
116 const unsigned col = event.GetColumn();
117
118 int w = wxWindowBase::GetTextExtent(GetColumn(col).GetTitle()).x;
f458d4dd 119 w += 4*GetCharWidth(); // add some arbitrary margins around text
3bfaa5a7
VZ
120
121 if ( !UpdateColumnWidthToFit(col, w) )
122 event.Skip();
123 else
124 UpdateColumn(col);
125}
126
8a2e3f80 127#if wxUSE_MENUS
faef508f 128
613de0e8
VZ
129void wxHeaderCtrlBase::OnRClick(wxHeaderCtrlEvent& event)
130{
131 if ( !HasFlag(wxHD_ALLOW_HIDE) )
132 {
133 event.Skip();
134 return;
135 }
136
137 ShowColumnsMenu(ScreenToClient(wxGetMousePosition()));
138}
139
8a2e3f80 140#endif // wxUSE_MENUS
faef508f 141
702f5349
VZ
142// ----------------------------------------------------------------------------
143// wxHeaderCtrlBase column reordering
144// ----------------------------------------------------------------------------
145
146void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt& order)
147{
148 const unsigned count = GetColumnCount();
149 wxCHECK_RET( order.size() == count, "wrong number of columns" );
150
151 // check the array validity
152 wxArrayInt seen(count, 0);
153 for ( unsigned n = 0; n < count; n++ )
154 {
155 const unsigned idx = order[n];
156 wxCHECK_RET( idx < count, "invalid column index" );
157 wxCHECK_RET( !seen[idx], "duplicate column index" );
158
159 seen[idx] = 1;
160 }
161
162 DoSetColumnsOrder(order);
163
164 // TODO-RTL: do we need to reverse the array?
165}
166
da5a641f
VZ
167void wxHeaderCtrlBase::ResetColumnsOrder()
168{
169 const unsigned count = GetColumnCount();
170 wxArrayInt order(count);
171 for ( unsigned n = 0; n < count; n++ )
172 order[n] = n;
173
174 DoSetColumnsOrder(order);
175}
176
702f5349
VZ
177wxArrayInt wxHeaderCtrlBase::GetColumnsOrder() const
178{
179 const wxArrayInt order = DoGetColumnsOrder();
180
181 wxASSERT_MSG( order.size() == GetColumnCount(), "invalid order array" );
182
183 return order;
184}
185
186unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos) const
187{
188 wxCHECK_MSG( pos < GetColumnCount(), wxNO_COLUMN, "invalid position" );
189
190 return GetColumnsOrder()[pos];
191}
192
193unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx) const
194{
195 const unsigned count = GetColumnCount();
196
197 wxCHECK_MSG( idx < count, wxNO_COLUMN, "invalid index" );
198
199 const wxArrayInt order = GetColumnsOrder();
200 for ( unsigned n = 0; n < count; n++ )
201 {
202 if ( (unsigned)order[n] == idx )
203 return n;
204 }
205
206 wxFAIL_MSG( "column unexpectedly not displayed at all" );
207
208 return wxNO_COLUMN;
209}
210
1bb74626
VZ
211/* static */
212void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt& order,
213 unsigned int idx,
214 unsigned int pos)
215{
216 const unsigned count = order.size();
217
218 wxArrayInt orderNew;
219 orderNew.reserve(count);
220 for ( unsigned n = 0; ; n++ )
221 {
222 // NB: order of checks is important for this to work when the new
223 // column position is the same as the old one
224
225 // insert the column at its new position
226 if ( orderNew.size() == pos )
227 orderNew.push_back(idx);
228
229 if ( n == count )
230 break;
231
232 // delete the column from its old position
233 const unsigned idxOld = order[n];
234 if ( idxOld == idx )
235 continue;
236
237 orderNew.push_back(idxOld);
238 }
239
240 order.swap(orderNew);
241}
242
f6655391
VZ
243void
244wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt& colIndices, unsigned int count)
245{
246 // update the column indices array if necessary
247 const unsigned countOld = colIndices.size();
248 if ( count > countOld )
249 {
250 // all new columns have default positions equal to their indices
251 for ( unsigned n = countOld; n < count; n++ )
252 colIndices.push_back(n);
253 }
254 else if ( count < countOld )
255 {
256 // filter out all the positions which are invalid now while keeping the
257 // order of the remaining ones
258 wxArrayInt colIndicesNew;
259 colIndicesNew.reserve(count);
260 for ( unsigned n = 0; n < countOld; n++ )
261 {
262 const unsigned idx = colIndices[n];
263 if ( idx < count )
264 colIndicesNew.push_back(idx);
265 }
266
267 colIndices.swap(colIndicesNew);
268 }
613de0e8 269 //else: count didn't really change, nothing to do
f6655391
VZ
270
271 wxASSERT_MSG( colIndices.size() == count, "logic error" );
272}
273
e8f25dbb
VZ
274// ----------------------------------------------------------------------------
275// wxHeaderCtrl extra UI
276// ----------------------------------------------------------------------------
277
8a2e3f80 278#if wxUSE_MENUS
faef508f 279
ddd0db96 280void wxHeaderCtrlBase::AddColumnsItems(wxMenu& menu, int idColumnsBase)
e8f25dbb 281{
e8f25dbb
VZ
282 const unsigned count = GetColumnCount();
283 for ( unsigned n = 0; n < count; n++ )
284 {
285 const wxHeaderColumn& col = GetColumn(n);
ddd0db96 286 menu.AppendCheckItem(idColumnsBase + n, col.GetTitle());
e8f25dbb
VZ
287 if ( col.IsShown() )
288 menu.Check(n, true);
289 }
ddd0db96
VZ
290}
291
292bool wxHeaderCtrlBase::ShowColumnsMenu(const wxPoint& pt, const wxString& title)
293{
294 // construct the menu with the entries for all columns
295 wxMenu menu;
296 if ( !title.empty() )
297 menu.SetTitle(title);
298
299 AddColumnsItems(menu);
e8f25dbb 300
613de0e8
VZ
301 // ... and an extra one to show the customization dialog if the user is
302 // allowed to reorder the columns too
ddd0db96 303 const unsigned count = GetColumnCount();
613de0e8
VZ
304 if ( HasFlag(wxHD_ALLOW_REORDER) )
305 {
306 menu.AppendSeparator();
307 menu.Append(count, _("&Customize..."));
308 }
309
310 // do show the menu and get the user selection
311 const int rc = GetPopupMenuSelectionFromUser(menu, pt);
312 if ( rc == wxID_NONE )
313 return false;
314
315 if ( static_cast<unsigned>(rc) == count )
316 {
317 return ShowCustomizeDialog();
318 }
319 else // a column selected from the menu
320 {
321 UpdateColumnVisibility(rc, !GetColumn(rc).IsShown());
322 }
323
324 return true;
325}
326
8a2e3f80 327#endif // wxUSE_MENUS
faef508f 328
613de0e8
VZ
329bool wxHeaderCtrlBase::ShowCustomizeDialog()
330{
f0bb342f 331#if wxUSE_REARRANGECTRL
af67f39d
VZ
332 // prepare the data for showing the dialog
333 wxArrayInt order = GetColumnsOrder();
334
335 const unsigned count = GetColumnCount();
336
337 // notice that titles are always in the index order, they will be shown
338 // rearranged according to the display order in the dialog
339 wxArrayString titles;
340 titles.reserve(count);
341 for ( unsigned n = 0; n < count; n++ )
342 titles.push_back(GetColumn(n).GetTitle());
343
344 // this loop is however over positions and not indices
345 unsigned pos;
346 for ( pos = 0; pos < count; pos++ )
347 {
348 int& idx = order[pos];
349 if ( GetColumn(idx).IsHidden() )
350 {
351 // indicate that this one is hidden
352 idx = ~idx;
353 }
354 }
355
356 // do show it
357 wxHeaderColumnsRearrangeDialog dlg(this, order, titles);
f0bb342f 358 if ( dlg.ShowModal() == wxID_OK )
af67f39d 359 {
f0bb342f
VZ
360 // and apply the changes
361 order = dlg.GetOrder();
362 for ( pos = 0; pos < count; pos++ )
af67f39d 363 {
f0bb342f
VZ
364 int& idx = order[pos];
365 const bool show = idx >= 0;
366 if ( !show )
367 {
368 // make all indices positive for passing them to SetColumnsOrder()
369 idx = ~idx;
370 }
371
372 if ( show != GetColumn(idx).IsShown() )
373 UpdateColumnVisibility(idx, show);
af67f39d
VZ
374 }
375
f0bb342f
VZ
376 UpdateColumnsOrder(order);
377 SetColumnsOrder(order);
af67f39d 378
f0bb342f
VZ
379 return true;
380 }
381#endif // wxUSE_REARRANGECTRL
af67f39d 382
f0bb342f 383 return false;
e8f25dbb
VZ
384}
385
e2bfe673
VZ
386// ============================================================================
387// wxHeaderCtrlSimple implementation
388// ============================================================================
389
390void wxHeaderCtrlSimple::Init()
391{
392 m_sortKey = wxNO_COLUMN;
393}
394
482d06f8 395const wxHeaderColumn& wxHeaderCtrlSimple::GetColumn(unsigned int idx) const
e2bfe673
VZ
396{
397 return m_cols[idx];
398}
399
400void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple& col, unsigned int idx)
401{
402 m_cols.insert(m_cols.begin() + idx, col);
403
404 UpdateColumnCount();
405}
406
407void wxHeaderCtrlSimple::DoDelete(unsigned int idx)
408{
409 m_cols.erase(m_cols.begin() + idx);
410 if ( idx == m_sortKey )
411 m_sortKey = wxNO_COLUMN;
412
413 UpdateColumnCount();
414}
415
416void wxHeaderCtrlSimple::DeleteAllColumns()
417{
418 m_cols.clear();
419 m_sortKey = wxNO_COLUMN;
420
421 UpdateColumnCount();
422}
423
424
425void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx, bool show)
426{
427 if ( show != m_cols[idx].IsShown() )
428 {
429 m_cols[idx].SetHidden(!show);
430
431 UpdateColumn(idx);
432 }
433}
434
435void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx, bool ascending)
436{
437 RemoveSortIndicator();
438
439 m_cols[idx].SetAsSortKey(ascending);
440 m_sortKey = idx;
441
442 UpdateColumn(idx);
443}
444
445void wxHeaderCtrlSimple::RemoveSortIndicator()
446{
447 if ( m_sortKey != wxNO_COLUMN )
448 {
449 const unsigned sortOld = m_sortKey;
450 m_sortKey = wxNO_COLUMN;
451
452 m_cols[sortOld].UnsetAsSortKey();
453
454 UpdateColumn(sortOld);
455 }
456}
457
e5a16353
VZ
458bool
459wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
460{
461 const int widthContents = GetBestFittingWidth(idx);
462 if ( widthContents == -1 )
463 return false;
464
465 m_cols[idx].SetWidth(wxMax(widthContents, widthTitle));
e5a16353
VZ
466
467 return true;
468}
469
fa3d4aaf
VZ
470// ============================================================================
471// wxHeaderCtrlEvent implementation
472// ============================================================================
473
474IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent, wxNotifyEvent)
475
3c778901
VZ
476wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_CLICK, wxHeaderCtrlEvent);
477wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_RIGHT_CLICK, wxHeaderCtrlEvent);
478wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_MIDDLE_CLICK, wxHeaderCtrlEvent);
fa3d4aaf 479
3c778901
VZ
480wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_DCLICK, wxHeaderCtrlEvent);
481wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_RIGHT_DCLICK, wxHeaderCtrlEvent);
482wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_MIDDLE_DCLICK, wxHeaderCtrlEvent);
3bfaa5a7 483
3c778901 484wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK, wxHeaderCtrlEvent);
aef252d9 485
3c778901
VZ
486wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_BEGIN_RESIZE, wxHeaderCtrlEvent);
487wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_RESIZING, wxHeaderCtrlEvent);
488wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_END_RESIZE, wxHeaderCtrlEvent);
702f5349 489
3c778901
VZ
490wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_BEGIN_REORDER, wxHeaderCtrlEvent);
491wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_END_REORDER, wxHeaderCtrlEvent);
565804f2 492
3c778901 493wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED, wxHeaderCtrlEvent);
e721a2a2
VZ
494
495#endif // wxUSE_HEADERCTRL