1 |
william |
31 |
/////////////////////////////////////////////////////////////////////////////// |
2 |
|
|
// Name: src/common/bookctrl.cpp |
3 |
|
|
// Purpose: wxBookCtrlBase implementation |
4 |
|
|
// Author: Vadim Zeitlin |
5 |
|
|
// Modified by: |
6 |
|
|
// Created: 19.08.03 |
7 |
|
|
// RCS-ID: $Id: bookctrl.cpp 53783 2008-05-27 14:15:14Z SC $ |
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_BOOKCTRL |
28 |
|
|
|
29 |
|
|
#include "wx/imaglist.h" |
30 |
|
|
|
31 |
|
|
#include "wx/bookctrl.h" |
32 |
|
|
|
33 |
|
|
// ============================================================================ |
34 |
|
|
// implementation |
35 |
|
|
// ============================================================================ |
36 |
|
|
|
37 |
|
|
// ---------------------------------------------------------------------------- |
38 |
|
|
// event table |
39 |
|
|
// ---------------------------------------------------------------------------- |
40 |
|
|
|
41 |
|
|
IMPLEMENT_ABSTRACT_CLASS(wxBookCtrlBase, wxControl) |
42 |
|
|
|
43 |
|
|
BEGIN_EVENT_TABLE(wxBookCtrlBase, wxControl) |
44 |
|
|
EVT_SIZE(wxBookCtrlBase::OnSize) |
45 |
|
|
#if wxUSE_HELP |
46 |
|
|
EVT_HELP(wxID_ANY, wxBookCtrlBase::OnHelp) |
47 |
|
|
#endif // wxUSE_HELP |
48 |
|
|
END_EVENT_TABLE() |
49 |
|
|
|
50 |
|
|
// ---------------------------------------------------------------------------- |
51 |
|
|
// constructors and destructors |
52 |
|
|
// ---------------------------------------------------------------------------- |
53 |
|
|
|
54 |
|
|
void wxBookCtrlBase::Init() |
55 |
|
|
{ |
56 |
|
|
m_bookctrl = NULL; |
57 |
|
|
m_imageList = NULL; |
58 |
|
|
m_ownsImageList = false; |
59 |
|
|
m_fitToCurrentPage = false; |
60 |
|
|
|
61 |
|
|
#if defined(__WXWINCE__) |
62 |
|
|
m_internalBorder = 1; |
63 |
|
|
#else |
64 |
|
|
m_internalBorder = 5; |
65 |
|
|
#endif |
66 |
|
|
|
67 |
|
|
m_controlMargin = 0; |
68 |
|
|
m_controlSizer = NULL; |
69 |
|
|
} |
70 |
|
|
|
71 |
|
|
bool |
72 |
|
|
wxBookCtrlBase::Create(wxWindow *parent, |
73 |
|
|
wxWindowID id, |
74 |
|
|
const wxPoint& pos, |
75 |
|
|
const wxSize& size, |
76 |
|
|
long style, |
77 |
|
|
const wxString& name) |
78 |
|
|
{ |
79 |
|
|
return wxControl::Create |
80 |
|
|
( |
81 |
|
|
parent, |
82 |
|
|
id, |
83 |
|
|
pos, |
84 |
|
|
size, |
85 |
|
|
style, |
86 |
|
|
wxDefaultValidator, |
87 |
|
|
name |
88 |
|
|
); |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
wxBookCtrlBase::~wxBookCtrlBase() |
92 |
|
|
{ |
93 |
|
|
if ( m_ownsImageList ) |
94 |
|
|
{ |
95 |
|
|
// may be NULL, ok |
96 |
|
|
delete m_imageList; |
97 |
|
|
} |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
// ---------------------------------------------------------------------------- |
101 |
|
|
// image list |
102 |
|
|
// ---------------------------------------------------------------------------- |
103 |
|
|
|
104 |
|
|
void wxBookCtrlBase::SetImageList(wxImageList *imageList) |
105 |
|
|
{ |
106 |
|
|
if ( m_ownsImageList ) |
107 |
|
|
{ |
108 |
|
|
// may be NULL, ok |
109 |
|
|
delete m_imageList; |
110 |
|
|
|
111 |
|
|
m_ownsImageList = false; |
112 |
|
|
} |
113 |
|
|
|
114 |
|
|
m_imageList = imageList; |
115 |
|
|
} |
116 |
|
|
|
117 |
|
|
void wxBookCtrlBase::AssignImageList(wxImageList* imageList) |
118 |
|
|
{ |
119 |
|
|
SetImageList(imageList); |
120 |
|
|
|
121 |
|
|
m_ownsImageList = true; |
122 |
|
|
} |
123 |
|
|
|
124 |
|
|
// ---------------------------------------------------------------------------- |
125 |
|
|
// geometry |
126 |
|
|
// ---------------------------------------------------------------------------- |
127 |
|
|
|
128 |
|
|
void wxBookCtrlBase::DoInvalidateBestSize() |
129 |
|
|
{ |
130 |
|
|
// notice that it is not necessary to invalidate our own best size |
131 |
|
|
// explicitly if we have m_bookctrl as it will already invalidate the best |
132 |
|
|
// size of its parent when its own size is invalidated and its parent is |
133 |
|
|
// this control |
134 |
|
|
if ( m_bookctrl ) |
135 |
|
|
m_bookctrl->InvalidateBestSize(); |
136 |
|
|
else |
137 |
|
|
wxControl::InvalidateBestSize(); |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
void wxBookCtrlBase::SetPageSize(const wxSize& size) |
141 |
|
|
{ |
142 |
|
|
SetClientSize(CalcSizeFromPage(size)); |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
wxSize wxBookCtrlBase::DoGetBestSize() const |
146 |
|
|
{ |
147 |
|
|
wxSize bestSize; |
148 |
|
|
|
149 |
|
|
// iterate over all pages, get the largest width and height |
150 |
|
|
const size_t nCount = m_pages.size(); |
151 |
|
|
for ( size_t nPage = 0; nPage < nCount; nPage++ ) |
152 |
|
|
{ |
153 |
|
|
const wxWindow * const pPage = m_pages[nPage]; |
154 |
|
|
if( pPage ) |
155 |
|
|
{ |
156 |
|
|
wxSize childBestSize(pPage->GetBestSize()); |
157 |
|
|
|
158 |
|
|
if ( childBestSize.x > bestSize.x ) |
159 |
|
|
bestSize.x = childBestSize.x; |
160 |
|
|
|
161 |
|
|
if ( childBestSize.y > bestSize.y ) |
162 |
|
|
bestSize.y = childBestSize.y; |
163 |
|
|
} |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
if (m_fitToCurrentPage && GetCurrentPage()) |
167 |
|
|
bestSize = GetCurrentPage()->GetBestSize(); |
168 |
|
|
|
169 |
|
|
// convert display area to window area, adding the size necessary for the |
170 |
|
|
// tabs |
171 |
|
|
wxSize best = CalcSizeFromPage(bestSize); |
172 |
|
|
CacheBestSize(best); |
173 |
|
|
return best; |
174 |
|
|
} |
175 |
|
|
|
176 |
|
|
wxRect wxBookCtrlBase::GetPageRect() const |
177 |
|
|
{ |
178 |
|
|
const wxSize size = GetControllerSize(); |
179 |
|
|
|
180 |
|
|
wxPoint pt; |
181 |
|
|
wxRect rectPage(pt, GetClientSize()); |
182 |
|
|
|
183 |
|
|
switch ( GetWindowStyle() & wxBK_ALIGN_MASK ) |
184 |
|
|
{ |
185 |
|
|
default: |
186 |
|
|
wxFAIL_MSG( _T("unexpected alignment") ); |
187 |
|
|
// fall through |
188 |
|
|
|
189 |
|
|
case wxBK_TOP: |
190 |
|
|
rectPage.y = size.y + GetInternalBorder(); |
191 |
|
|
// fall through |
192 |
|
|
|
193 |
|
|
case wxBK_BOTTOM: |
194 |
|
|
rectPage.height -= size.y + GetInternalBorder(); |
195 |
|
|
if (rectPage.height < 0) |
196 |
|
|
rectPage.height = 0; |
197 |
|
|
break; |
198 |
|
|
|
199 |
|
|
case wxBK_LEFT: |
200 |
|
|
rectPage.x = size.x + GetInternalBorder(); |
201 |
|
|
// fall through |
202 |
|
|
|
203 |
|
|
case wxBK_RIGHT: |
204 |
|
|
rectPage.width -= size.x + GetInternalBorder(); |
205 |
|
|
if (rectPage.width < 0) |
206 |
|
|
rectPage.width = 0; |
207 |
|
|
break; |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
return rectPage; |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
// Lay out controls |
214 |
|
|
void wxBookCtrlBase::DoSize() |
215 |
|
|
{ |
216 |
|
|
if ( !m_bookctrl ) |
217 |
|
|
{ |
218 |
|
|
// we're not fully created yet or OnSize() should be hidden by derived class |
219 |
|
|
return; |
220 |
|
|
} |
221 |
|
|
|
222 |
|
|
if (GetSizer()) |
223 |
|
|
Layout(); |
224 |
|
|
else |
225 |
|
|
{ |
226 |
|
|
// resize controller and the page area to fit inside our new size |
227 |
|
|
const wxSize sizeClient( GetClientSize() ), |
228 |
|
|
sizeBorder( m_bookctrl->GetSize() - m_bookctrl->GetClientSize() ), |
229 |
|
|
sizeCtrl( GetControllerSize() ); |
230 |
|
|
|
231 |
|
|
m_bookctrl->SetClientSize( sizeCtrl.x - sizeBorder.x, sizeCtrl.y - sizeBorder.y ); |
232 |
|
|
// if this changes the visibility of the scrollbars the best size changes, relayout in this case |
233 |
|
|
wxSize sizeCtrl2 = GetControllerSize(); |
234 |
|
|
if ( sizeCtrl != sizeCtrl2 ) |
235 |
|
|
{ |
236 |
|
|
wxSize sizeBorder2 = m_bookctrl->GetSize() - m_bookctrl->GetClientSize(); |
237 |
|
|
m_bookctrl->SetClientSize( sizeCtrl2.x - sizeBorder2.x, sizeCtrl2.y - sizeBorder2.y ); |
238 |
|
|
} |
239 |
|
|
|
240 |
|
|
const wxSize sizeNew = m_bookctrl->GetSize(); |
241 |
|
|
wxPoint posCtrl; |
242 |
|
|
switch ( GetWindowStyle() & wxBK_ALIGN_MASK ) |
243 |
|
|
{ |
244 |
|
|
default: |
245 |
|
|
wxFAIL_MSG( _T("unexpected alignment") ); |
246 |
|
|
// fall through |
247 |
|
|
|
248 |
|
|
case wxBK_TOP: |
249 |
|
|
case wxBK_LEFT: |
250 |
|
|
// posCtrl is already ok |
251 |
|
|
break; |
252 |
|
|
|
253 |
|
|
case wxBK_BOTTOM: |
254 |
|
|
posCtrl.y = sizeClient.y - sizeNew.y; |
255 |
|
|
break; |
256 |
|
|
|
257 |
|
|
case wxBK_RIGHT: |
258 |
|
|
posCtrl.x = sizeClient.x - sizeNew.x; |
259 |
|
|
break; |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
if ( m_bookctrl->GetPosition() != posCtrl ) |
263 |
|
|
m_bookctrl->Move(posCtrl); |
264 |
|
|
} |
265 |
|
|
|
266 |
|
|
// resize all pages to fit the new control size |
267 |
|
|
const wxRect pageRect = GetPageRect(); |
268 |
|
|
const unsigned pagesCount = m_pages.Count(); |
269 |
|
|
for ( unsigned int i = 0; i < pagesCount; ++i ) |
270 |
|
|
{ |
271 |
|
|
wxWindow * const page = m_pages[i]; |
272 |
|
|
if ( !page ) |
273 |
|
|
{ |
274 |
|
|
wxASSERT_MSG( AllowNullPage(), |
275 |
|
|
_T("Null page in a control that does not allow null pages?") ); |
276 |
|
|
continue; |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
page->SetSize(pageRect); |
280 |
|
|
} |
281 |
|
|
} |
282 |
|
|
|
283 |
|
|
void wxBookCtrlBase::OnSize(wxSizeEvent& event) |
284 |
|
|
{ |
285 |
|
|
event.Skip(); |
286 |
|
|
|
287 |
|
|
DoSize(); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
wxSize wxBookCtrlBase::GetControllerSize() const |
291 |
|
|
{ |
292 |
|
|
if(!m_bookctrl) |
293 |
|
|
return wxSize(0,0); |
294 |
|
|
|
295 |
|
|
const wxSize sizeClient = GetClientSize(), |
296 |
|
|
sizeBorder = m_bookctrl->GetSize() - m_bookctrl->GetClientSize(), |
297 |
|
|
sizeCtrl = m_bookctrl->GetBestSize() + sizeBorder; |
298 |
|
|
|
299 |
|
|
wxSize size; |
300 |
|
|
|
301 |
|
|
if ( IsVertical() ) |
302 |
|
|
{ |
303 |
|
|
size.x = sizeClient.x; |
304 |
|
|
size.y = sizeCtrl.y; |
305 |
|
|
} |
306 |
|
|
else // left/right aligned |
307 |
|
|
{ |
308 |
|
|
size.x = sizeCtrl.x; |
309 |
|
|
size.y = sizeClient.y; |
310 |
|
|
} |
311 |
|
|
|
312 |
|
|
return size; |
313 |
|
|
} |
314 |
|
|
|
315 |
|
|
// ---------------------------------------------------------------------------- |
316 |
|
|
// miscellaneous stuff |
317 |
|
|
// ---------------------------------------------------------------------------- |
318 |
|
|
|
319 |
|
|
#if wxUSE_HELP |
320 |
|
|
|
321 |
|
|
void wxBookCtrlBase::OnHelp(wxHelpEvent& event) |
322 |
|
|
{ |
323 |
|
|
// determine where does this even originate from to avoid redirecting it |
324 |
|
|
// back to the page which generated it (resulting in an infinite loop) |
325 |
|
|
|
326 |
|
|
// notice that we have to check in the hard(er) way instead of just testing |
327 |
|
|
// if the event object == this because the book control can have other |
328 |
|
|
// subcontrols inside it (e.g. wxSpinButton in case of a notebook in wxUniv) |
329 |
|
|
wxWindow *source = wxStaticCast(event.GetEventObject(), wxWindow); |
330 |
|
|
while ( source && source != this && source->GetParent() != this ) |
331 |
|
|
{ |
332 |
|
|
source = source->GetParent(); |
333 |
|
|
} |
334 |
|
|
|
335 |
|
|
if ( source && m_pages.Index(source) == wxNOT_FOUND ) |
336 |
|
|
{ |
337 |
|
|
// this event is for the book control itself, redirect it to the |
338 |
|
|
// corresponding page |
339 |
|
|
wxWindow *page = NULL; |
340 |
|
|
|
341 |
|
|
if ( event.GetOrigin() == wxHelpEvent::Origin_HelpButton ) |
342 |
|
|
{ |
343 |
|
|
// show help for the page under the mouse |
344 |
|
|
const int pagePos = HitTest(ScreenToClient(event.GetPosition())); |
345 |
|
|
|
346 |
|
|
if ( pagePos != wxNOT_FOUND) |
347 |
|
|
{ |
348 |
|
|
page = GetPage((size_t)pagePos); |
349 |
|
|
} |
350 |
|
|
} |
351 |
|
|
else // event from keyboard or unknown source |
352 |
|
|
{ |
353 |
|
|
// otherwise show the current page help |
354 |
|
|
page = GetCurrentPage(); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
if ( page ) |
358 |
|
|
{ |
359 |
|
|
// change event object to the page to avoid infinite recursion if |
360 |
|
|
// we get this event ourselves if the page doesn't handle it |
361 |
|
|
event.SetEventObject(page); |
362 |
|
|
|
363 |
|
|
if ( page->GetEventHandler()->ProcessEvent(event) ) |
364 |
|
|
{ |
365 |
|
|
// don't call event.Skip() |
366 |
|
|
return; |
367 |
|
|
} |
368 |
|
|
} |
369 |
|
|
} |
370 |
|
|
//else: event coming from one of our pages already |
371 |
|
|
|
372 |
|
|
event.Skip(); |
373 |
|
|
} |
374 |
|
|
|
375 |
|
|
#endif // wxUSE_HELP |
376 |
|
|
|
377 |
|
|
// ---------------------------------------------------------------------------- |
378 |
|
|
// pages management |
379 |
|
|
// ---------------------------------------------------------------------------- |
380 |
|
|
|
381 |
|
|
bool |
382 |
|
|
wxBookCtrlBase::InsertPage(size_t nPage, |
383 |
|
|
wxWindow *page, |
384 |
|
|
const wxString& WXUNUSED(text), |
385 |
|
|
bool WXUNUSED(bSelect), |
386 |
|
|
int WXUNUSED(imageId)) |
387 |
|
|
{ |
388 |
|
|
wxCHECK_MSG( page || AllowNullPage(), false, |
389 |
|
|
_T("NULL page in wxBookCtrlBase::InsertPage()") ); |
390 |
|
|
wxCHECK_MSG( nPage <= m_pages.size(), false, |
391 |
|
|
_T("invalid page index in wxBookCtrlBase::InsertPage()") ); |
392 |
|
|
|
393 |
|
|
m_pages.Insert(page, nPage); |
394 |
|
|
if ( page ) |
395 |
|
|
page->SetSize(GetPageRect()); |
396 |
|
|
|
397 |
|
|
DoInvalidateBestSize(); |
398 |
|
|
|
399 |
|
|
return true; |
400 |
|
|
} |
401 |
|
|
|
402 |
|
|
bool wxBookCtrlBase::DeletePage(size_t nPage) |
403 |
|
|
{ |
404 |
|
|
wxWindow *page = DoRemovePage(nPage); |
405 |
|
|
if ( !(page || AllowNullPage()) ) |
406 |
|
|
return false; |
407 |
|
|
|
408 |
|
|
// delete NULL is harmless |
409 |
|
|
delete page; |
410 |
|
|
|
411 |
|
|
return true; |
412 |
|
|
} |
413 |
|
|
|
414 |
|
|
wxWindow *wxBookCtrlBase::DoRemovePage(size_t nPage) |
415 |
|
|
{ |
416 |
|
|
wxCHECK_MSG( nPage < m_pages.size(), NULL, |
417 |
|
|
_T("invalid page index in wxBookCtrlBase::DoRemovePage()") ); |
418 |
|
|
|
419 |
|
|
wxWindow *pageRemoved = m_pages[nPage]; |
420 |
|
|
m_pages.RemoveAt(nPage); |
421 |
|
|
DoInvalidateBestSize(); |
422 |
|
|
|
423 |
|
|
return pageRemoved; |
424 |
|
|
} |
425 |
|
|
|
426 |
|
|
int wxBookCtrlBase::GetNextPage(bool forward) const |
427 |
|
|
{ |
428 |
|
|
int nPage; |
429 |
|
|
|
430 |
|
|
int nMax = GetPageCount(); |
431 |
|
|
if ( nMax-- ) // decrement it to get the last valid index |
432 |
|
|
{ |
433 |
|
|
int nSel = GetSelection(); |
434 |
|
|
|
435 |
|
|
// change selection wrapping if it becomes invalid |
436 |
|
|
nPage = forward ? nSel == nMax ? 0 |
437 |
|
|
: nSel + 1 |
438 |
|
|
: nSel == 0 ? nMax |
439 |
|
|
: nSel - 1; |
440 |
|
|
} |
441 |
|
|
else // notebook is empty, no next page |
442 |
|
|
{ |
443 |
|
|
nPage = wxNOT_FOUND; |
444 |
|
|
} |
445 |
|
|
|
446 |
|
|
return nPage; |
447 |
|
|
} |
448 |
|
|
|
449 |
|
|
int wxBookCtrlBase::DoSetSelection(size_t n, int flags) |
450 |
|
|
{ |
451 |
|
|
wxCHECK_MSG( n < GetPageCount(), wxNOT_FOUND, |
452 |
|
|
wxT("invalid page index in wxBookCtrlBase::DoSetSelection()") ); |
453 |
|
|
|
454 |
|
|
const int oldSel = GetSelection(); |
455 |
|
|
|
456 |
|
|
if ( n != (size_t)oldSel ) |
457 |
|
|
{ |
458 |
|
|
wxBookCtrlBaseEvent *event = CreatePageChangingEvent(); |
459 |
|
|
bool allowed = false; |
460 |
|
|
|
461 |
|
|
if ( flags & SetSelection_SendEvent ) |
462 |
|
|
{ |
463 |
|
|
event->SetSelection(n); |
464 |
|
|
event->SetOldSelection(oldSel); |
465 |
|
|
event->SetEventObject(this); |
466 |
|
|
|
467 |
|
|
allowed = !GetEventHandler()->ProcessEvent(*event) || event->IsAllowed(); |
468 |
|
|
} |
469 |
|
|
|
470 |
|
|
if ( !(flags & SetSelection_SendEvent) || allowed) |
471 |
|
|
{ |
472 |
|
|
if ( oldSel != wxNOT_FOUND ) |
473 |
|
|
m_pages[oldSel]->Hide(); |
474 |
|
|
|
475 |
|
|
wxWindow *page = m_pages[n]; |
476 |
|
|
page->SetSize(GetPageRect()); |
477 |
|
|
page->Show(); |
478 |
|
|
|
479 |
|
|
// change selection now to ignore the selection change event |
480 |
|
|
UpdateSelectedPage(n); |
481 |
|
|
|
482 |
|
|
if ( flags & SetSelection_SendEvent ) |
483 |
|
|
{ |
484 |
|
|
// program allows the page change |
485 |
|
|
MakeChangedEvent(*event); |
486 |
|
|
(void)GetEventHandler()->ProcessEvent(*event); |
487 |
|
|
} |
488 |
|
|
} |
489 |
|
|
|
490 |
|
|
delete event; |
491 |
|
|
} |
492 |
|
|
|
493 |
|
|
return oldSel; |
494 |
|
|
} |
495 |
|
|
|
496 |
|
|
|
497 |
|
|
#endif // wxUSE_BOOKCTRL |