1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/headerctrlcmn.cpp
3 // Purpose: implementation of wxHeaderCtrlBase
4 // Author: Vadim Zeitlin
6 // Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
31 #include "wx/headerctrl.h"
32 #include "wx/rearrangectrl.h"
33 #include "wx/renderer.h"
38 // ----------------------------------------------------------------------------
40 // ----------------------------------------------------------------------------
42 const unsigned int wxNO_COLUMN
= static_cast<unsigned>(-1);
44 // ----------------------------------------------------------------------------
45 // wxHeaderColumnsRearrangeDialog: dialog for customizing our columns
46 // ----------------------------------------------------------------------------
48 #if wxUSE_REARRANGECTRL
50 class wxHeaderColumnsRearrangeDialog
: public wxRearrangeDialog
53 wxHeaderColumnsRearrangeDialog(wxWindow
*parent
,
54 const wxArrayInt
& order
,
55 const wxArrayString
& items
)
59 _("Please select the columns to show and define their order:"),
60 _("Customize Columns"),
68 #endif // wxUSE_REARRANGECTRL
70 } // anonymous namespace
72 // ============================================================================
73 // wxHeaderCtrlBase implementation
74 // ============================================================================
76 extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr
[] = "wxHeaderCtrl";
78 BEGIN_EVENT_TABLE(wxHeaderCtrlBase
, wxControl
)
79 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY
, wxHeaderCtrlBase::OnSeparatorDClick
)
81 EVT_HEADER_RIGHT_CLICK(wxID_ANY
, wxHeaderCtrlBase::OnRClick
)
85 void wxHeaderCtrlBase::ScrollWindow(int dx
,
86 int WXUNUSED_UNLESS_DEBUG(dy
),
87 const wxRect
* WXUNUSED_UNLESS_DEBUG(rect
))
90 // this doesn't make sense at all
91 wxASSERT_MSG( !dy
, "header window can't be scrolled vertically" );
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" );
100 void wxHeaderCtrlBase::SetColumnCount(unsigned int count
)
102 if ( count
!= GetColumnCount() )
103 OnColumnCountChanging(count
);
105 // still call DoSetCount() even if the count didn't really change in order
106 // to update all the columns
110 int wxHeaderCtrlBase::GetColumnTitleWidth(const wxHeaderColumn
& col
)
112 int w
= wxWindowBase::GetTextExtent(col
.GetTitle()).x
;
115 w
+= wxRendererNative::Get().GetHeaderButtonMargin(this);
117 // if a bitmap is used, add space for it and 2px border:
118 wxBitmap bmp
= col
.GetBitmap();
120 w
+= bmp
.GetWidth() + 2;
125 // ----------------------------------------------------------------------------
126 // wxHeaderCtrlBase event handling
127 // ----------------------------------------------------------------------------
129 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent
& event
)
131 const unsigned col
= event
.GetColumn();
132 const wxHeaderColumn
& column
= GetColumn(col
);
134 if ( !column
.IsResizeable() )
140 int w
= GetColumnTitleWidth(column
);
142 if ( !UpdateColumnWidthToFit(col
, w
) )
150 void wxHeaderCtrlBase::OnRClick(wxHeaderCtrlEvent
& event
)
152 if ( !HasFlag(wxHD_ALLOW_HIDE
) )
158 ShowColumnsMenu(ScreenToClient(wxGetMousePosition()));
161 #endif // wxUSE_MENUS
163 // ----------------------------------------------------------------------------
164 // wxHeaderCtrlBase column reordering
165 // ----------------------------------------------------------------------------
167 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt
& order
)
169 const unsigned count
= GetColumnCount();
170 wxCHECK_RET( order
.size() == count
, "wrong number of columns" );
172 // check the array validity
173 wxArrayInt
seen(count
, 0);
174 for ( unsigned n
= 0; n
< count
; n
++ )
176 const unsigned idx
= order
[n
];
177 wxCHECK_RET( idx
< count
, "invalid column index" );
178 wxCHECK_RET( !seen
[idx
], "duplicate column index" );
183 DoSetColumnsOrder(order
);
185 // TODO-RTL: do we need to reverse the array?
188 void wxHeaderCtrlBase::ResetColumnsOrder()
190 const unsigned count
= GetColumnCount();
191 wxArrayInt
order(count
);
192 for ( unsigned n
= 0; n
< count
; n
++ )
195 DoSetColumnsOrder(order
);
198 wxArrayInt
wxHeaderCtrlBase::GetColumnsOrder() const
200 const wxArrayInt order
= DoGetColumnsOrder();
202 wxASSERT_MSG( order
.size() == GetColumnCount(), "invalid order array" );
207 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos
) const
209 wxCHECK_MSG( pos
< GetColumnCount(), wxNO_COLUMN
, "invalid position" );
211 return GetColumnsOrder()[pos
];
214 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx
) const
216 const unsigned count
= GetColumnCount();
218 wxCHECK_MSG( idx
< count
, wxNO_COLUMN
, "invalid index" );
220 const wxArrayInt order
= GetColumnsOrder();
221 for ( unsigned n
= 0; n
< count
; n
++ )
223 if ( (unsigned)order
[n
] == idx
)
227 wxFAIL_MSG( "column unexpectedly not displayed at all" );
233 void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt
& order
,
237 const unsigned count
= order
.size();
240 orderNew
.reserve(count
);
241 for ( unsigned n
= 0; ; n
++ )
243 // NB: order of checks is important for this to work when the new
244 // column position is the same as the old one
246 // insert the column at its new position
247 if ( orderNew
.size() == pos
)
248 orderNew
.push_back(idx
);
253 // delete the column from its old position
254 const unsigned idxOld
= order
[n
];
258 orderNew
.push_back(idxOld
);
261 order
.swap(orderNew
);
265 wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt
& colIndices
, unsigned int count
)
267 // update the column indices array if necessary
268 const unsigned countOld
= colIndices
.size();
269 if ( count
> countOld
)
271 // all new columns have default positions equal to their indices
272 for ( unsigned n
= countOld
; n
< count
; n
++ )
273 colIndices
.push_back(n
);
275 else if ( count
< countOld
)
277 // filter out all the positions which are invalid now while keeping the
278 // order of the remaining ones
279 wxArrayInt colIndicesNew
;
280 colIndicesNew
.reserve(count
);
281 for ( unsigned n
= 0; n
< countOld
; n
++ )
283 const unsigned idx
= colIndices
[n
];
285 colIndicesNew
.push_back(idx
);
288 colIndices
.swap(colIndicesNew
);
290 //else: count didn't really change, nothing to do
292 wxASSERT_MSG( colIndices
.size() == count
, "logic error" );
295 // ----------------------------------------------------------------------------
296 // wxHeaderCtrl extra UI
297 // ----------------------------------------------------------------------------
301 void wxHeaderCtrlBase::AddColumnsItems(wxMenu
& menu
, int idColumnsBase
)
303 const unsigned count
= GetColumnCount();
304 for ( unsigned n
= 0; n
< count
; n
++ )
306 const wxHeaderColumn
& col
= GetColumn(n
);
307 menu
.AppendCheckItem(idColumnsBase
+ n
, col
.GetTitle());
313 bool wxHeaderCtrlBase::ShowColumnsMenu(const wxPoint
& pt
, const wxString
& title
)
315 // construct the menu with the entries for all columns
317 if ( !title
.empty() )
318 menu
.SetTitle(title
);
320 AddColumnsItems(menu
);
322 // ... and an extra one to show the customization dialog if the user is
323 // allowed to reorder the columns too
324 const unsigned count
= GetColumnCount();
325 if ( HasFlag(wxHD_ALLOW_REORDER
) )
327 menu
.AppendSeparator();
328 menu
.Append(count
, _("&Customize..."));
331 // do show the menu and get the user selection
332 const int rc
= GetPopupMenuSelectionFromUser(menu
, pt
);
333 if ( rc
== wxID_NONE
)
336 if ( static_cast<unsigned>(rc
) == count
)
338 return ShowCustomizeDialog();
340 else // a column selected from the menu
342 UpdateColumnVisibility(rc
, !GetColumn(rc
).IsShown());
348 #endif // wxUSE_MENUS
350 bool wxHeaderCtrlBase::ShowCustomizeDialog()
352 #if wxUSE_REARRANGECTRL
353 // prepare the data for showing the dialog
354 wxArrayInt order
= GetColumnsOrder();
356 const unsigned count
= GetColumnCount();
358 // notice that titles are always in the index order, they will be shown
359 // rearranged according to the display order in the dialog
360 wxArrayString titles
;
361 titles
.reserve(count
);
362 for ( unsigned n
= 0; n
< count
; n
++ )
363 titles
.push_back(GetColumn(n
).GetTitle());
365 // this loop is however over positions and not indices
367 for ( pos
= 0; pos
< count
; pos
++ )
369 int& idx
= order
[pos
];
370 if ( GetColumn(idx
).IsHidden() )
372 // indicate that this one is hidden
378 wxHeaderColumnsRearrangeDialog
dlg(this, order
, titles
);
379 if ( dlg
.ShowModal() == wxID_OK
)
381 // and apply the changes
382 order
= dlg
.GetOrder();
383 for ( pos
= 0; pos
< count
; pos
++ )
385 int& idx
= order
[pos
];
386 const bool show
= idx
>= 0;
389 // make all indices positive for passing them to SetColumnsOrder()
393 if ( show
!= GetColumn(idx
).IsShown() )
394 UpdateColumnVisibility(idx
, show
);
397 UpdateColumnsOrder(order
);
398 SetColumnsOrder(order
);
402 #endif // wxUSE_REARRANGECTRL
407 // ============================================================================
408 // wxHeaderCtrlSimple implementation
409 // ============================================================================
411 void wxHeaderCtrlSimple::Init()
413 m_sortKey
= wxNO_COLUMN
;
416 const wxHeaderColumn
& wxHeaderCtrlSimple::GetColumn(unsigned int idx
) const
421 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple
& col
, unsigned int idx
)
423 m_cols
.insert(m_cols
.begin() + idx
, col
);
428 void wxHeaderCtrlSimple::DoDelete(unsigned int idx
)
430 m_cols
.erase(m_cols
.begin() + idx
);
431 if ( idx
== m_sortKey
)
432 m_sortKey
= wxNO_COLUMN
;
437 void wxHeaderCtrlSimple::DeleteAllColumns()
440 m_sortKey
= wxNO_COLUMN
;
446 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx
, bool show
)
448 if ( show
!= m_cols
[idx
].IsShown() )
450 m_cols
[idx
].SetHidden(!show
);
456 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx
, bool ascending
)
458 RemoveSortIndicator();
460 m_cols
[idx
].SetSortOrder(ascending
);
466 void wxHeaderCtrlSimple::RemoveSortIndicator()
468 if ( m_sortKey
!= wxNO_COLUMN
)
470 const unsigned sortOld
= m_sortKey
;
471 m_sortKey
= wxNO_COLUMN
;
473 m_cols
[sortOld
].UnsetAsSortKey();
475 UpdateColumn(sortOld
);
480 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx
, int widthTitle
)
482 const int widthContents
= GetBestFittingWidth(idx
);
483 if ( widthContents
== -1 )
486 m_cols
[idx
].SetWidth(wxMax(widthContents
, widthTitle
));
491 // ============================================================================
492 // wxHeaderCtrlEvent implementation
493 // ============================================================================
495 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent
, wxNotifyEvent
)
497 wxDEFINE_EVENT( wxEVT_HEADER_CLICK
, wxHeaderCtrlEvent
);
498 wxDEFINE_EVENT( wxEVT_HEADER_RIGHT_CLICK
, wxHeaderCtrlEvent
);
499 wxDEFINE_EVENT( wxEVT_HEADER_MIDDLE_CLICK
, wxHeaderCtrlEvent
);
501 wxDEFINE_EVENT( wxEVT_HEADER_DCLICK
, wxHeaderCtrlEvent
);
502 wxDEFINE_EVENT( wxEVT_HEADER_RIGHT_DCLICK
, wxHeaderCtrlEvent
);
503 wxDEFINE_EVENT( wxEVT_HEADER_MIDDLE_DCLICK
, wxHeaderCtrlEvent
);
505 wxDEFINE_EVENT( wxEVT_HEADER_SEPARATOR_DCLICK
, wxHeaderCtrlEvent
);
507 wxDEFINE_EVENT( wxEVT_HEADER_BEGIN_RESIZE
, wxHeaderCtrlEvent
);
508 wxDEFINE_EVENT( wxEVT_HEADER_RESIZING
, wxHeaderCtrlEvent
);
509 wxDEFINE_EVENT( wxEVT_HEADER_END_RESIZE
, wxHeaderCtrlEvent
);
511 wxDEFINE_EVENT( wxEVT_HEADER_BEGIN_REORDER
, wxHeaderCtrlEvent
);
512 wxDEFINE_EVENT( wxEVT_HEADER_END_REORDER
, wxHeaderCtrlEvent
);
514 wxDEFINE_EVENT( wxEVT_HEADER_DRAGGING_CANCELLED
, wxHeaderCtrlEvent
);
516 #endif // wxUSE_HEADERCTRL