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 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22
23 #if wxUSE_LISTBOOK
24
25 #include "wx/listbook.h"
26
27 #ifndef WX_PRECOMP
28 #include "wx/settings.h"
29 #endif
30
31 #include "wx/listctrl.h"
32 #include "wx/statline.h"
33 #include "wx/imaglist.h"
34
35 // ----------------------------------------------------------------------------
36 // event table
37 // ----------------------------------------------------------------------------
38
39 wxIMPLEMENT_DYNAMIC_CLASS(wxListbook, wxBookCtrlBase);
40
41 wxDEFINE_EVENT( wxEVT_LISTBOOK_PAGE_CHANGING, wxBookCtrlEvent );
42 wxDEFINE_EVENT( wxEVT_LISTBOOK_PAGE_CHANGED, wxBookCtrlEvent );
43
wxBEGIN_EVENT_TABLE(wxListbook,wxBookCtrlBase)44 wxBEGIN_EVENT_TABLE(wxListbook, wxBookCtrlBase)
45 EVT_SIZE(wxListbook::OnSize)
46 EVT_LIST_ITEM_SELECTED(wxID_ANY, wxListbook::OnListSelected)
47 wxEND_EVENT_TABLE()
48
49 // ============================================================================
50 // wxListbook implementation
51 // ============================================================================
52
53 // ----------------------------------------------------------------------------
54 // wxListbook creation
55 // ----------------------------------------------------------------------------
56
57 bool
58 wxListbook::Create(wxWindow *parent,
59 wxWindowID id,
60 const wxPoint& pos,
61 const wxSize& size,
62 long style,
63 const wxString& name)
64 {
65 if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
66 {
67 #ifdef __WXMAC__
68 style |= wxBK_TOP;
69 #else // !__WXMAC__
70 style |= wxBK_LEFT;
71 #endif // __WXMAC__/!__WXMAC__
72 }
73
74 // no border for this control, it doesn't look nice together with
75 // wxListCtrl border
76 style &= ~wxBORDER_MASK;
77 style |= wxBORDER_NONE;
78
79 if ( !wxControl::Create(parent, id, pos, size, style,
80 wxDefaultValidator, name) )
81 return false;
82
83 m_bookctrl = new wxListView
84 (
85 this,
86 wxID_ANY,
87 wxDefaultPosition,
88 wxDefaultSize,
89 GetListCtrlFlags()
90 );
91
92 if ( GetListView()->InReportView() )
93 GetListView()->InsertColumn(0, wxS("Pages"));
94
95 // Ensure that we rearrange the items in our list view after all the pages
96 // are added.
97 PostSizeEvent();
98
99 return true;
100 }
101
102 // ----------------------------------------------------------------------------
103 // wxListCtrl flags
104 // ----------------------------------------------------------------------------
105
GetListCtrlFlags() const106 long wxListbook::GetListCtrlFlags() const
107 {
108 // We'd like to always use wxLC_ICON mode but it doesn't work with the
109 // native wxListCtrl under MSW unless we do have icons for all the items,
110 // so we can't use it if we have no image list. In this case we'd like to
111 // use wxLC_LIST mode because it works correctly for both horizontally and
112 // vertically laid out controls, but MSW native wxListCtrl insists on
113 // creating multiple columns if there are too many items and there doesn't
114 // seem anything to do about it, so we have to use wxLC_REPORT mode in this
115 // case there.
116
117 long flags = IsVertical() ? wxLC_ALIGN_LEFT : wxLC_ALIGN_TOP;
118 if ( GetImageList() )
119 {
120 flags |= wxLC_ICON;
121 }
122 else // No images.
123 {
124 #ifdef __WXMSW__
125 if ( !IsVertical() )
126 {
127 // Notice that we intentionally overwrite the alignment flags here
128 // by not using "|=", alignment isn't used for report view.
129 flags = wxLC_REPORT | wxLC_NO_HEADER;
130 }
131 else
132 #endif // __WXMSW__
133 {
134 flags |= wxLC_LIST;
135 }
136
137 #ifdef __WXQT__
138 flags |= wxLC_NO_HEADER;
139 #endif
140 }
141
142 // Use single selection in any case.
143 return flags | wxLC_SINGLE_SEL;
144 }
145
146 // ----------------------------------------------------------------------------
147 // wxListbook geometry management
148 // ----------------------------------------------------------------------------
149
OnSize(wxSizeEvent & event)150 void wxListbook::OnSize(wxSizeEvent& event)
151 {
152 // arrange the icons before calling SetClientSize(), otherwise it wouldn't
153 // account for the scrollbars the list control might need and, at least
154 // under MSW, we'd finish with an ugly looking list control with both
155 // vertical and horizontal scrollbar (with one of them being added because
156 // the other one is not accounted for in client size computations)
157 wxListView * const list = GetListView();
158 if ( list )
159 {
160 list->Arrange();
161
162 const int sel = GetSelection();
163 if ( sel != wxNOT_FOUND )
164 list->EnsureVisible(sel);
165 }
166
167 event.Skip();
168 }
169
HitTest(const wxPoint & pt,long * flags) const170 int wxListbook::HitTest(const wxPoint& pt, long *flags) const
171 {
172 int pagePos = wxNOT_FOUND;
173
174 if ( flags )
175 *flags = wxBK_HITTEST_NOWHERE;
176
177 // convert from listbook control coordinates to list control coordinates
178 const wxListView * const list = GetListView();
179 const wxPoint listPt = list->ScreenToClient(ClientToScreen(pt));
180
181 // is the point inside list control?
182 if ( wxRect(list->GetSize()).Contains(listPt) )
183 {
184 int flagsList;
185 pagePos = list->HitTest(listPt, flagsList);
186
187 if ( flags )
188 {
189 if ( pagePos != wxNOT_FOUND )
190 *flags = 0;
191
192 if ( flagsList & (wxLIST_HITTEST_ONITEMICON |
193 wxLIST_HITTEST_ONITEMSTATEICON ) )
194 *flags |= wxBK_HITTEST_ONICON;
195
196 if ( flagsList & wxLIST_HITTEST_ONITEMLABEL )
197 *flags |= wxBK_HITTEST_ONLABEL;
198 }
199 }
200 else // not over list control at all
201 {
202 if ( flags && GetPageRect().Contains(pt) )
203 *flags |= wxBK_HITTEST_ONPAGE;
204 }
205
206 return pagePos;
207 }
208
UpdateSize()209 void wxListbook::UpdateSize()
210 {
211 // we should find a more elegant way to force a layout than generating this
212 // dummy event
213 wxSizeEvent sz(GetSize(), GetId());
214 GetEventHandler()->ProcessEvent(sz);
215 }
216
217 // ----------------------------------------------------------------------------
218 // accessing the pages
219 // ----------------------------------------------------------------------------
220
SetPageText(size_t n,const wxString & strText)221 bool wxListbook::SetPageText(size_t n, const wxString& strText)
222 {
223 GetListView()->SetItemText(n, strText);
224
225 return true;
226 }
227
GetPageText(size_t n) const228 wxString wxListbook::GetPageText(size_t n) const
229 {
230 return GetListView()->GetItemText(n);
231 }
232
GetPageImage(size_t n) const233 int wxListbook::GetPageImage(size_t n) const
234 {
235 wxListItem item;
236 item.SetId(n);
237 item.SetMask(wxLIST_MASK_IMAGE);
238
239 if (GetListView()->GetItem(item))
240 {
241 return item.GetImage();
242 }
243 else
244 {
245 return wxNOT_FOUND;
246 }
247 }
248
SetPageImage(size_t n,int imageId)249 bool wxListbook::SetPageImage(size_t n, int imageId)
250 {
251 return GetListView()->SetItemImage(n, imageId);
252 }
253
254 // ----------------------------------------------------------------------------
255 // image list stuff
256 // ----------------------------------------------------------------------------
257
SetImageList(wxImageList * imageList)258 void wxListbook::SetImageList(wxImageList *imageList)
259 {
260 const long flagsOld = GetListCtrlFlags();
261
262 wxBookCtrlBase::SetImageList(imageList);
263
264 const long flagsNew = GetListCtrlFlags();
265
266 wxListView * const list = GetListView();
267
268 // We may need to change the list control mode if the image list presence
269 // has changed.
270 if ( flagsNew != flagsOld )
271 {
272 // Preserve the selection which is lost when changing the mode
273 const int oldSel = GetSelection();
274
275 list->SetWindowStyleFlag(flagsNew);
276 if ( list->InReportView() )
277 list->InsertColumn(0, wxS("Pages"));
278
279 // Restore selection
280 if ( oldSel != wxNOT_FOUND )
281 SetSelection(oldSel);
282 }
283
284 list->SetImageList(imageList, wxIMAGE_LIST_NORMAL);
285 }
286
287 // ----------------------------------------------------------------------------
288 // selection
289 // ----------------------------------------------------------------------------
290
UpdateSelectedPage(size_t newsel)291 void wxListbook::UpdateSelectedPage(size_t newsel)
292 {
293 GetListView()->Select(newsel);
294 GetListView()->Focus(newsel);
295 }
296
CreatePageChangingEvent() const297 wxBookCtrlEvent* wxListbook::CreatePageChangingEvent() const
298 {
299 return new wxBookCtrlEvent(wxEVT_LISTBOOK_PAGE_CHANGING, m_windowId);
300 }
301
MakeChangedEvent(wxBookCtrlEvent & event)302 void wxListbook::MakeChangedEvent(wxBookCtrlEvent &event)
303 {
304 event.SetEventType(wxEVT_LISTBOOK_PAGE_CHANGED);
305 }
306
307
308 // ----------------------------------------------------------------------------
309 // adding/removing the pages
310 // ----------------------------------------------------------------------------
311
312 bool
InsertPage(size_t n,wxWindow * page,const wxString & text,bool bSelect,int imageId)313 wxListbook::InsertPage(size_t n,
314 wxWindow *page,
315 const wxString& text,
316 bool bSelect,
317 int imageId)
318 {
319 if ( !wxBookCtrlBase::InsertPage(n, page, text, bSelect, imageId) )
320 return false;
321
322 GetListView()->InsertItem(n, text, imageId);
323
324 // if the inserted page is before the selected one, we must update the
325 // index of the selected page
326 if ( int(n) <= m_selection )
327 {
328 // one extra page added
329 m_selection++;
330 GetListView()->Select(m_selection);
331 GetListView()->Focus(m_selection);
332 }
333
334 if ( !DoSetSelectionAfterInsertion(n, bSelect) )
335 page->Hide();
336
337 UpdateSize();
338
339 return true;
340 }
341
DoRemovePage(size_t page)342 wxWindow *wxListbook::DoRemovePage(size_t page)
343 {
344 wxWindow *win = wxBookCtrlBase::DoRemovePage(page);
345
346 if ( win )
347 {
348 GetListView()->DeleteItem(page);
349
350 DoSetSelectionAfterRemoval(page);
351
352 GetListView()->Arrange();
353 UpdateSize();
354 }
355
356 return win;
357 }
358
359
DeleteAllPages()360 bool wxListbook::DeleteAllPages()
361 {
362 GetListView()->DeleteAllItems();
363 if (!wxBookCtrlBase::DeleteAllPages())
364 return false;
365
366 UpdateSize();
367
368 return true;
369 }
370
371 // ----------------------------------------------------------------------------
372 // wxListbook events
373 // ----------------------------------------------------------------------------
374
OnListSelected(wxListEvent & eventList)375 void wxListbook::OnListSelected(wxListEvent& eventList)
376 {
377 if ( eventList.GetEventObject() != m_bookctrl )
378 {
379 eventList.Skip();
380 return;
381 }
382
383 const int selNew = eventList.GetIndex();
384
385 if ( selNew == m_selection )
386 {
387 // this event can only come from our own Select(m_selection) below
388 // which we call when the page change is vetoed, so we should simply
389 // ignore it
390 return;
391 }
392
393 SetSelection(selNew);
394
395 // change wasn't allowed, return to previous state
396 if (m_selection != selNew)
397 {
398 GetListView()->Select(m_selection);
399 GetListView()->Focus(m_selection);
400 }
401 }
402
403 #endif // wxUSE_LISTBOOK
404