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 wxHeaderColumn
& wxHeaderCtrlSimple::GetColumn(unsigned int idx
) 
 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();