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
)
53 void wxHeaderCtrlBase::ScrollWindow(int dx
,
54 int WXUNUSED_UNLESS_DEBUG(dy
),
55 const wxRect
* WXUNUSED_UNLESS_DEBUG(rect
))
58 // this doesn't make sense at all
59 wxASSERT_MSG( !dy
, "header window can't be scrolled vertically" );
61 // this would actually be nice to support for "frozen" headers but it isn't
62 // supported currently
63 wxASSERT_MSG( !rect
, "header window can't be scrolled partially" );
68 void wxHeaderCtrlBase::SetColumnCount(unsigned int count
)
70 if ( count
== GetColumnCount() )
73 OnColumnCountChanging(count
);
78 // ----------------------------------------------------------------------------
79 // wxHeaderCtrlBase event handling
80 // ----------------------------------------------------------------------------
82 void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent
& event
)
84 const unsigned col
= event
.GetColumn();
86 int w
= wxWindowBase::GetTextExtent(GetColumn(col
).GetTitle()).x
;
87 w
+= 4*GetCharWidth(); // add some arbitrary margins around text
89 if ( !UpdateColumnWidthToFit(col
, w
) )
95 // ----------------------------------------------------------------------------
96 // wxHeaderCtrlBase column reordering
97 // ----------------------------------------------------------------------------
99 void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt
& order
)
101 const unsigned count
= GetColumnCount();
102 wxCHECK_RET( order
.size() == count
, "wrong number of columns" );
104 // check the array validity
105 wxArrayInt
seen(count
, 0);
106 for ( unsigned n
= 0; n
< count
; n
++ )
108 const unsigned idx
= order
[n
];
109 wxCHECK_RET( idx
< count
, "invalid column index" );
110 wxCHECK_RET( !seen
[idx
], "duplicate column index" );
115 DoSetColumnsOrder(order
);
117 // TODO-RTL: do we need to reverse the array?
120 void wxHeaderCtrlBase::ResetColumnsOrder()
122 const unsigned count
= GetColumnCount();
123 wxArrayInt
order(count
);
124 for ( unsigned n
= 0; n
< count
; n
++ )
127 DoSetColumnsOrder(order
);
130 wxArrayInt
wxHeaderCtrlBase::GetColumnsOrder() const
132 const wxArrayInt order
= DoGetColumnsOrder();
134 wxASSERT_MSG( order
.size() == GetColumnCount(), "invalid order array" );
139 unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos
) const
141 wxCHECK_MSG( pos
< GetColumnCount(), wxNO_COLUMN
, "invalid position" );
143 return GetColumnsOrder()[pos
];
146 unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx
) const
148 const unsigned count
= GetColumnCount();
150 wxCHECK_MSG( idx
< count
, wxNO_COLUMN
, "invalid index" );
152 const wxArrayInt order
= GetColumnsOrder();
153 for ( unsigned n
= 0; n
< count
; n
++ )
155 if ( (unsigned)order
[n
] == idx
)
159 wxFAIL_MSG( "column unexpectedly not displayed at all" );
165 void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt
& order
,
169 const unsigned count
= order
.size();
172 orderNew
.reserve(count
);
173 for ( unsigned n
= 0; ; n
++ )
175 // NB: order of checks is important for this to work when the new
176 // column position is the same as the old one
178 // insert the column at its new position
179 if ( orderNew
.size() == pos
)
180 orderNew
.push_back(idx
);
185 // delete the column from its old position
186 const unsigned idxOld
= order
[n
];
190 orderNew
.push_back(idxOld
);
193 order
.swap(orderNew
);
197 wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt
& colIndices
, unsigned int count
)
199 // update the column indices array if necessary
200 const unsigned countOld
= colIndices
.size();
201 if ( count
> countOld
)
203 // all new columns have default positions equal to their indices
204 for ( unsigned n
= countOld
; n
< count
; n
++ )
205 colIndices
.push_back(n
);
207 else if ( count
< countOld
)
209 // filter out all the positions which are invalid now while keeping the
210 // order of the remaining ones
211 wxArrayInt colIndicesNew
;
212 colIndicesNew
.reserve(count
);
213 for ( unsigned n
= 0; n
< countOld
; n
++ )
215 const unsigned idx
= colIndices
[n
];
217 colIndicesNew
.push_back(idx
);
220 colIndices
.swap(colIndicesNew
);
222 else // count didn't really change, we shouldn't even be called
224 wxFAIL_MSG( "useless call to DoResizeColumnIndices()" );
227 wxASSERT_MSG( colIndices
.size() == count
, "logic error" );
230 // ----------------------------------------------------------------------------
231 // wxHeaderCtrl extra UI
232 // ----------------------------------------------------------------------------
234 int wxHeaderCtrlBase::ShowColumnsMenu(const wxString
& title
)
237 if ( !title
.empty() )
238 menu
.SetTitle(title
);
240 const unsigned count
= GetColumnCount();
241 for ( unsigned n
= 0; n
< count
; n
++ )
243 const wxHeaderColumn
& col
= GetColumn(n
);
244 menu
.AppendCheckItem(n
, col
.GetTitle());
249 return GetPopupMenuSelectionFromUser(menu
,
250 ScreenToClient(wxGetMousePosition()));
253 // ============================================================================
254 // wxHeaderCtrlSimple implementation
255 // ============================================================================
257 void wxHeaderCtrlSimple::Init()
259 m_sortKey
= wxNO_COLUMN
;
262 wxHeaderColumn
& wxHeaderCtrlSimple::GetColumn(unsigned int idx
)
267 void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple
& col
, unsigned int idx
)
269 m_cols
.insert(m_cols
.begin() + idx
, col
);
274 void wxHeaderCtrlSimple::DoDelete(unsigned int idx
)
276 m_cols
.erase(m_cols
.begin() + idx
);
277 if ( idx
== m_sortKey
)
278 m_sortKey
= wxNO_COLUMN
;
283 void wxHeaderCtrlSimple::DeleteAllColumns()
286 m_sortKey
= wxNO_COLUMN
;
292 void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx
, bool show
)
294 if ( show
!= m_cols
[idx
].IsShown() )
296 m_cols
[idx
].SetHidden(!show
);
302 void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx
, bool ascending
)
304 RemoveSortIndicator();
306 m_cols
[idx
].SetAsSortKey(ascending
);
312 void wxHeaderCtrlSimple::RemoveSortIndicator()
314 if ( m_sortKey
!= wxNO_COLUMN
)
316 const unsigned sortOld
= m_sortKey
;
317 m_sortKey
= wxNO_COLUMN
;
319 m_cols
[sortOld
].UnsetAsSortKey();
321 UpdateColumn(sortOld
);
326 wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx
, int widthTitle
)
328 const int widthContents
= GetBestFittingWidth(idx
);
329 if ( widthContents
== -1 )
332 m_cols
[idx
].SetWidth(wxMax(widthContents
, widthTitle
));
337 // ============================================================================
338 // wxHeaderCtrlEvent implementation
339 // ============================================================================
341 IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent
, wxNotifyEvent
)
343 const wxEventType wxEVT_COMMAND_HEADER_CLICK
= wxNewEventType();
344 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_CLICK
= wxNewEventType();
345 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_CLICK
= wxNewEventType();
347 const wxEventType wxEVT_COMMAND_HEADER_DCLICK
= wxNewEventType();
348 const wxEventType wxEVT_COMMAND_HEADER_RIGHT_DCLICK
= wxNewEventType();
349 const wxEventType wxEVT_COMMAND_HEADER_MIDDLE_DCLICK
= wxNewEventType();
351 const wxEventType wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK
= wxNewEventType();
353 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_RESIZE
= wxNewEventType();
354 const wxEventType wxEVT_COMMAND_HEADER_RESIZING
= wxNewEventType();
355 const wxEventType wxEVT_COMMAND_HEADER_END_RESIZE
= wxNewEventType();
357 const wxEventType wxEVT_COMMAND_HEADER_BEGIN_REORDER
= wxNewEventType();
358 const wxEventType wxEVT_COMMAND_HEADER_END_REORDER
= wxNewEventType();
360 const wxEventType wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED
= wxNewEventType();