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"
30 #include "wx/headerctrl.h"
31 #include "wx/rearrangectrl.h"
36 // ----------------------------------------------------------------------------
38 // ----------------------------------------------------------------------------
40 const unsigned int wxNO_COLUMN
= static_cast<unsigned>(-1);
42 // ----------------------------------------------------------------------------
43 // wxHeaderColumnsRearrangeDialog: dialog for customizing our columns
44 // ----------------------------------------------------------------------------
46 class wxHeaderColumnsRearrangeDialog
: public wxRearrangeDialog
49 wxHeaderColumnsRearrangeDialog(wxWindow
*parent
,
50 const wxArrayInt
& order
,
51 const wxArrayString
& items
)
55 _("Please select the columns to show and define their order:"),
56 _("Customize Columns"),
64 } // anonymous namespace
66 // ============================================================================
67 // wxHeaderCtrlBase implementation
68 // ============================================================================
70 extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr
[] = "wxHeaderCtrl";
72 BEGIN_EVENT_TABLE(wxHeaderCtrlBase
, wxControl
)
73 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY
, wxHeaderCtrlBase::OnSeparatorDClick
)
74 EVT_HEADER_RIGHT_CLICK(wxID_ANY
, wxHeaderCtrlBase::OnRClick
)
77 void wxHeaderCtrlBase::ScrollWindow(int dx
,
78 int WXUNUSED_UNLESS_DEBUG(dy
),
79 const wxRect
* WXUNUSED_UNLESS_DEBUG(rect
))
82 // this doesn't make sense at all
83 wxASSERT_MSG( !dy
, "header window can't be scrolled vertically" );
85 // this would actually be nice to support for "frozen" headers but it isn't
86 // supported currently
87 wxASSERT_MSG( !rect
, "header window can't be scrolled partially" );
92 void wxHeaderCtrlBase::SetColumnCount(unsigned int count
)
94 if ( count
!= GetColumnCount() )
95 OnColumnCountChanging(count
);
97 // still call DoSetCount() even if the count didn't really change in order
98 // to update all the columns
102 // ----------------------------------------------------------------------------
103 // wxHeaderCtrlBase event handling
104 // ----------------------------------------------------------------------------
106 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent
& event
)
108 const unsigned col
= event
.GetColumn();
110 int w
= wxWindowBase::GetTextExtent(GetColumn(col
).GetTitle()).x
;
111 w
+= 4*GetCharWidth(); // add some arbitrary margins around text
113 if ( !UpdateColumnWidthToFit(col
, w
) )
119 void wxHeaderCtrlBase::OnRClick(wxHeaderCtrlEvent
& event
)
121 if ( !HasFlag(wxHD_ALLOW_HIDE
) )
127 ShowColumnsMenu(ScreenToClient(wxGetMousePosition()));
130 // ----------------------------------------------------------------------------
131 // wxHeaderCtrlBase column reordering
132 // ----------------------------------------------------------------------------
134 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt
& order
)
136 const unsigned count
= GetColumnCount();
137 wxCHECK_RET( order
.size() == count
, "wrong number of columns" );
139 // check the array validity
140 wxArrayInt
seen(count
, 0);
141 for ( unsigned n
= 0; n
< count
; n
++ )
143 const unsigned idx
= order
[n
];
144 wxCHECK_RET( idx
< count
, "invalid column index" );
145 wxCHECK_RET( !seen
[idx
], "duplicate column index" );
150 DoSetColumnsOrder(order
);
152 // TODO-RTL: do we need to reverse the array?
155 void wxHeaderCtrlBase::ResetColumnsOrder()
157 const unsigned count
= GetColumnCount();
158 wxArrayInt
order(count
);
159 for ( unsigned n
= 0; n
< count
; n
++ )
162 DoSetColumnsOrder(order
);
165 wxArrayInt
wxHeaderCtrlBase::GetColumnsOrder() const
167 const wxArrayInt order
= DoGetColumnsOrder();
169 wxASSERT_MSG( order
.size() == GetColumnCount(), "invalid order array" );
174 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos
) const
176 wxCHECK_MSG( pos
< GetColumnCount(), wxNO_COLUMN
, "invalid position" );
178 return GetColumnsOrder()[pos
];
181 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx
) const
183 const unsigned count
= GetColumnCount();
185 wxCHECK_MSG( idx
< count
, wxNO_COLUMN
, "invalid index" );
187 const wxArrayInt order
= GetColumnsOrder();
188 for ( unsigned n
= 0; n
< count
; n
++ )
190 if ( (unsigned)order
[n
] == idx
)
194 wxFAIL_MSG( "column unexpectedly not displayed at all" );
200 void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt
& order
,
204 const unsigned count
= order
.size();
207 orderNew
.reserve(count
);
208 for ( unsigned n
= 0; ; n
++ )
210 // NB: order of checks is important for this to work when the new
211 // column position is the same as the old one
213 // insert the column at its new position
214 if ( orderNew
.size() == pos
)
215 orderNew
.push_back(idx
);
220 // delete the column from its old position
221 const unsigned idxOld
= order
[n
];
225 orderNew
.push_back(idxOld
);
228 order
.swap(orderNew
);
232 wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt
& colIndices
, unsigned int count
)
234 // update the column indices array if necessary
235 const unsigned countOld
= colIndices
.size();
236 if ( count
> countOld
)
238 // all new columns have default positions equal to their indices
239 for ( unsigned n
= countOld
; n
< count
; n
++ )
240 colIndices
.push_back(n
);
242 else if ( count
< countOld
)
244 // filter out all the positions which are invalid now while keeping the
245 // order of the remaining ones
246 wxArrayInt colIndicesNew
;
247 colIndicesNew
.reserve(count
);
248 for ( unsigned n
= 0; n
< countOld
; n
++ )
250 const unsigned idx
= colIndices
[n
];
252 colIndicesNew
.push_back(idx
);
255 colIndices
.swap(colIndicesNew
);
257 //else: count didn't really change, nothing to do
259 wxASSERT_MSG( colIndices
.size() == count
, "logic error" );
262 // ----------------------------------------------------------------------------
263 // wxHeaderCtrl extra UI
264 // ----------------------------------------------------------------------------
266 bool wxHeaderCtrlBase::ShowColumnsMenu(const wxPoint
& pt
, const wxString
& title
)
268 // construct the menu with the entries for all columns
270 if ( !title
.empty() )
271 menu
.SetTitle(title
);
273 const unsigned count
= GetColumnCount();
274 for ( unsigned n
= 0; n
< count
; n
++ )
276 const wxHeaderColumn
& col
= GetColumn(n
);
277 menu
.AppendCheckItem(n
, col
.GetTitle());
282 // ... and an extra one to show the customization dialog if the user is
283 // allowed to reorder the columns too
284 if ( HasFlag(wxHD_ALLOW_REORDER
) )
286 menu
.AppendSeparator();
287 menu
.Append(count
, _("&Customize..."));
290 // do show the menu and get the user selection
291 const int rc
= GetPopupMenuSelectionFromUser(menu
, pt
);
292 if ( rc
== wxID_NONE
)
295 if ( static_cast<unsigned>(rc
) == count
)
297 return ShowCustomizeDialog();
299 else // a column selected from the menu
301 UpdateColumnVisibility(rc
, !GetColumn(rc
).IsShown());
307 bool wxHeaderCtrlBase::ShowCustomizeDialog()
309 // prepare the data for showing the dialog
310 wxArrayInt order
= GetColumnsOrder();
312 const unsigned count
= GetColumnCount();
314 // notice that titles are always in the index order, they will be shown
315 // rearranged according to the display order in the dialog
316 wxArrayString titles
;
317 titles
.reserve(count
);
318 for ( unsigned n
= 0; n
< count
; n
++ )
319 titles
.push_back(GetColumn(n
).GetTitle());
321 // this loop is however over positions and not indices
323 for ( pos
= 0; pos
< count
; pos
++ )
325 int& idx
= order
[pos
];
326 if ( GetColumn(idx
).IsHidden() )
328 // indicate that this one is hidden
334 wxHeaderColumnsRearrangeDialog
dlg(this, order
, titles
);
335 if ( dlg
.ShowModal() != wxID_OK
)
338 // and apply the changes
339 order
= dlg
.GetOrder();
340 for ( pos
= 0; pos
< count
; pos
++ )
342 int& idx
= order
[pos
];
343 const bool show
= idx
>= 0;
346 // make all indices positive for passing them to SetColumnsOrder()
350 if ( show
!= GetColumn(idx
).IsShown() )
351 UpdateColumnVisibility(idx
, show
);
354 UpdateColumnsOrder(order
);
355 SetColumnsOrder(order
);
360 // ============================================================================
361 // wxHeaderCtrlSimple implementation
362 // ============================================================================
364 void wxHeaderCtrlSimple::Init()
366 m_sortKey
= wxNO_COLUMN
;
369 const wxHeaderColumn
& wxHeaderCtrlSimple::GetColumn(unsigned int idx
) const
374 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple
& col
, unsigned int idx
)
376 m_cols
.insert(m_cols
.begin() + idx
, col
);
381 void wxHeaderCtrlSimple::DoDelete(unsigned int idx
)
383 m_cols
.erase(m_cols
.begin() + idx
);
384 if ( idx
== m_sortKey
)
385 m_sortKey
= wxNO_COLUMN
;
390 void wxHeaderCtrlSimple::DeleteAllColumns()
393 m_sortKey
= wxNO_COLUMN
;
399 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx
, bool show
)
401 if ( show
!= m_cols
[idx
].IsShown() )
403 m_cols
[idx
].SetHidden(!show
);
409 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx
, bool ascending
)
411 RemoveSortIndicator();
413 m_cols
[idx
].SetAsSortKey(ascending
);
419 void wxHeaderCtrlSimple::RemoveSortIndicator()
421 if ( m_sortKey
!= wxNO_COLUMN
)
423 const unsigned sortOld
= m_sortKey
;
424 m_sortKey
= wxNO_COLUMN
;
426 m_cols
[sortOld
].UnsetAsSortKey();
428 UpdateColumn(sortOld
);
433 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx
, int widthTitle
)
435 const int widthContents
= GetBestFittingWidth(idx
);
436 if ( widthContents
== -1 )
439 m_cols
[idx
].SetWidth(wxMax(widthContents
, widthTitle
));
444 // ============================================================================
445 // wxHeaderCtrlEvent implementation
446 // ============================================================================
448 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent
, wxNotifyEvent
)
450 const wxEventType wxEVT_COMMAND_HEADER_CLICK
= wxNewEventType();
451 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK
= wxNewEventType();
452 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK
= wxNewEventType();
454 const wxEventType wxEVT_COMMAND_HEADER_DCLICK
= wxNewEventType();
455 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK
= wxNewEventType();
456 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
= wxNewEventType();
458 const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
= wxNewEventType();
460 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE
= wxNewEventType();
461 const wxEventType wxEVT_COMMAND_HEADER_RESIZING
= wxNewEventType();
462 const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE
= wxNewEventType();
464 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER
= wxNewEventType();
465 const wxEventType wxEVT_COMMAND_HEADER_END_REORDER
= wxNewEventType();
467 const wxEventType wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED
= wxNewEventType();