| 1 | /////////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: wx/headerctrl.h |
| 3 | // Purpose: wxHeaderCtrlBase class: interface of wxHeaderCtrl |
| 4 | // Author: Vadim Zeitlin |
| 5 | // Created: 2008-12-01 |
| 6 | // RCS-ID: $Id$ |
| 7 | // Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org> |
| 8 | // Licence: wxWindows licence |
| 9 | /////////////////////////////////////////////////////////////////////////////// |
| 10 | |
| 11 | #ifndef _WX_HEADERCTRL_H_ |
| 12 | #define _WX_HEADERCTRL_H_ |
| 13 | |
| 14 | #include "wx/control.h" |
| 15 | |
| 16 | #if wxUSE_HEADERCTRL |
| 17 | |
| 18 | #include "wx/dynarray.h" |
| 19 | #include "wx/vector.h" |
| 20 | |
| 21 | #include "wx/headercol.h" |
| 22 | |
| 23 | // notice that the classes in this header are defined in the core library even |
| 24 | // although currently they're only used by wxGrid which is in wxAdv because we |
| 25 | // plan to use it in wxListCtrl which is in core too in the future |
| 26 | class WXDLLIMPEXP_FWD_CORE wxHeaderCtrlEvent; |
| 27 | |
| 28 | // ---------------------------------------------------------------------------- |
| 29 | // constants |
| 30 | // ---------------------------------------------------------------------------- |
| 31 | |
| 32 | enum |
| 33 | { |
| 34 | // allow column drag and drop |
| 35 | wxHD_ALLOW_REORDER = 0x0001, |
| 36 | |
| 37 | // allow hiding (and showing back) the columns using the menu shown by |
| 38 | // right clicking the header |
| 39 | wxHD_ALLOW_HIDE = 0x0002, |
| 40 | |
| 41 | // style used by default when creating the control |
| 42 | wxHD_DEFAULT_STYLE = wxHD_ALLOW_REORDER |
| 43 | }; |
| 44 | |
| 45 | extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr[]; |
| 46 | |
| 47 | // ---------------------------------------------------------------------------- |
| 48 | // wxHeaderCtrlBase defines the interface of a header control |
| 49 | // ---------------------------------------------------------------------------- |
| 50 | |
| 51 | class WXDLLIMPEXP_CORE wxHeaderCtrlBase : public wxControl |
| 52 | { |
| 53 | public: |
| 54 | /* |
| 55 | Derived classes must provide default ctor as well as a ctor and |
| 56 | Create() function with the following signatures: |
| 57 | |
| 58 | wxHeaderCtrl(wxWindow *parent, |
| 59 | wxWindowID winid = wxID_ANY, |
| 60 | const wxPoint& pos = wxDefaultPosition, |
| 61 | const wxSize& size = wxDefaultSize, |
| 62 | long style = wxHD_DEFAULT_STYLE, |
| 63 | const wxString& name = wxHeaderCtrlNameStr); |
| 64 | |
| 65 | bool Create(wxWindow *parent, |
| 66 | wxWindowID winid = wxID_ANY, |
| 67 | const wxPoint& pos = wxDefaultPosition, |
| 68 | const wxSize& size = wxDefaultSize, |
| 69 | long style = wxHD_DEFAULT_STYLE, |
| 70 | const wxString& name = wxHeaderCtrlNameStr); |
| 71 | */ |
| 72 | |
| 73 | // column-related methods |
| 74 | // ---------------------- |
| 75 | |
| 76 | // set the number of columns in the control |
| 77 | // |
| 78 | // this also calls UpdateColumn() for all columns |
| 79 | void SetColumnCount(unsigned int count); |
| 80 | |
| 81 | // return the number of columns in the control as set by SetColumnCount() |
| 82 | unsigned int GetColumnCount() const { return DoGetCount(); } |
| 83 | |
| 84 | // return whether the control has any columns |
| 85 | bool IsEmpty() const { return DoGetCount() == 0; } |
| 86 | |
| 87 | // update the column with the given index |
| 88 | void UpdateColumn(unsigned int idx) |
| 89 | { |
| 90 | wxCHECK_RET( idx < GetColumnCount(), "invalid column index" ); |
| 91 | |
| 92 | DoUpdate(idx); |
| 93 | } |
| 94 | |
| 95 | |
| 96 | // columns order |
| 97 | // ------------- |
| 98 | |
| 99 | // set the columns order: the array defines the column index which appears |
| 100 | // the given position, it must have GetColumnCount() elements and contain |
| 101 | // all indices exactly once |
| 102 | void SetColumnsOrder(const wxArrayInt& order); |
| 103 | wxArrayInt GetColumnsOrder() const; |
| 104 | |
| 105 | // get the index of the column at the given display position |
| 106 | unsigned int GetColumnAt(unsigned int pos) const; |
| 107 | |
| 108 | // get the position at which this column is currently displayed |
| 109 | unsigned int GetColumnPos(unsigned int idx) const; |
| 110 | |
| 111 | // reset the columns order to the natural one |
| 112 | void ResetColumnsOrder(); |
| 113 | |
| 114 | // helper function used by the generic version of this control and also |
| 115 | // wxGrid: reshuffles the array of column indices indexed by positions |
| 116 | // (i.e. using the same convention as for SetColumnsOrder()) so that the |
| 117 | // column with the given index is found at the specified position |
| 118 | static void MoveColumnInOrderArray(wxArrayInt& order, |
| 119 | unsigned int idx, |
| 120 | unsigned int pos); |
| 121 | |
| 122 | |
| 123 | // UI helpers |
| 124 | // ---------- |
| 125 | |
| 126 | #if wxUSE_MENUS |
| 127 | // show the popup menu containing all columns with check marks for the ones |
| 128 | // which are currently shown and return true if something was done using it |
| 129 | // (in this case UpdateColumnVisibility() will have been called) or false |
| 130 | // if the menu was cancelled |
| 131 | // |
| 132 | // this is called from the default right click handler for the controls |
| 133 | // with wxHD_ALLOW_HIDE style |
| 134 | bool ShowColumnsMenu(const wxPoint& pt, const wxString& title = wxString()); |
| 135 | |
| 136 | // append the entries for all our columns to the given menu, with the |
| 137 | // currently visible columns being checked |
| 138 | // |
| 139 | // this is used by ShowColumnsMenu() but can also be used if you use your |
| 140 | // own custom columns menu but nevertheless want to show all the columns in |
| 141 | // it |
| 142 | // |
| 143 | // the ids of the items corresponding to the columns are consecutive and |
| 144 | // start from idColumnsBase |
| 145 | void AddColumnsItems(wxMenu& menu, int idColumnsBase = 0); |
| 146 | #endif // wxUSE_MENUS |
| 147 | |
| 148 | // show the columns customization dialog and return true if something was |
| 149 | // changed using it (in which case UpdateColumnVisibility() and/or |
| 150 | // UpdateColumnsOrder() will have been called) |
| 151 | // |
| 152 | // this is called by the control itself from ShowColumnsMenu() (which in |
| 153 | // turn is only called by the control if wxHD_ALLOW_HIDE style was |
| 154 | // specified) and if the control has wxHD_ALLOW_REORDER style as well |
| 155 | bool ShowCustomizeDialog(); |
| 156 | |
| 157 | |
| 158 | // implementation only from now on |
| 159 | // ------------------------------- |
| 160 | |
| 161 | // the user doesn't need to TAB to this control |
| 162 | virtual bool AcceptsFocusFromKeyboard() const { return false; } |
| 163 | |
| 164 | // this method is only overridden in order to synchronize the control with |
| 165 | // the main window when it is scrolled, the derived class must implement |
| 166 | // DoScrollHorz() |
| 167 | virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL); |
| 168 | |
| 169 | protected: |
| 170 | // this method must be implemented by the derived classes to return the |
| 171 | // information for the given column |
| 172 | virtual const wxHeaderColumn& GetColumn(unsigned int idx) const = 0; |
| 173 | |
| 174 | // this method is called from the default EVT_HEADER_SEPARATOR_DCLICK |
| 175 | // handler to update the fitting column width of the given column, it |
| 176 | // should return true if the width was really updated |
| 177 | virtual bool UpdateColumnWidthToFit(unsigned int WXUNUSED(idx), |
| 178 | int WXUNUSED(widthTitle)) |
| 179 | { |
| 180 | return false; |
| 181 | } |
| 182 | |
| 183 | // this method is called from ShowColumnsMenu() and must be overridden to |
| 184 | // update the internal column visibility (there is no need to call |
| 185 | // UpdateColumn() from here, this will be done internally) |
| 186 | virtual void UpdateColumnVisibility(unsigned int WXUNUSED(idx), |
| 187 | bool WXUNUSED(show)) |
| 188 | { |
| 189 | wxFAIL_MSG( "must be overridden if called" ); |
| 190 | } |
| 191 | |
| 192 | // this method is called from ShowCustomizeDialog() to reorder all columns |
| 193 | // at once and should be implemented for controls using wxHD_ALLOW_REORDER |
| 194 | // style (there is no need to call SetColumnsOrder() from here, this is |
| 195 | // done by the control itself) |
| 196 | virtual void UpdateColumnsOrder(const wxArrayInt& WXUNUSED(order)) |
| 197 | { |
| 198 | wxFAIL_MSG( "must be overridden if called" ); |
| 199 | } |
| 200 | |
| 201 | // this method can be overridden in the derived classes to do something |
| 202 | // (e.g. update/resize some internal data structures) before the number of |
| 203 | // columns in the control changes |
| 204 | virtual void OnColumnCountChanging(unsigned int WXUNUSED(count)) { } |
| 205 | |
| 206 | |
| 207 | // helper function for the derived classes: update the array of column |
| 208 | // indices after the number of columns changed |
| 209 | void DoResizeColumnIndices(wxArrayInt& colIndices, unsigned int count); |
| 210 | |
| 211 | private: |
| 212 | // methods implementing our public API and defined in platform-specific |
| 213 | // implementations |
| 214 | virtual void DoSetCount(unsigned int count) = 0; |
| 215 | virtual unsigned int DoGetCount() const = 0; |
| 216 | virtual void DoUpdate(unsigned int idx) = 0; |
| 217 | |
| 218 | virtual void DoScrollHorz(int dx) = 0; |
| 219 | |
| 220 | virtual void DoSetColumnsOrder(const wxArrayInt& order) = 0; |
| 221 | virtual wxArrayInt DoGetColumnsOrder() const = 0; |
| 222 | |
| 223 | // this window doesn't look nice with the border so don't use it by default |
| 224 | virtual wxBorder GetDefaultBorder() const { return wxBORDER_NONE; } |
| 225 | |
| 226 | // event handlers |
| 227 | void OnSeparatorDClick(wxHeaderCtrlEvent& event); |
| 228 | #if wxUSE_MENUS |
| 229 | void OnRClick(wxHeaderCtrlEvent& event); |
| 230 | #endif // wxUSE_MENUS |
| 231 | |
| 232 | DECLARE_EVENT_TABLE() |
| 233 | }; |
| 234 | |
| 235 | // ---------------------------------------------------------------------------- |
| 236 | // wxHeaderCtrl: port-specific header control implementation, notice that this |
| 237 | // is still an ABC which is meant to be used as part of another |
| 238 | // control, see wxHeaderCtrlSimple for a standalone version |
| 239 | // ---------------------------------------------------------------------------- |
| 240 | |
| 241 | #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) |
| 242 | #include "wx/msw/headerctrl.h" |
| 243 | #else |
| 244 | #define wxHAS_GENERIC_HEADERCTRL |
| 245 | #include "wx/generic/headerctrlg.h" |
| 246 | #endif // platform |
| 247 | |
| 248 | // ---------------------------------------------------------------------------- |
| 249 | // wxHeaderCtrlSimple: concrete header control which can be used standalone |
| 250 | // ---------------------------------------------------------------------------- |
| 251 | |
| 252 | class WXDLLIMPEXP_CORE wxHeaderCtrlSimple : public wxHeaderCtrl |
| 253 | { |
| 254 | public: |
| 255 | // control creation |
| 256 | // ---------------- |
| 257 | |
| 258 | wxHeaderCtrlSimple() { Init(); } |
| 259 | wxHeaderCtrlSimple(wxWindow *parent, |
| 260 | wxWindowID winid = wxID_ANY, |
| 261 | const wxPoint& pos = wxDefaultPosition, |
| 262 | const wxSize& size = wxDefaultSize, |
| 263 | long style = wxHD_DEFAULT_STYLE, |
| 264 | const wxString& name = wxHeaderCtrlNameStr) |
| 265 | { |
| 266 | Init(); |
| 267 | |
| 268 | Create(parent, winid, pos, size, style, name); |
| 269 | } |
| 270 | |
| 271 | // managing the columns |
| 272 | // -------------------- |
| 273 | |
| 274 | // insert the column at the given position, using GetColumnCount() as |
| 275 | // position appends it at the end |
| 276 | void InsertColumn(const wxHeaderColumnSimple& col, unsigned int idx) |
| 277 | { |
| 278 | wxCHECK_RET( idx <= GetColumnCount(), "invalid column index" ); |
| 279 | |
| 280 | DoInsert(col, idx); |
| 281 | } |
| 282 | |
| 283 | // append the column to the end of the control |
| 284 | void AppendColumn(const wxHeaderColumnSimple& col) |
| 285 | { |
| 286 | DoInsert(col, GetColumnCount()); |
| 287 | } |
| 288 | |
| 289 | // delete the column at the given index |
| 290 | void DeleteColumn(unsigned int idx) |
| 291 | { |
| 292 | wxCHECK_RET( idx < GetColumnCount(), "invalid column index" ); |
| 293 | |
| 294 | DoDelete(idx); |
| 295 | } |
| 296 | |
| 297 | // delete all the existing columns |
| 298 | void DeleteAllColumns(); |
| 299 | |
| 300 | |
| 301 | // modifying columns |
| 302 | // ----------------- |
| 303 | |
| 304 | // show or hide the column, notice that even when a column is hidden we |
| 305 | // still account for it when using indices |
| 306 | void ShowColumn(unsigned int idx, bool show = true) |
| 307 | { |
| 308 | wxCHECK_RET( idx < GetColumnCount(), "invalid column index" ); |
| 309 | |
| 310 | DoShowColumn(idx, show); |
| 311 | } |
| 312 | |
| 313 | void HideColumn(unsigned int idx) |
| 314 | { |
| 315 | ShowColumn(idx, false); |
| 316 | } |
| 317 | |
| 318 | // indicate that the column is used for sorting |
| 319 | void ShowSortIndicator(unsigned int idx, bool ascending = true) |
| 320 | { |
| 321 | wxCHECK_RET( idx < GetColumnCount(), "invalid column index" ); |
| 322 | |
| 323 | DoShowSortIndicator(idx, ascending); |
| 324 | } |
| 325 | |
| 326 | // remove the sort indicator completely |
| 327 | void RemoveSortIndicator(); |
| 328 | |
| 329 | protected: |
| 330 | // implement/override base class methods |
| 331 | virtual const wxHeaderColumn& GetColumn(unsigned int idx) const; |
| 332 | virtual bool UpdateColumnWidthToFit(unsigned int idx, int widthTitle); |
| 333 | |
| 334 | // and define another one to be overridden in the derived classes: it |
| 335 | // should return the best width for the given column contents or -1 if not |
| 336 | // implemented, we use it to implement UpdateColumnWidthToFit() |
| 337 | virtual int GetBestFittingWidth(unsigned int WXUNUSED(idx)) const |
| 338 | { |
| 339 | return -1; |
| 340 | } |
| 341 | |
| 342 | private: |
| 343 | // functions implementing our public API |
| 344 | void DoInsert(const wxHeaderColumnSimple& col, unsigned int idx); |
| 345 | void DoDelete(unsigned int idx); |
| 346 | void DoShowColumn(unsigned int idx, bool show); |
| 347 | void DoShowSortIndicator(unsigned int idx, bool ascending); |
| 348 | |
| 349 | // common part of all ctors |
| 350 | void Init(); |
| 351 | |
| 352 | // bring the column count in sync with the number of columns we store |
| 353 | void UpdateColumnCount() { SetColumnCount(m_cols.size()); } |
| 354 | |
| 355 | |
| 356 | // all our current columns |
| 357 | typedef wxVector<wxHeaderColumnSimple> Columns; |
| 358 | Columns m_cols; |
| 359 | |
| 360 | // the column currently used for sorting or -1 if none |
| 361 | unsigned int m_sortKey; |
| 362 | |
| 363 | |
| 364 | wxDECLARE_NO_COPY_CLASS(wxHeaderCtrlSimple); |
| 365 | }; |
| 366 | |
| 367 | // ---------------------------------------------------------------------------- |
| 368 | // wxHeaderCtrl events |
| 369 | // ---------------------------------------------------------------------------- |
| 370 | |
| 371 | class WXDLLIMPEXP_CORE wxHeaderCtrlEvent : public wxNotifyEvent |
| 372 | { |
| 373 | public: |
| 374 | wxHeaderCtrlEvent(wxEventType commandType = wxEVT_NULL, int winid = 0) |
| 375 | : wxNotifyEvent(commandType, winid), |
| 376 | m_col(-1), |
| 377 | m_width(0), |
| 378 | m_order(static_cast<unsigned int>(-1)) |
| 379 | { |
| 380 | } |
| 381 | |
| 382 | wxHeaderCtrlEvent(const wxHeaderCtrlEvent& event) |
| 383 | : wxNotifyEvent(event), |
| 384 | m_col(event.m_col), |
| 385 | m_width(event.m_width), |
| 386 | m_order(event.m_order) |
| 387 | { |
| 388 | } |
| 389 | |
| 390 | // the column which this event pertains to: valid for all header events |
| 391 | int GetColumn() const { return m_col; } |
| 392 | void SetColumn(int col) { m_col = col; } |
| 393 | |
| 394 | // the width of the column: valid for column resizing/dragging events only |
| 395 | int GetWidth() const { return m_width; } |
| 396 | void SetWidth(int width) { m_width = width; } |
| 397 | |
| 398 | // the new position of the column: for end reorder events only |
| 399 | unsigned int GetNewOrder() const { return m_order; } |
| 400 | void SetNewOrder(unsigned int order) { m_order = order; } |
| 401 | |
| 402 | virtual wxEvent *Clone() const { return new wxHeaderCtrlEvent(*this); } |
| 403 | |
| 404 | protected: |
| 405 | // the column affected by the event |
| 406 | int m_col; |
| 407 | |
| 408 | // the current width for the dragging events |
| 409 | int m_width; |
| 410 | |
| 411 | // the new column position for end reorder event |
| 412 | unsigned int m_order; |
| 413 | |
| 414 | private: |
| 415 | DECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxHeaderCtrlEvent) |
| 416 | }; |
| 417 | |
| 418 | |
| 419 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_CLICK, wxHeaderCtrlEvent ); |
| 420 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_RIGHT_CLICK, wxHeaderCtrlEvent ); |
| 421 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_MIDDLE_CLICK, wxHeaderCtrlEvent ); |
| 422 | |
| 423 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_DCLICK, wxHeaderCtrlEvent ); |
| 424 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_RIGHT_DCLICK, wxHeaderCtrlEvent ); |
| 425 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_MIDDLE_DCLICK, wxHeaderCtrlEvent ); |
| 426 | |
| 427 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_SEPARATOR_DCLICK, wxHeaderCtrlEvent ); |
| 428 | |
| 429 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_BEGIN_RESIZE, wxHeaderCtrlEvent ); |
| 430 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_RESIZING, wxHeaderCtrlEvent ); |
| 431 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_END_RESIZE, wxHeaderCtrlEvent ); |
| 432 | |
| 433 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_BEGIN_REORDER, wxHeaderCtrlEvent ); |
| 434 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_END_REORDER, wxHeaderCtrlEvent ); |
| 435 | |
| 436 | wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_COMMAND_HEADER_DRAGGING_CANCELLED, wxHeaderCtrlEvent ); |
| 437 | |
| 438 | typedef void (wxEvtHandler::*wxHeaderCtrlEventFunction)(wxHeaderCtrlEvent&); |
| 439 | |
| 440 | #define wxHeaderCtrlEventHandler(func) \ |
| 441 | wxEVENT_HANDLER_CAST(wxHeaderCtrlEventFunction, func) |
| 442 | |
| 443 | #define wx__DECLARE_HEADER_EVT(evt, id, fn) \ |
| 444 | wx__DECLARE_EVT1(wxEVT_COMMAND_HEADER_ ## evt, id, wxHeaderCtrlEventHandler(fn)) |
| 445 | |
| 446 | #define EVT_HEADER_CLICK(id, fn) wx__DECLARE_HEADER_EVT(CLICK, id, fn) |
| 447 | #define EVT_HEADER_RIGHT_CLICK(id, fn) wx__DECLARE_HEADER_EVT(RIGHT_CLICK, id, fn) |
| 448 | #define EVT_HEADER_MIDDLE_CLICK(id, fn) wx__DECLARE_HEADER_EVT(MIDDLE_CLICK, id, fn) |
| 449 | |
| 450 | #define EVT_HEADER_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(DCLICK, id, fn) |
| 451 | #define EVT_HEADER_RIGHT_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(RIGHT_DCLICK, id, fn) |
| 452 | #define EVT_HEADER_MIDDLE_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(MIDDLE_DCLICK, id, fn) |
| 453 | |
| 454 | #define EVT_HEADER_SEPARATOR_DCLICK(id, fn) wx__DECLARE_HEADER_EVT(SEPARATOR_DCLICK, id, fn) |
| 455 | |
| 456 | #define EVT_HEADER_BEGIN_RESIZE(id, fn) wx__DECLARE_HEADER_EVT(BEGIN_RESIZE, id, fn) |
| 457 | #define EVT_HEADER_RESIZING(id, fn) wx__DECLARE_HEADER_EVT(RESIZING, id, fn) |
| 458 | #define EVT_HEADER_END_RESIZE(id, fn) wx__DECLARE_HEADER_EVT(END_RESIZE, id, fn) |
| 459 | |
| 460 | #define EVT_HEADER_BEGIN_REORDER(id, fn) wx__DECLARE_HEADER_EVT(BEGIN_REORDER, id, fn) |
| 461 | #define EVT_HEADER_END_REORDER(id, fn) wx__DECLARE_HEADER_EVT(END_REORDER, id, fn) |
| 462 | |
| 463 | #define EVT_HEADER_DRAGGING_CANCELLED(id, fn) wx__DECLARE_HEADER_EVT(DRAGGING_CANCELLED, id, fn) |
| 464 | |
| 465 | #endif // wxUSE_HEADERCTRL |
| 466 | |
| 467 | #endif // _WX_HEADERCTRL_H_ |