| 1 | ///////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: wx/ctrlsub.h (read: "wxConTRoL with SUBitems") |
| 3 | // Purpose: wxControlWithItems interface |
| 4 | // Author: Vadim Zeitlin |
| 5 | // Modified by: |
| 6 | // Created: 22.10.99 |
| 7 | // RCS-ID: $Id$ |
| 8 | // Copyright: (c) wxWidgets team |
| 9 | // Licence: wxWindows licence |
| 10 | ///////////////////////////////////////////////////////////////////////////// |
| 11 | |
| 12 | #ifndef _WX_CTRLSUB_H_BASE_ |
| 13 | #define _WX_CTRLSUB_H_BASE_ |
| 14 | |
| 15 | #include "wx/defs.h" |
| 16 | |
| 17 | #if wxUSE_CONTROLS |
| 18 | |
| 19 | #include "wx/arrstr.h" |
| 20 | #include "wx/control.h" // base class |
| 21 | |
| 22 | // ---------------------------------------------------------------------------- |
| 23 | // wxItemContainer defines an interface which is implemented by all controls |
| 24 | // which have string subitems each of which may be selected. |
| 25 | // |
| 26 | // It is decomposed in wxItemContainerImmutable which omits all methods |
| 27 | // adding/removing items and is used by wxRadioBox and wxItemContainer itself. |
| 28 | // |
| 29 | // Examples: wxListBox, wxCheckListBox, wxChoice and wxComboBox (which |
| 30 | // implements an extended interface deriving from this one) |
| 31 | // ---------------------------------------------------------------------------- |
| 32 | |
| 33 | class WXDLLIMPEXP_CORE wxItemContainerImmutable |
| 34 | { |
| 35 | public: |
| 36 | wxItemContainerImmutable() { } |
| 37 | virtual ~wxItemContainerImmutable(); |
| 38 | |
| 39 | // accessing strings |
| 40 | // ----------------- |
| 41 | |
| 42 | virtual unsigned int GetCount() const = 0; |
| 43 | bool IsEmpty() const { return GetCount() == 0; } |
| 44 | |
| 45 | virtual wxString GetString(unsigned int n) const = 0; |
| 46 | wxArrayString GetStrings() const; |
| 47 | virtual void SetString(unsigned int n, const wxString& s) = 0; |
| 48 | |
| 49 | // finding string natively is either case sensitive or insensitive |
| 50 | // but never both so fall back to this base version for not |
| 51 | // supported search type |
| 52 | virtual int FindString(const wxString& s, bool bCase = false) const |
| 53 | { |
| 54 | unsigned int count = GetCount(); |
| 55 | |
| 56 | for ( unsigned int i = 0; i < count ; ++i ) |
| 57 | { |
| 58 | if (GetString(i).IsSameAs( s , bCase )) |
| 59 | return (int)i; |
| 60 | } |
| 61 | |
| 62 | return wxNOT_FOUND; |
| 63 | } |
| 64 | |
| 65 | |
| 66 | // selection |
| 67 | // --------- |
| 68 | |
| 69 | virtual void SetSelection(int n) = 0; |
| 70 | virtual int GetSelection() const = 0; |
| 71 | |
| 72 | // set selection to the specified string, return false if not found |
| 73 | bool SetStringSelection(const wxString& s); |
| 74 | |
| 75 | // return the selected string or empty string if none |
| 76 | virtual wxString GetStringSelection() const; |
| 77 | |
| 78 | // this is the same as SetSelection( for single-selection controls but |
| 79 | // reads better for multi-selection ones |
| 80 | void Select(int n) { SetSelection(n); } |
| 81 | |
| 82 | |
| 83 | protected: |
| 84 | // check that the index is valid |
| 85 | bool IsValid(unsigned int n) const { return n < GetCount(); } |
| 86 | bool IsValidInsert(unsigned int n) const { return n <= GetCount(); } |
| 87 | }; |
| 88 | |
| 89 | // ---------------------------------------------------------------------------- |
| 90 | // wxItemContainer extends wxItemContainerImmutable interface with methods |
| 91 | // for adding/removing items. |
| 92 | // |
| 93 | // Classes deriving from this one must override DoInsertItems() to implement |
| 94 | // adding items to the control. This can often be implemented more efficiently |
| 95 | // than simply looping over the elements and inserting them but if this is not |
| 96 | // the case, the generic DoInsertItemsInLoop can be used in implementation, but |
| 97 | // in this case DoInsertItem() needs to be overridden. |
| 98 | // ---------------------------------------------------------------------------- |
| 99 | |
| 100 | class WXDLLIMPEXP_CORE wxItemContainer : public wxItemContainerImmutable |
| 101 | { |
| 102 | private: |
| 103 | // AppendItems() and InsertItems() helpers just call DoAppend/InsertItems() |
| 104 | // after doing some checks |
| 105 | // |
| 106 | // NB: they're defined here so that they're inlined when used in public part |
| 107 | int AppendItems(const wxArrayStringsAdapter& items, |
| 108 | void **clientData, |
| 109 | wxClientDataType type) |
| 110 | { |
| 111 | if ( items.IsEmpty() ) |
| 112 | return wxNOT_FOUND; |
| 113 | |
| 114 | return DoAppendItems(items, clientData, type); |
| 115 | } |
| 116 | |
| 117 | int AppendItems(const wxArrayStringsAdapter& items) |
| 118 | { |
| 119 | return AppendItems(items, NULL, wxClientData_None); |
| 120 | } |
| 121 | |
| 122 | int AppendItems(const wxArrayStringsAdapter& items, void **clientData) |
| 123 | { |
| 124 | wxASSERT_MSG( GetClientDataType() != wxClientData_Object, |
| 125 | _T("can't mix different types of client data") ); |
| 126 | |
| 127 | return AppendItems(items, clientData, wxClientData_Void); |
| 128 | } |
| 129 | |
| 130 | int AppendItems(const wxArrayStringsAdapter& items, |
| 131 | wxClientData **clientData) |
| 132 | { |
| 133 | wxASSERT_MSG( GetClientDataType() != wxClientData_Void, |
| 134 | _T("can't mix different types of client data") ); |
| 135 | |
| 136 | return AppendItems(items, reinterpret_cast<void **>(clientData), |
| 137 | wxClientData_Object); |
| 138 | } |
| 139 | |
| 140 | int InsertItems(const wxArrayStringsAdapter& items, |
| 141 | unsigned int pos, |
| 142 | void **clientData, |
| 143 | wxClientDataType type) |
| 144 | { |
| 145 | wxASSERT_MSG( !IsSorted(), _T("can't insert items in sorted control") ); |
| 146 | |
| 147 | wxCHECK_MSG( pos <= GetCount(), wxNOT_FOUND, |
| 148 | _T("position out of range") ); |
| 149 | |
| 150 | // not all derived classes handle empty arrays correctly in |
| 151 | // DoInsertItems() and besides it really doesn't make much sense to do |
| 152 | // this (for append it could correspond to creating an initially empty |
| 153 | // control but why would anybody need to insert 0 items?) |
| 154 | wxCHECK_MSG( !items.IsEmpty(), wxNOT_FOUND, |
| 155 | _T("need something to insert") ); |
| 156 | |
| 157 | return DoInsertItems(items, pos, clientData, type); |
| 158 | } |
| 159 | |
| 160 | int InsertItems(const wxArrayStringsAdapter& items, unsigned int pos) |
| 161 | { |
| 162 | return InsertItems(items, pos, NULL, wxClientData_None); |
| 163 | } |
| 164 | |
| 165 | int InsertItems(const wxArrayStringsAdapter& items, |
| 166 | unsigned int pos, |
| 167 | void **clientData) |
| 168 | { |
| 169 | wxASSERT_MSG( GetClientDataType() != wxClientData_Object, |
| 170 | _T("can't mix different types of client data") ); |
| 171 | |
| 172 | return InsertItems(items, pos, clientData, wxClientData_Void); |
| 173 | } |
| 174 | |
| 175 | int InsertItems(const wxArrayStringsAdapter& items, |
| 176 | unsigned int pos, |
| 177 | wxClientData **clientData) |
| 178 | { |
| 179 | wxASSERT_MSG( GetClientDataType() != wxClientData_Void, |
| 180 | _T("can't mix different types of client data") ); |
| 181 | |
| 182 | return InsertItems(items, pos, |
| 183 | reinterpret_cast<void **>(clientData), |
| 184 | wxClientData_Object); |
| 185 | } |
| 186 | |
| 187 | public: |
| 188 | wxItemContainer() { m_clientDataItemsType = wxClientData_None; } |
| 189 | virtual ~wxItemContainer(); |
| 190 | |
| 191 | // adding items |
| 192 | // ------------ |
| 193 | |
| 194 | // append single item, return its position in the control (which can be |
| 195 | // different from the last one if the control is sorted) |
| 196 | int Append(const wxString& item) |
| 197 | { return AppendItems(item); } |
| 198 | int Append(const wxString& item, void *clientData) |
| 199 | { return AppendItems(item, &clientData); } |
| 200 | int Append(const wxString& item, wxClientData *clientData) |
| 201 | { return AppendItems(item, &clientData); } |
| 202 | |
| 203 | // append several items at once to the control, return the position of the |
| 204 | // last item appended |
| 205 | int Append(const wxArrayString& items) |
| 206 | { return AppendItems(items); } |
| 207 | int Append(const wxArrayString& items, void **clientData) |
| 208 | { return AppendItems(items, clientData); } |
| 209 | int Append(const wxArrayString& items, wxClientData **clientData) |
| 210 | { return AppendItems(items, clientData); } |
| 211 | int Append(unsigned int n, const wxString *items) |
| 212 | { return AppendItems(wxArrayStringsAdapter(n, items)); } |
| 213 | int Append(unsigned int n, const wxString *items, void **clientData) |
| 214 | { return AppendItems(wxArrayStringsAdapter(n, items), clientData); } |
| 215 | int Append(unsigned int n, |
| 216 | const wxString *items, |
| 217 | wxClientData **clientData) |
| 218 | { return AppendItems(wxArrayStringsAdapter(n, items), clientData); } |
| 219 | |
| 220 | // only for RTTI needs (separate name) |
| 221 | void AppendString(const wxString& item) |
| 222 | { Append(item); } |
| 223 | |
| 224 | |
| 225 | // inserting items: not for sorted controls! |
| 226 | // ----------------------------------------- |
| 227 | |
| 228 | // insert single item at the given position, return its effective position |
| 229 | int Insert(const wxString& item, unsigned int pos) |
| 230 | { return InsertItems(item, pos); } |
| 231 | int Insert(const wxString& item, unsigned int pos, void *clientData) |
| 232 | { return InsertItems(item, pos, &clientData); } |
| 233 | int Insert(const wxString& item, unsigned int pos, wxClientData *clientData) |
| 234 | { return InsertItems(item, pos, &clientData); } |
| 235 | |
| 236 | // insert several items at once into the control, return the index of the |
| 237 | // last item inserted |
| 238 | int Insert(const wxArrayString& items, unsigned int pos) |
| 239 | { return InsertItems(items, pos); } |
| 240 | int Insert(const wxArrayString& items, unsigned int pos, void **clientData) |
| 241 | { return InsertItems(items, pos, clientData); } |
| 242 | int Insert(const wxArrayString& items, |
| 243 | unsigned int pos, |
| 244 | wxClientData **clientData) |
| 245 | { return InsertItems(items, pos, clientData); } |
| 246 | int Insert(unsigned int n, const wxString *items, unsigned int pos) |
| 247 | { return InsertItems(wxArrayStringsAdapter(n, items), pos); } |
| 248 | int Insert(unsigned int n, |
| 249 | const wxString *items, |
| 250 | unsigned int pos, |
| 251 | void **clientData) |
| 252 | { return InsertItems(wxArrayStringsAdapter(n, items), pos, clientData); } |
| 253 | int Insert(unsigned int n, |
| 254 | const wxString *items, |
| 255 | unsigned int pos, |
| 256 | wxClientData **clientData) |
| 257 | { return InsertItems(wxArrayStringsAdapter(n, items), pos, clientData); } |
| 258 | |
| 259 | |
| 260 | // replacing items |
| 261 | // --------------- |
| 262 | |
| 263 | void Set(const wxArrayString& items) |
| 264 | { Clear(); Append(items); } |
| 265 | void Set(const wxArrayString& items, void **clientData) |
| 266 | { Clear(); Append(items, clientData); } |
| 267 | void Set(const wxArrayString& items, wxClientData **clientData) |
| 268 | { Clear(); Append(items, clientData); } |
| 269 | void Set(unsigned int n, const wxString *items) |
| 270 | { Clear(); Append(n, items); } |
| 271 | void Set(unsigned int n, const wxString *items, void **clientData) |
| 272 | { Clear(); Append(n, items, clientData); } |
| 273 | void Set(unsigned int n, const wxString *items, wxClientData **clientData) |
| 274 | { Clear(); Append(n, items, clientData); } |
| 275 | |
| 276 | // deleting items |
| 277 | // -------------- |
| 278 | |
| 279 | void Clear(); |
| 280 | void Delete(unsigned int pos); |
| 281 | |
| 282 | |
| 283 | // various accessors |
| 284 | // ----------------- |
| 285 | |
| 286 | // The control may maintain its items in a sorted order in which case |
| 287 | // items are automatically inserted at the right position when they are |
| 288 | // inserted or appended. Derived classes have to override this method if |
| 289 | // they implement sorting, typically by returning HasFlag(wxXX_SORT) |
| 290 | virtual bool IsSorted() const { return false; } |
| 291 | |
| 292 | |
| 293 | // client data stuff |
| 294 | // ----------------- |
| 295 | |
| 296 | void SetClientData(unsigned int n, void* clientData); |
| 297 | void* GetClientData(unsigned int n) const; |
| 298 | |
| 299 | void SetClientObject(unsigned int n, wxClientData* clientData); |
| 300 | wxClientData* GetClientObject(unsigned int n) const; |
| 301 | |
| 302 | // return the type of client data stored in this control: usually it just |
| 303 | // returns m_clientDataItemsType but must be overridden in the controls |
| 304 | // which delegate their client data storage to another one (e.g. wxChoice |
| 305 | // in wxUniv which stores data in wxListBox which it uses anyhow); don't |
| 306 | // forget to override SetClientDataType() if you override this one |
| 307 | // |
| 308 | // NB: for this to work no code should ever access m_clientDataItemsType |
| 309 | // directly but only via this function! |
| 310 | virtual wxClientDataType GetClientDataType() const |
| 311 | { return m_clientDataItemsType; } |
| 312 | |
| 313 | bool HasClientData() const |
| 314 | { return GetClientDataType() != wxClientData_None; } |
| 315 | bool HasClientObjectData() const |
| 316 | { return GetClientDataType() == wxClientData_Object; } |
| 317 | bool HasClientUntypedData() const |
| 318 | { return GetClientDataType() == wxClientData_Void; } |
| 319 | |
| 320 | protected: |
| 321 | // there is usually no need to override this method but you can do it if it |
| 322 | // is more convenient to only do "real" insertions in DoInsertItems() and |
| 323 | // to implement items appending here (in which case DoInsertItems() should |
| 324 | // call this method if pos == GetCount() as it can still be called in this |
| 325 | // case if public Insert() is called with such position) |
| 326 | virtual int DoAppendItems(const wxArrayStringsAdapter& items, |
| 327 | void **clientData, |
| 328 | wxClientDataType type) |
| 329 | { |
| 330 | return DoInsertItems(items, GetCount(), clientData, type); |
| 331 | } |
| 332 | |
| 333 | // this method must be implemented to insert the items into the control at |
| 334 | // position pos which can be GetCount() meaning that the items should be |
| 335 | // appended; for the sorted controls the position can be ignored |
| 336 | // |
| 337 | // the derived classes typically use AssignNewItemClientData() to |
| 338 | // associate the data with the items as they're being inserted |
| 339 | // |
| 340 | // the method should return the index of the position the last item was |
| 341 | // inserted into or wxNOT_FOUND if an error occurred |
| 342 | virtual int DoInsertItems(const wxArrayStringsAdapter & items, |
| 343 | unsigned int pos, |
| 344 | void **clientData, |
| 345 | wxClientDataType type) = 0; |
| 346 | |
| 347 | // before the client data is set for the first time for the control which |
| 348 | // hadn't had it before, DoInitItemClientData() is called which gives the |
| 349 | // derived class the possibility to initialize its client data storage only |
| 350 | // when client data is really used |
| 351 | virtual void DoInitItemClientData() { } |
| 352 | virtual void DoSetItemClientData(unsigned int n, void *clientData) = 0; |
| 353 | virtual void *DoGetItemClientData(unsigned int n) const = 0; |
| 354 | |
| 355 | virtual void DoClear() = 0; |
| 356 | virtual void DoDeleteOneItem(unsigned int pos) = 0; |
| 357 | |
| 358 | |
| 359 | // methods useful for the derived classes which don't have any better way |
| 360 | // of adding multiple items to the control than doing it one by one: such |
| 361 | // classes should call DoInsertItemsInLoop() from their DoInsert() and |
| 362 | // override DoInsertOneItem() to perform the real insertion |
| 363 | virtual int DoInsertOneItem(const wxString& item, unsigned int pos); |
| 364 | int DoInsertItemsInLoop(const wxArrayStringsAdapter& items, |
| 365 | unsigned int pos, |
| 366 | void **clientData, |
| 367 | wxClientDataType type); |
| 368 | |
| 369 | |
| 370 | // helper for DoInsertItems(): n is the index into clientData, pos is the |
| 371 | // position of the item in the control |
| 372 | void AssignNewItemClientData(unsigned int pos, |
| 373 | void **clientData, |
| 374 | unsigned int n, |
| 375 | wxClientDataType type); |
| 376 | |
| 377 | // free the client object associated with the item at given position and |
| 378 | // set it to NULL (must only be called if HasClientObjectData()) |
| 379 | void ResetItemClientObject(unsigned int n); |
| 380 | |
| 381 | // set the type of the client data stored in this control: override this if |
| 382 | // you override GetClientDataType() |
| 383 | virtual void SetClientDataType(wxClientDataType clientDataItemsType) |
| 384 | { |
| 385 | m_clientDataItemsType = clientDataItemsType; |
| 386 | } |
| 387 | |
| 388 | private: |
| 389 | // the type of the client data for the items |
| 390 | wxClientDataType m_clientDataItemsType; |
| 391 | }; |
| 392 | |
| 393 | // this macro must (unfortunately) be used in any class deriving from both |
| 394 | // wxItemContainer and wxControl because otherwise there is ambiguity when |
| 395 | // calling GetClientXXX() functions -- the compiler can't choose between the |
| 396 | // two versions |
| 397 | #define wxCONTROL_ITEMCONTAINER_CLIENTDATAOBJECT_RECAST \ |
| 398 | void SetClientData(void *data) \ |
| 399 | { wxEvtHandler::SetClientData(data); } \ |
| 400 | void *GetClientData() const \ |
| 401 | { return wxEvtHandler::GetClientData(); } \ |
| 402 | void SetClientObject(wxClientData *data) \ |
| 403 | { wxEvtHandler::SetClientObject(data); } \ |
| 404 | wxClientData *GetClientObject() const \ |
| 405 | { return wxEvtHandler::GetClientObject(); } \ |
| 406 | void SetClientData(unsigned int n, void* clientData) \ |
| 407 | { wxItemContainer::SetClientData(n, clientData); } \ |
| 408 | void* GetClientData(unsigned int n) const \ |
| 409 | { return wxItemContainer::GetClientData(n); } \ |
| 410 | void SetClientObject(unsigned int n, wxClientData* clientData) \ |
| 411 | { wxItemContainer::SetClientObject(n, clientData); } \ |
| 412 | wxClientData* GetClientObject(unsigned int n) const \ |
| 413 | { return wxItemContainer::GetClientObject(n); } |
| 414 | |
| 415 | class WXDLLIMPEXP_CORE wxControlWithItemsBase : public wxControl, |
| 416 | public wxItemContainer |
| 417 | { |
| 418 | public: |
| 419 | wxControlWithItemsBase() { } |
| 420 | |
| 421 | // we have to redefine these functions here to avoid ambiguities in classes |
| 422 | // deriving from us which would arise otherwise because both base classses |
| 423 | // have the methods with the same names - hopefully, a smart compiler can |
| 424 | // optimize away these simple inline wrappers so we don't suffer much from |
| 425 | // this |
| 426 | wxCONTROL_ITEMCONTAINER_CLIENTDATAOBJECT_RECAST |
| 427 | |
| 428 | // usually the controls like list/combo boxes have their own background |
| 429 | // colour |
| 430 | virtual bool ShouldInheritColours() const { return false; } |
| 431 | |
| 432 | protected: |
| 433 | // fill in the client object or data field of the event as appropriate |
| 434 | // |
| 435 | // calls InitCommandEvent() and, if n != wxNOT_FOUND, also sets the per |
| 436 | // item client data |
| 437 | void InitCommandEventWithItems(wxCommandEvent& event, int n); |
| 438 | |
| 439 | private: |
| 440 | DECLARE_NO_COPY_CLASS(wxControlWithItemsBase) |
| 441 | }; |
| 442 | |
| 443 | // define the platform-specific wxControlWithItems class |
| 444 | #if defined(__WXMSW__) |
| 445 | #include "wx/msw/ctrlsub.h" |
| 446 | #elif defined(__WXMOTIF__) |
| 447 | #include "wx/motif/ctrlsub.h" |
| 448 | #else |
| 449 | class WXDLLIMPEXP_CORE wxControlWithItems : public wxControlWithItemsBase |
| 450 | { |
| 451 | public: |
| 452 | wxControlWithItems() { } |
| 453 | |
| 454 | private: |
| 455 | DECLARE_ABSTRACT_CLASS(wxControlWithItems) |
| 456 | DECLARE_NO_COPY_CLASS(wxControlWithItems) |
| 457 | }; |
| 458 | #endif |
| 459 | |
| 460 | #endif // wxUSE_CONTROLS |
| 461 | |
| 462 | #endif // _WX_CTRLSUB_H_BASE_ |