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"
38 // ----------------------------------------------------------------------------
40 // ----------------------------------------------------------------------------
42 const unsigned int wxNO_COLUMN
= static_cast<unsigned>(-1);
44 // ----------------------------------------------------------------------------
45 // wxHeaderColumnsRearrangeDialog: dialog for customizing our columns
46 // ----------------------------------------------------------------------------
48 class wxHeaderColumnsRearrangeDialog
: public wxRearrangeDialog
51 wxHeaderColumnsRearrangeDialog(wxWindow
*parent
,
52 const wxArrayInt
& order
,
53 const wxArrayString
& items
)
57 _("Please select the columns to show and define their order:"),
58 _("Customize Columns"),
66 } // anonymous namespace
68 // ============================================================================
69 // wxHeaderCtrlBase implementation
70 // ============================================================================
72 extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr
[] = "wxHeaderCtrl";
74 BEGIN_EVENT_TABLE(wxHeaderCtrlBase
, wxControl
)
75 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY
, wxHeaderCtrlBase::OnSeparatorDClick
)
77 EVT_HEADER_RIGHT_CLICK(wxID_ANY
, wxHeaderCtrlBase::OnRClick
)
81 void wxHeaderCtrlBase::ScrollWindow(int dx
,
82 int WXUNUSED_UNLESS_DEBUG(dy
),
83 const wxRect
* WXUNUSED_UNLESS_DEBUG(rect
))
86 // this doesn't make sense at all
87 wxASSERT_MSG( !dy
, "header window can't be scrolled vertically" );
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" );
96 void wxHeaderCtrlBase::SetColumnCount(unsigned int count
)
98 if ( count
!= GetColumnCount() )
99 OnColumnCountChanging(count
);
101 // still call DoSetCount() even if the count didn't really change in order
102 // to update all the columns
106 // ----------------------------------------------------------------------------
107 // wxHeaderCtrlBase event handling
108 // ----------------------------------------------------------------------------
110 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent
& event
)
112 const unsigned col
= event
.GetColumn();
114 int w
= wxWindowBase::GetTextExtent(GetColumn(col
).GetTitle()).x
;
115 w
+= 4*GetCharWidth(); // add some arbitrary margins around text
117 if ( !UpdateColumnWidthToFit(col
, w
) )
125 void wxHeaderCtrlBase::OnRClick(wxHeaderCtrlEvent
& event
)
127 if ( !HasFlag(wxHD_ALLOW_HIDE
) )
133 ShowColumnsMenu(ScreenToClient(wxGetMousePosition()));
136 #endif // wxUSE_MENUS
138 // ----------------------------------------------------------------------------
139 // wxHeaderCtrlBase column reordering
140 // ----------------------------------------------------------------------------
142 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt
& order
)
144 const unsigned count
= GetColumnCount();
145 wxCHECK_RET( order
.size() == count
, "wrong number of columns" );
147 // check the array validity
148 wxArrayInt
seen(count
, 0);
149 for ( unsigned n
= 0; n
< count
; n
++ )
151 const unsigned idx
= order
[n
];
152 wxCHECK_RET( idx
< count
, "invalid column index" );
153 wxCHECK_RET( !seen
[idx
], "duplicate column index" );
158 DoSetColumnsOrder(order
);
160 // TODO-RTL: do we need to reverse the array?
163 void wxHeaderCtrlBase::ResetColumnsOrder()
165 const unsigned count
= GetColumnCount();
166 wxArrayInt
order(count
);
167 for ( unsigned n
= 0; n
< count
; n
++ )
170 DoSetColumnsOrder(order
);
173 wxArrayInt
wxHeaderCtrlBase::GetColumnsOrder() const
175 const wxArrayInt order
= DoGetColumnsOrder();
177 wxASSERT_MSG( order
.size() == GetColumnCount(), "invalid order array" );
182 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos
) const
184 wxCHECK_MSG( pos
< GetColumnCount(), wxNO_COLUMN
, "invalid position" );
186 return GetColumnsOrder()[pos
];
189 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx
) const
191 const unsigned count
= GetColumnCount();
193 wxCHECK_MSG( idx
< count
, wxNO_COLUMN
, "invalid index" );
195 const wxArrayInt order
= GetColumnsOrder();
196 for ( unsigned n
= 0; n
< count
; n
++ )
198 if ( (unsigned)order
[n
] == idx
)
202 wxFAIL_MSG( "column unexpectedly not displayed at all" );
208 void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt
& order
,
212 const unsigned count
= order
.size();
215 orderNew
.reserve(count
);
216 for ( unsigned n
= 0; ; n
++ )
218 // NB: order of checks is important for this to work when the new
219 // column position is the same as the old one
221 // insert the column at its new position
222 if ( orderNew
.size() == pos
)
223 orderNew
.push_back(idx
);
228 // delete the column from its old position
229 const unsigned idxOld
= order
[n
];
233 orderNew
.push_back(idxOld
);
236 order
.swap(orderNew
);
240 wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt
& colIndices
, unsigned int count
)
242 // update the column indices array if necessary
243 const unsigned countOld
= colIndices
.size();
244 if ( count
> countOld
)
246 // all new columns have default positions equal to their indices
247 for ( unsigned n
= countOld
; n
< count
; n
++ )
248 colIndices
.push_back(n
);
250 else if ( count
< countOld
)
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
++ )
258 const unsigned idx
= colIndices
[n
];
260 colIndicesNew
.push_back(idx
);
263 colIndices
.swap(colIndicesNew
);
265 //else: count didn't really change, nothing to do
267 wxASSERT_MSG( colIndices
.size() == count
, "logic error" );
270 // ----------------------------------------------------------------------------
271 // wxHeaderCtrl extra UI
272 // ----------------------------------------------------------------------------
276 void wxHeaderCtrlBase::AddColumnsItems(wxMenu
& menu
, int idColumnsBase
)
278 const unsigned count
= GetColumnCount();
279 for ( unsigned n
= 0; n
< count
; n
++ )
281 const wxHeaderColumn
& col
= GetColumn(n
);
282 menu
.AppendCheckItem(idColumnsBase
+ n
, col
.GetTitle());
288 bool wxHeaderCtrlBase::ShowColumnsMenu(const wxPoint
& pt
, const wxString
& title
)
290 // construct the menu with the entries for all columns
292 if ( !title
.empty() )
293 menu
.SetTitle(title
);
295 AddColumnsItems(menu
);
297 // ... and an extra one to show the customization dialog if the user is
298 // allowed to reorder the columns too
299 const unsigned count
= GetColumnCount();
300 if ( HasFlag(wxHD_ALLOW_REORDER
) )
302 menu
.AppendSeparator();
303 menu
.Append(count
, _("&Customize..."));
306 // do show the menu and get the user selection
307 const int rc
= GetPopupMenuSelectionFromUser(menu
, pt
);
308 if ( rc
== wxID_NONE
)
311 if ( static_cast<unsigned>(rc
) == count
)
313 return ShowCustomizeDialog();
315 else // a column selected from the menu
317 UpdateColumnVisibility(rc
, !GetColumn(rc
).IsShown());
323 #endif // wxUSE_MENUS
325 bool wxHeaderCtrlBase::ShowCustomizeDialog()
327 #if wxUSE_REARRANGECTRL
328 // prepare the data for showing the dialog
329 wxArrayInt order
= GetColumnsOrder();
331 const unsigned count
= GetColumnCount();
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());
340 // this loop is however over positions and not indices
342 for ( pos
= 0; pos
< count
; pos
++ )
344 int& idx
= order
[pos
];
345 if ( GetColumn(idx
).IsHidden() )
347 // indicate that this one is hidden
353 wxHeaderColumnsRearrangeDialog
dlg(this, order
, titles
);
354 if ( dlg
.ShowModal() == wxID_OK
)
356 // and apply the changes
357 order
= dlg
.GetOrder();
358 for ( pos
= 0; pos
< count
; pos
++ )
360 int& idx
= order
[pos
];
361 const bool show
= idx
>= 0;
364 // make all indices positive for passing them to SetColumnsOrder()
368 if ( show
!= GetColumn(idx
).IsShown() )
369 UpdateColumnVisibility(idx
, show
);
372 UpdateColumnsOrder(order
);
373 SetColumnsOrder(order
);
377 #endif // wxUSE_REARRANGECTRL
382 // ============================================================================
383 // wxHeaderCtrlSimple implementation
384 // ============================================================================
386 void wxHeaderCtrlSimple::Init()
388 m_sortKey
= wxNO_COLUMN
;
391 const wxHeaderColumn
& wxHeaderCtrlSimple::GetColumn(unsigned int idx
) const
396 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple
& col
, unsigned int idx
)
398 m_cols
.insert(m_cols
.begin() + idx
, col
);
403 void wxHeaderCtrlSimple::DoDelete(unsigned int idx
)
405 m_cols
.erase(m_cols
.begin() + idx
);
406 if ( idx
== m_sortKey
)
407 m_sortKey
= wxNO_COLUMN
;
412 void wxHeaderCtrlSimple::DeleteAllColumns()
415 m_sortKey
= wxNO_COLUMN
;
421 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx
, bool show
)
423 if ( show
!= m_cols
[idx
].IsShown() )
425 m_cols
[idx
].SetHidden(!show
);
431 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx
, bool ascending
)
433 RemoveSortIndicator();
435 m_cols
[idx
].SetAsSortKey(ascending
);
441 void wxHeaderCtrlSimple::RemoveSortIndicator()
443 if ( m_sortKey
!= wxNO_COLUMN
)
445 const unsigned sortOld
= m_sortKey
;
446 m_sortKey
= wxNO_COLUMN
;
448 m_cols
[sortOld
].UnsetAsSortKey();
450 UpdateColumn(sortOld
);
455 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx
, int widthTitle
)
457 const int widthContents
= GetBestFittingWidth(idx
);
458 if ( widthContents
== -1 )
461 m_cols
[idx
].SetWidth(wxMax(widthContents
, widthTitle
));
466 // ============================================================================
467 // wxHeaderCtrlEvent implementation
468 // ============================================================================
470 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent
, wxNotifyEvent
)
472 const wxEventType wxEVT_COMMAND_HEADER_CLICK
= wxNewEventType();
473 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK
= wxNewEventType();
474 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK
= wxNewEventType();
476 const wxEventType wxEVT_COMMAND_HEADER_DCLICK
= wxNewEventType();
477 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK
= wxNewEventType();
478 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
= wxNewEventType();
480 const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
= wxNewEventType();
482 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE
= wxNewEventType();
483 const wxEventType wxEVT_COMMAND_HEADER_RESIZING
= wxNewEventType();
484 const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE
= wxNewEventType();
486 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER
= wxNewEventType();
487 const wxEventType wxEVT_COMMAND_HEADER_END_REORDER
= wxNewEventType();
489 const wxEventType wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED
= wxNewEventType();
491 #endif // wxUSE_HEADERCTRL