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