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"
32 // ----------------------------------------------------------------------------
34 // ----------------------------------------------------------------------------
39 const unsigned int wxNO_COLUMN
= static_cast<unsigned>(-1);
41 } // anonymous namespace
43 // ============================================================================
44 // wxHeaderCtrlBase implementation
45 // ============================================================================
47 extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr
[] = "wxHeaderCtrl";
49 BEGIN_EVENT_TABLE(wxHeaderCtrlBase
, wxControl
)
50 EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY
, wxHeaderCtrlBase::OnSeparatorDClick
)
51 EVT_HEADER_RIGHT_CLICK(wxID_ANY
, wxHeaderCtrlBase::OnRClick
)
54 void wxHeaderCtrlBase::ScrollWindow(int dx
,
55 int WXUNUSED_UNLESS_DEBUG(dy
),
56 const wxRect
* WXUNUSED_UNLESS_DEBUG(rect
))
59 // this doesn't make sense at all
60 wxASSERT_MSG( !dy
, "header window can't be scrolled vertically" );
62 // this would actually be nice to support for "frozen" headers but it isn't
63 // supported currently
64 wxASSERT_MSG( !rect
, "header window can't be scrolled partially" );
69 void wxHeaderCtrlBase::SetColumnCount(unsigned int count
)
71 if ( count
!= GetColumnCount() )
72 OnColumnCountChanging(count
);
74 // still call DoSetCount() even if the count didn't really change in order
75 // to update all the columns
79 // ----------------------------------------------------------------------------
80 // wxHeaderCtrlBase event handling
81 // ----------------------------------------------------------------------------
83 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent
& event
)
85 const unsigned col
= event
.GetColumn();
87 int w
= wxWindowBase::GetTextExtent(GetColumn(col
).GetTitle()).x
;
88 w
+= 4*GetCharWidth(); // add some arbitrary margins around text
90 if ( !UpdateColumnWidthToFit(col
, w
) )
96 void wxHeaderCtrlBase::OnRClick(wxHeaderCtrlEvent
& event
)
98 if ( !HasFlag(wxHD_ALLOW_HIDE
) )
104 ShowColumnsMenu(ScreenToClient(wxGetMousePosition()));
107 // ----------------------------------------------------------------------------
108 // wxHeaderCtrlBase column reordering
109 // ----------------------------------------------------------------------------
111 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt
& order
)
113 const unsigned count
= GetColumnCount();
114 wxCHECK_RET( order
.size() == count
, "wrong number of columns" );
116 // check the array validity
117 wxArrayInt
seen(count
, 0);
118 for ( unsigned n
= 0; n
< count
; n
++ )
120 const unsigned idx
= order
[n
];
121 wxCHECK_RET( idx
< count
, "invalid column index" );
122 wxCHECK_RET( !seen
[idx
], "duplicate column index" );
127 DoSetColumnsOrder(order
);
129 // TODO-RTL: do we need to reverse the array?
132 void wxHeaderCtrlBase::ResetColumnsOrder()
134 const unsigned count
= GetColumnCount();
135 wxArrayInt
order(count
);
136 for ( unsigned n
= 0; n
< count
; n
++ )
139 DoSetColumnsOrder(order
);
142 wxArrayInt
wxHeaderCtrlBase::GetColumnsOrder() const
144 const wxArrayInt order
= DoGetColumnsOrder();
146 wxASSERT_MSG( order
.size() == GetColumnCount(), "invalid order array" );
151 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos
) const
153 wxCHECK_MSG( pos
< GetColumnCount(), wxNO_COLUMN
, "invalid position" );
155 return GetColumnsOrder()[pos
];
158 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx
) const
160 const unsigned count
= GetColumnCount();
162 wxCHECK_MSG( idx
< count
, wxNO_COLUMN
, "invalid index" );
164 const wxArrayInt order
= GetColumnsOrder();
165 for ( unsigned n
= 0; n
< count
; n
++ )
167 if ( (unsigned)order
[n
] == idx
)
171 wxFAIL_MSG( "column unexpectedly not displayed at all" );
177 void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt
& order
,
181 const unsigned count
= order
.size();
184 orderNew
.reserve(count
);
185 for ( unsigned n
= 0; ; n
++ )
187 // NB: order of checks is important for this to work when the new
188 // column position is the same as the old one
190 // insert the column at its new position
191 if ( orderNew
.size() == pos
)
192 orderNew
.push_back(idx
);
197 // delete the column from its old position
198 const unsigned idxOld
= order
[n
];
202 orderNew
.push_back(idxOld
);
205 order
.swap(orderNew
);
209 wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt
& colIndices
, unsigned int count
)
211 // update the column indices array if necessary
212 const unsigned countOld
= colIndices
.size();
213 if ( count
> countOld
)
215 // all new columns have default positions equal to their indices
216 for ( unsigned n
= countOld
; n
< count
; n
++ )
217 colIndices
.push_back(n
);
219 else if ( count
< countOld
)
221 // filter out all the positions which are invalid now while keeping the
222 // order of the remaining ones
223 wxArrayInt colIndicesNew
;
224 colIndicesNew
.reserve(count
);
225 for ( unsigned n
= 0; n
< countOld
; n
++ )
227 const unsigned idx
= colIndices
[n
];
229 colIndicesNew
.push_back(idx
);
232 colIndices
.swap(colIndicesNew
);
234 //else: count didn't really change, nothing to do
236 wxASSERT_MSG( colIndices
.size() == count
, "logic error" );
239 // ----------------------------------------------------------------------------
240 // wxHeaderCtrl extra UI
241 // ----------------------------------------------------------------------------
243 bool wxHeaderCtrlBase::ShowColumnsMenu(const wxPoint
& pt
, const wxString
& title
)
245 // construct the menu with the entries for all columns
247 if ( !title
.empty() )
248 menu
.SetTitle(title
);
250 const unsigned count
= GetColumnCount();
251 for ( unsigned n
= 0; n
< count
; n
++ )
253 const wxHeaderColumn
& col
= GetColumn(n
);
254 menu
.AppendCheckItem(n
, col
.GetTitle());
259 // ... and an extra one to show the customization dialog if the user is
260 // allowed to reorder the columns too
261 if ( HasFlag(wxHD_ALLOW_REORDER
) )
263 menu
.AppendSeparator();
264 menu
.Append(count
, _("&Customize..."));
267 // do show the menu and get the user selection
268 const int rc
= GetPopupMenuSelectionFromUser(menu
, pt
);
269 if ( rc
== wxID_NONE
)
272 if ( static_cast<unsigned>(rc
) == count
)
274 return ShowCustomizeDialog();
276 else // a column selected from the menu
278 UpdateColumnVisibility(rc
, !GetColumn(rc
).IsShown());
284 bool wxHeaderCtrlBase::ShowCustomizeDialog()
290 // ============================================================================
291 // wxHeaderCtrlSimple implementation
292 // ============================================================================
294 void wxHeaderCtrlSimple::Init()
296 m_sortKey
= wxNO_COLUMN
;
299 wxHeaderColumn
& wxHeaderCtrlSimple::GetColumn(unsigned int idx
)
304 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple
& col
, unsigned int idx
)
306 m_cols
.insert(m_cols
.begin() + idx
, col
);
311 void wxHeaderCtrlSimple::DoDelete(unsigned int idx
)
313 m_cols
.erase(m_cols
.begin() + idx
);
314 if ( idx
== m_sortKey
)
315 m_sortKey
= wxNO_COLUMN
;
320 void wxHeaderCtrlSimple::DeleteAllColumns()
323 m_sortKey
= wxNO_COLUMN
;
329 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx
, bool show
)
331 if ( show
!= m_cols
[idx
].IsShown() )
333 m_cols
[idx
].SetHidden(!show
);
339 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx
, bool ascending
)
341 RemoveSortIndicator();
343 m_cols
[idx
].SetAsSortKey(ascending
);
349 void wxHeaderCtrlSimple::RemoveSortIndicator()
351 if ( m_sortKey
!= wxNO_COLUMN
)
353 const unsigned sortOld
= m_sortKey
;
354 m_sortKey
= wxNO_COLUMN
;
356 m_cols
[sortOld
].UnsetAsSortKey();
358 UpdateColumn(sortOld
);
363 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx
, int widthTitle
)
365 const int widthContents
= GetBestFittingWidth(idx
);
366 if ( widthContents
== -1 )
369 m_cols
[idx
].SetWidth(wxMax(widthContents
, widthTitle
));
374 // ============================================================================
375 // wxHeaderCtrlEvent implementation
376 // ============================================================================
378 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent
, wxNotifyEvent
)
380 const wxEventType wxEVT_COMMAND_HEADER_CLICK
= wxNewEventType();
381 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK
= wxNewEventType();
382 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK
= wxNewEventType();
384 const wxEventType wxEVT_COMMAND_HEADER_DCLICK
= wxNewEventType();
385 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK
= wxNewEventType();
386 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
= wxNewEventType();
388 const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
= wxNewEventType();
390 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE
= wxNewEventType();
391 const wxEventType wxEVT_COMMAND_HEADER_RESIZING
= wxNewEventType();
392 const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE
= wxNewEventType();
394 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER
= wxNewEventType();
395 const wxEventType wxEVT_COMMAND_HEADER_END_REORDER
= wxNewEventType();
397 const wxEventType wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED
= wxNewEventType();