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