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