1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/generic/listbkg.cpp 3// Purpose: generic implementation of wxListbook 4// Author: Vadim Zeitlin 5// Modified by: 6// Created: 19.08.03 7// RCS-ID: $Id: listbkg.cpp 48783 2007-09-19 11:24:38Z RR $ 8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org> 9// Licence: wxWindows licence 10/////////////////////////////////////////////////////////////////////////////// 11 12// ============================================================================ 13// declarations 14// ============================================================================ 15 16// ---------------------------------------------------------------------------- 17// headers 18// ---------------------------------------------------------------------------- 19 20// For compilers that support precompilation, includes "wx.h". 21#include "wx/wxprec.h" 22 23#ifdef __BORLANDC__ 24 #pragma hdrstop 25#endif 26 27#if wxUSE_LISTBOOK 28 29#include "wx/listbook.h" 30 31#ifndef WX_PRECOMP 32 #include "wx/settings.h" 33#endif 34 35#include "wx/listctrl.h" 36#include "wx/statline.h" 37#include "wx/imaglist.h" 38 39// ---------------------------------------------------------------------------- 40// various wxWidgets macros 41// ---------------------------------------------------------------------------- 42 43// check that the page index is valid 44#define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount()) 45 46// ---------------------------------------------------------------------------- 47// event table 48// ---------------------------------------------------------------------------- 49 50IMPLEMENT_DYNAMIC_CLASS(wxListbook, wxBookCtrlBase) 51IMPLEMENT_DYNAMIC_CLASS(wxListbookEvent, wxNotifyEvent) 52 53#if !WXWIN_COMPATIBILITY_EVENT_TYPES 54const wxEventType wxEVT_COMMAND_LISTBOOK_PAGE_CHANGING = wxNewEventType(); 55const wxEventType wxEVT_COMMAND_LISTBOOK_PAGE_CHANGED = wxNewEventType(); 56#endif 57 58BEGIN_EVENT_TABLE(wxListbook, wxBookCtrlBase) 59 EVT_SIZE(wxListbook::OnSize) 60 EVT_LIST_ITEM_SELECTED(wxID_ANY, wxListbook::OnListSelected) 61END_EVENT_TABLE() 62 63// ============================================================================ 64// wxListbook implementation 65// ============================================================================ 66 67// ---------------------------------------------------------------------------- 68// wxListbook creation 69// ---------------------------------------------------------------------------- 70 71void wxListbook::Init() 72{ 73 m_selection = wxNOT_FOUND; 74} 75 76bool 77wxListbook::Create(wxWindow *parent, 78 wxWindowID id, 79 const wxPoint& pos, 80 const wxSize& size, 81 long style, 82 const wxString& name) 83{ 84 if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT ) 85 { 86#ifdef __WXMAC__ 87 style |= wxBK_TOP; 88#else // !__WXMAC__ 89 style |= wxBK_LEFT; 90#endif // __WXMAC__/!__WXMAC__ 91 } 92 93 // no border for this control, it doesn't look nice together with 94 // wxListCtrl border 95 style &= ~wxBORDER_MASK; 96 style |= wxBORDER_NONE; 97 98 if ( !wxControl::Create(parent, id, pos, size, style, 99 wxDefaultValidator, name) ) 100 return false; 101 102 m_bookctrl = new wxListView 103 ( 104 this, 105 wxID_ANY, 106 wxDefaultPosition, 107 wxDefaultSize, 108 wxLC_ICON | wxLC_SINGLE_SEL | 109 (IsVertical() ? wxLC_ALIGN_LEFT : wxLC_ALIGN_TOP) 110 ); 111 112#ifdef __WXMSW__ 113 // On XP with themes enabled the GetViewRect used in GetControllerSize() to 114 // determine the space needed for the list view will incorrectly return 115 // (0,0,0,0) the first time. So send a pending event so OnSize will be 116 // called again after the window is ready to go. Technically we don't 117 // need to do this on non-XP windows, but if things are already sized 118 // correctly then nothing changes and so there is no harm. 119 wxSizeEvent evt; 120 GetEventHandler()->AddPendingEvent(evt); 121#endif 122 return true; 123} 124 125// ---------------------------------------------------------------------------- 126// wxListbook geometry management 127// ---------------------------------------------------------------------------- 128 129wxSize wxListbook::GetControllerSize() const 130{ 131 const wxSize sizeClient = GetClientSize(), 132 sizeBorder = m_bookctrl->GetSize() - m_bookctrl->GetClientSize(), 133 sizeList = GetListView()->GetViewRect().GetSize() + sizeBorder; 134 135 wxSize size; 136 137 if ( IsVertical() ) 138 { 139 size.x = sizeClient.x; 140 size.y = sizeList.y; 141 } 142 else // left/right aligned 143 { 144 size.x = sizeList.x; 145 size.y = sizeClient.y; 146 } 147 148 return size; 149} 150 151void wxListbook::OnSize(wxSizeEvent& event) 152{ 153 // arrange the icons before calling SetClientSize(), otherwise it wouldn't 154 // account for the scrollbars the list control might need and, at least 155 // under MSW, we'd finish with an ugly looking list control with both 156 // vertical and horizontal scrollbar (with one of them being added because 157 // the other one is not accounted for in client size computations) 158 wxListView *list = GetListView(); 159 if (list) list->Arrange(); 160 wxBookCtrlBase::OnSize(event); 161} 162 163int wxListbook::HitTest(const wxPoint& pt, long *flags) const 164{ 165 int pagePos = wxNOT_FOUND; 166 167 if ( flags ) 168 *flags = wxBK_HITTEST_NOWHERE; 169 170 // convert from listbook control coordinates to list control coordinates 171 const wxListView * const list = GetListView(); 172 const wxPoint listPt = list->ScreenToClient(ClientToScreen(pt)); 173 174 // is the point inside list control? 175 if ( wxRect(list->GetSize()).Contains(listPt) ) 176 { 177 int flagsList; 178 pagePos = list->HitTest(listPt, flagsList); 179 180 if ( flags ) 181 { 182 if ( pagePos != wxNOT_FOUND ) 183 *flags = 0; 184 185 if ( flagsList & (wxLIST_HITTEST_ONITEMICON | 186 wxLIST_HITTEST_ONITEMSTATEICON ) ) 187 *flags |= wxBK_HITTEST_ONICON; 188 189 if ( flagsList & wxLIST_HITTEST_ONITEMLABEL ) 190 *flags |= wxBK_HITTEST_ONLABEL; 191 } 192 } 193 else // not over list control at all 194 { 195 if ( flags && GetPageRect().Contains(pt) ) 196 *flags |= wxBK_HITTEST_ONPAGE; 197 } 198 199 return pagePos; 200} 201 202wxSize wxListbook::CalcSizeFromPage(const wxSize& sizePage) const 203{ 204 // we need to add the size of the list control and the border between 205 const wxSize sizeList = GetControllerSize(); 206 207 wxSize size = sizePage; 208 if ( IsVertical() ) 209 { 210 size.y += sizeList.y + GetInternalBorder(); 211 } 212 else // left/right aligned 213 { 214 size.x += sizeList.x + GetInternalBorder(); 215 } 216 217 return size; 218} 219 220 221// ---------------------------------------------------------------------------- 222// accessing the pages 223// ---------------------------------------------------------------------------- 224 225bool wxListbook::SetPageText(size_t n, const wxString& strText) 226{ 227 GetListView()->SetItemText(n, strText); 228 229 return true; 230} 231 232wxString wxListbook::GetPageText(size_t n) const 233{ 234 return GetListView()->GetItemText(n); 235} 236 237int wxListbook::GetPageImage(size_t WXUNUSED(n)) const 238{ 239 wxFAIL_MSG( _T("wxListbook::GetPageImage() not implemented") ); 240 241 return wxNOT_FOUND; 242} 243 244bool wxListbook::SetPageImage(size_t n, int imageId) 245{ 246 return GetListView()->SetItemImage(n, imageId); 247} 248 249// ---------------------------------------------------------------------------- 250// image list stuff 251// ---------------------------------------------------------------------------- 252 253void wxListbook::SetImageList(wxImageList *imageList) 254{ 255 GetListView()->SetImageList(imageList, wxIMAGE_LIST_NORMAL); 256 257 wxBookCtrlBase::SetImageList(imageList); 258} 259 260// ---------------------------------------------------------------------------- 261// selection 262// ---------------------------------------------------------------------------- 263 264void wxListbook::UpdateSelectedPage(size_t newsel) 265{ 266 m_selection = newsel; 267 GetListView()->Select(newsel); 268 GetListView()->Focus(newsel); 269} 270 271int wxListbook::GetSelection() const 272{ 273 return m_selection; 274} 275 276wxBookCtrlBaseEvent* wxListbook::CreatePageChangingEvent() const 277{ 278 return new wxListbookEvent(wxEVT_COMMAND_LISTBOOK_PAGE_CHANGING, m_windowId); 279} 280 281void wxListbook::MakeChangedEvent(wxBookCtrlBaseEvent &event) 282{ 283 event.SetEventType(wxEVT_COMMAND_LISTBOOK_PAGE_CHANGED); 284} 285 286 287// ---------------------------------------------------------------------------- 288// adding/removing the pages 289// ---------------------------------------------------------------------------- 290 291bool 292wxListbook::InsertPage(size_t n, 293 wxWindow *page, 294 const wxString& text, 295 bool bSelect, 296 int imageId) 297{ 298 if ( !wxBookCtrlBase::InsertPage(n, page, text, bSelect, imageId) ) 299 return false; 300 301 GetListView()->InsertItem(n, text, imageId); 302 303 // if the inserted page is before the selected one, we must update the 304 // index of the selected page 305 if ( int(n) <= m_selection ) 306 { 307 // one extra page added 308 m_selection++; 309 GetListView()->Select(m_selection); 310 GetListView()->Focus(m_selection); 311 } 312 313 // some page should be selected: either this one or the first one if there 314 // is still no selection 315 int selNew = -1; 316 if ( bSelect ) 317 selNew = n; 318 else if ( m_selection == -1 ) 319 selNew = 0; 320 321 if ( selNew != m_selection ) 322 page->Hide(); 323 324 if ( selNew != -1 ) 325 SetSelection(selNew); 326 327 wxSizeEvent sz(GetSize(), GetId()); 328 GetEventHandler()->ProcessEvent(sz); 329 330 return true; 331} 332 333wxWindow *wxListbook::DoRemovePage(size_t page) 334{ 335 const size_t page_count = GetPageCount(); 336 wxWindow *win = wxBookCtrlBase::DoRemovePage(page); 337 338 if ( win ) 339 { 340 GetListView()->DeleteItem(page); 341 342 if (m_selection >= (int)page) 343 { 344 // force new sel valid if possible 345 int sel = m_selection - 1; 346 if (page_count == 1) 347 sel = wxNOT_FOUND; 348 else if ((page_count == 2) || (sel == -1)) 349 sel = 0; 350 351 // force sel invalid if deleting current page - don't try to hide it 352 m_selection = (m_selection == (int)page) ? wxNOT_FOUND : m_selection - 1; 353 354 if ((sel != wxNOT_FOUND) && (sel != m_selection)) 355 SetSelection(sel); 356 } 357 358 GetListView()->Arrange(); 359 if (GetPageCount() == 0) 360 { 361 wxSizeEvent sz(GetSize(), GetId()); 362 ProcessEvent(sz); 363 } 364 } 365 366 return win; 367} 368 369 370bool wxListbook::DeleteAllPages() 371{ 372 GetListView()->DeleteAllItems(); 373 if (!wxBookCtrlBase::DeleteAllPages()) 374 return false; 375 376 m_selection = -1; 377 378 wxSizeEvent sz(GetSize(), GetId()); 379 ProcessEvent(sz); 380 381 return true; 382} 383 384// ---------------------------------------------------------------------------- 385// wxListbook events 386// ---------------------------------------------------------------------------- 387 388void wxListbook::OnListSelected(wxListEvent& eventList) 389{ 390 if ( eventList.GetEventObject() != m_bookctrl ) 391 { 392 eventList.Skip(); 393 return; 394 } 395 396 const int selNew = eventList.GetIndex(); 397 398 if ( selNew == m_selection ) 399 { 400 // this event can only come from our own Select(m_selection) below 401 // which we call when the page change is vetoed, so we should simply 402 // ignore it 403 return; 404 } 405 406 SetSelection(selNew); 407 408 // change wasn't allowed, return to previous state 409 if (m_selection != selNew) 410 { 411 GetListView()->Select(m_selection); 412 GetListView()->Focus(m_selection); 413 } 414} 415 416#endif // wxUSE_LISTBOOK 417