1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/headerctrlcmn.cpp
3 // Purpose: implementation of wxHeaderCtrlBase
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
32 #include "wx/headerctrl.h"
33 #include "wx/rearrangectrl.h"
34 #include "wx/renderer.h"
39 // ----------------------------------------------------------------------------
41 // ----------------------------------------------------------------------------
43 const unsigned int wxNO_COLUMN
= static_cast<unsigned>(-1);
45 // ----------------------------------------------------------------------------
46 // wxHeaderColumnsRearrangeDialog: dialog for customizing our columns
47 // ----------------------------------------------------------------------------
49 #if wxUSE_REARRANGECTRL
51 class wxHeaderColumnsRearrangeDialog
: public wxRearrangeDialog
54 wxHeaderColumnsRearrangeDialog(wxWindow
*parent
,
55 const wxArrayInt
& order
,
56 const wxArrayString
& items
)
60 _("Please select the columns to show and define their order:"),
61 _("Customize Columns"),
69 #endif // wxUSE_REARRANGECTRL
71 } // anonymous namespace
73 // ============================================================================
74 // wxHeaderCtrlBase implementation
75 // ============================================================================
77 extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr
[] = "wxHeaderCtrl";
79 BEGIN_EVENT_TABLE(wxHeaderCtrlBase
, wxControl
)
80 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY
, wxHeaderCtrlBase::OnSeparatorDClick
)
82 EVT_HEADER_RIGHT_CLICK(wxID_ANY
, wxHeaderCtrlBase::OnRClick
)
86 void wxHeaderCtrlBase::ScrollWindow(int dx
,
87 int WXUNUSED_UNLESS_DEBUG(dy
),
88 const wxRect
* WXUNUSED_UNLESS_DEBUG(rect
))
91 // this doesn't make sense at all
92 wxASSERT_MSG( !dy
, "header window can't be scrolled vertically" );
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" );
101 void wxHeaderCtrlBase::SetColumnCount(unsigned int count
)
103 if ( count
!= GetColumnCount() )
104 OnColumnCountChanging(count
);
106 // still call DoSetCount() even if the count didn't really change in order
107 // to update all the columns
111 int wxHeaderCtrlBase::GetColumnTitleWidth(const wxHeaderColumn
& col
)
113 int w
= wxWindowBase::GetTextExtent(col
.GetTitle()).x
;
116 w
+= wxRendererNative::Get().GetHeaderButtonMargin(this);
118 // if a bitmap is used, add space for it and 2px border:
119 wxBitmap bmp
= col
.GetBitmap();
121 w
+= bmp
.GetWidth() + 2;
126 // ----------------------------------------------------------------------------
127 // wxHeaderCtrlBase event handling
128 // ----------------------------------------------------------------------------
130 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent
& event
)
132 const unsigned col
= event
.GetColumn();
133 const wxHeaderColumn
& column
= GetColumn(col
);
135 if ( !column
.IsResizeable() )
141 int w
= GetColumnTitleWidth(column
);
143 if ( !UpdateColumnWidthToFit(col
, w
) )
151 void wxHeaderCtrlBase::OnRClick(wxHeaderCtrlEvent
& event
)
153 if ( !HasFlag(wxHD_ALLOW_HIDE
) )
159 ShowColumnsMenu(ScreenToClient(wxGetMousePosition()));
162 #endif // wxUSE_MENUS
164 // ----------------------------------------------------------------------------
165 // wxHeaderCtrlBase column reordering
166 // ----------------------------------------------------------------------------
168 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt
& order
)
170 const unsigned count
= GetColumnCount();
171 wxCHECK_RET( order
.size() == count
, "wrong number of columns" );
173 // check the array validity
174 wxArrayInt
seen(count
, 0);
175 for ( unsigned n
= 0; n
< count
; n
++ )
177 const unsigned idx
= order
[n
];
178 wxCHECK_RET( idx
< count
, "invalid column index" );
179 wxCHECK_RET( !seen
[idx
], "duplicate column index" );
184 DoSetColumnsOrder(order
);
186 // TODO-RTL: do we need to reverse the array?
189 void wxHeaderCtrlBase::ResetColumnsOrder()
191 const unsigned count
= GetColumnCount();
192 wxArrayInt
order(count
);
193 for ( unsigned n
= 0; n
< count
; n
++ )
196 DoSetColumnsOrder(order
);
199 wxArrayInt
wxHeaderCtrlBase::GetColumnsOrder() const
201 const wxArrayInt order
= DoGetColumnsOrder();
203 wxASSERT_MSG( order
.size() == GetColumnCount(), "invalid order array" );
208 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos
) const
210 wxCHECK_MSG( pos
< GetColumnCount(), wxNO_COLUMN
, "invalid position" );
212 return GetColumnsOrder()[pos
];
215 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx
) const
217 const unsigned count
= GetColumnCount();
219 wxCHECK_MSG( idx
< count
, wxNO_COLUMN
, "invalid index" );
221 const wxArrayInt order
= GetColumnsOrder();
222 for ( unsigned n
= 0; n
< count
; n
++ )
224 if ( (unsigned)order
[n
] == idx
)
228 wxFAIL_MSG( "column unexpectedly not displayed at all" );
234 void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt
& order
,
238 const unsigned count
= order
.size();
241 orderNew
.reserve(count
);
242 for ( unsigned n
= 0; ; n
++ )
244 // NB: order of checks is important for this to work when the new
245 // column position is the same as the old one
247 // insert the column at its new position
248 if ( orderNew
.size() == pos
)
249 orderNew
.push_back(idx
);
254 // delete the column from its old position
255 const unsigned idxOld
= order
[n
];
259 orderNew
.push_back(idxOld
);
262 order
.swap(orderNew
);
266 wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt
& colIndices
, unsigned int count
)
268 // update the column indices array if necessary
269 const unsigned countOld
= colIndices
.size();
270 if ( count
> countOld
)
272 // all new columns have default positions equal to their indices
273 for ( unsigned n
= countOld
; n
< count
; n
++ )
274 colIndices
.push_back(n
);
276 else if ( count
< countOld
)
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
++ )
284 const unsigned idx
= colIndices
[n
];
286 colIndicesNew
.push_back(idx
);
289 colIndices
.swap(colIndicesNew
);
291 //else: count didn't really change, nothing to do
293 wxASSERT_MSG( colIndices
.size() == count
, "logic error" );
296 // ----------------------------------------------------------------------------
297 // wxHeaderCtrl extra UI
298 // ----------------------------------------------------------------------------
302 void wxHeaderCtrlBase::AddColumnsItems(wxMenu
& menu
, int idColumnsBase
)
304 const unsigned count
= GetColumnCount();
305 for ( unsigned n
= 0; n
< count
; n
++ )
307 const wxHeaderColumn
& col
= GetColumn(n
);
308 menu
.AppendCheckItem(idColumnsBase
+ n
, col
.GetTitle());
314 bool wxHeaderCtrlBase::ShowColumnsMenu(const wxPoint
& pt
, const wxString
& title
)
316 // construct the menu with the entries for all columns
318 if ( !title
.empty() )
319 menu
.SetTitle(title
);
321 AddColumnsItems(menu
);
323 // ... and an extra one to show the customization dialog if the user is
324 // allowed to reorder the columns too
325 const unsigned count
= GetColumnCount();
326 if ( HasFlag(wxHD_ALLOW_REORDER
) )
328 menu
.AppendSeparator();
329 menu
.Append(count
, _("&Customize..."));
332 // do show the menu and get the user selection
333 const int rc
= GetPopupMenuSelectionFromUser(menu
, pt
);
334 if ( rc
== wxID_NONE
)
337 if ( static_cast<unsigned>(rc
) == count
)
339 return ShowCustomizeDialog();
341 else // a column selected from the menu
343 UpdateColumnVisibility(rc
, !GetColumn(rc
).IsShown());
349 #endif // wxUSE_MENUS
351 bool wxHeaderCtrlBase::ShowCustomizeDialog()
353 #if wxUSE_REARRANGECTRL
354 // prepare the data for showing the dialog
355 wxArrayInt order
= GetColumnsOrder();
357 const unsigned count
= GetColumnCount();
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());
366 // this loop is however over positions and not indices
368 for ( pos
= 0; pos
< count
; pos
++ )
370 int& idx
= order
[pos
];
371 if ( GetColumn(idx
).IsHidden() )
373 // indicate that this one is hidden
379 wxHeaderColumnsRearrangeDialog
dlg(this, order
, titles
);
380 if ( dlg
.ShowModal() == wxID_OK
)
382 // and apply the changes
383 order
= dlg
.GetOrder();
384 for ( pos
= 0; pos
< count
; pos
++ )
386 int& idx
= order
[pos
];
387 const bool show
= idx
>= 0;
390 // make all indices positive for passing them to SetColumnsOrder()
394 if ( show
!= GetColumn(idx
).IsShown() )
395 UpdateColumnVisibility(idx
, show
);
398 UpdateColumnsOrder(order
);
399 SetColumnsOrder(order
);
403 #endif // wxUSE_REARRANGECTRL
408 // ============================================================================
409 // wxHeaderCtrlSimple implementation
410 // ============================================================================
412 void wxHeaderCtrlSimple::Init()
414 m_sortKey
= wxNO_COLUMN
;
417 const wxHeaderColumn
& wxHeaderCtrlSimple::GetColumn(unsigned int idx
) const
422 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple
& col
, unsigned int idx
)
424 m_cols
.insert(m_cols
.begin() + idx
, col
);
429 void wxHeaderCtrlSimple::DoDelete(unsigned int idx
)
431 m_cols
.erase(m_cols
.begin() + idx
);
432 if ( idx
== m_sortKey
)
433 m_sortKey
= wxNO_COLUMN
;
438 void wxHeaderCtrlSimple::DeleteAllColumns()
441 m_sortKey
= wxNO_COLUMN
;
447 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx
, bool show
)
449 if ( show
!= m_cols
[idx
].IsShown() )
451 m_cols
[idx
].SetHidden(!show
);
457 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx
, bool ascending
)
459 RemoveSortIndicator();
461 m_cols
[idx
].SetSortOrder(ascending
);
467 void wxHeaderCtrlSimple::RemoveSortIndicator()
469 if ( m_sortKey
!= wxNO_COLUMN
)
471 const unsigned sortOld
= m_sortKey
;
472 m_sortKey
= wxNO_COLUMN
;
474 m_cols
[sortOld
].UnsetAsSortKey();
476 UpdateColumn(sortOld
);
481 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx
, int widthTitle
)
483 const int widthContents
= GetBestFittingWidth(idx
);
484 if ( widthContents
== -1 )
487 m_cols
[idx
].SetWidth(wxMax(widthContents
, widthTitle
));
492 // ============================================================================
493 // wxHeaderCtrlEvent implementation
494 // ============================================================================
496 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent
, wxNotifyEvent
)
498 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_CLICK
, wxHeaderCtrlEvent
);
499 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_RIGHT_CLICK
, wxHeaderCtrlEvent
);
500 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_MIDDLE_CLICK
, wxHeaderCtrlEvent
);
502 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_DCLICK
, wxHeaderCtrlEvent
);
503 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_RIGHT_DCLICK
, wxHeaderCtrlEvent
);
504 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
, wxHeaderCtrlEvent
);
506 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
, wxHeaderCtrlEvent
);
508 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_BEGIN_RESIZE
, wxHeaderCtrlEvent
);
509 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_RESIZING
, wxHeaderCtrlEvent
);
510 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_END_RESIZE
, wxHeaderCtrlEvent
);
512 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_BEGIN_REORDER
, wxHeaderCtrlEvent
);
513 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_END_REORDER
, wxHeaderCtrlEvent
);
515 wxDEFINE_EVENT( wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED
, wxHeaderCtrlEvent
);
517 #endif // wxUSE_HEADERCTRL