]> git.saurik.com Git - wxWidgets.git/blame - src/generic/notebook.cpp
replaced untyped hash with a typed one; 64 bit fixes (don't cast pointers to long...)
[wxWidgets.git] / src / generic / notebook.cpp
CommitLineData
1e6d9499
JS
1///////////////////////////////////////////////////////////////////////////////
2// Name: notebook.cpp
3// Purpose: implementation of wxNotebook
4// Author: Julian Smart
5// Modified by:
6// Created: 17/09/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19#ifdef __GNUG__
20#pragma implementation "notebook.h"
21#endif
22
23// For compilers that support precompilation, includes "wx.h".
24#include "wx/wxprec.h"
25
26#ifdef __BORLANDC__
27#pragma hdrstop
28#endif
29
ed58dbea
RR
30#include "wx/string.h"
31#include "wx/log.h"
32#include "wx/settings.h"
33#include "wx/generic/imaglist.h"
34#include "wx/notebook.h"
35#include "wx/dcclient.h"
00dd3b18 36#include "wx/generic/tabg.h"
1e6d9499
JS
37
38// ----------------------------------------------------------------------------
39// macros
40// ----------------------------------------------------------------------------
41
42// check that the page index is valid
43#define IS_VALID_PAGE(nPage) (((nPage) >= 0) && ((nPage) < GetPageCount()))
44
45// ----------------------------------------------------------------------------
46// event table
47// ----------------------------------------------------------------------------
48
2e4df4bf
VZ
49DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED)
50DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING)
51
1e6d9499
JS
52BEGIN_EVENT_TABLE(wxNotebook, wxControl)
53 EVT_NOTEBOOK_PAGE_CHANGED(-1, wxNotebook::OnSelChange)
54 EVT_SIZE(wxNotebook::OnSize)
55 EVT_PAINT(wxNotebook::OnPaint)
56 EVT_MOUSE_EVENTS(wxNotebook::OnMouseEvent)
57 EVT_SET_FOCUS(wxNotebook::OnSetFocus)
58 EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
1e6d9499
JS
59END_EVENT_TABLE()
60
61IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxControl)
62IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxCommandEvent)
1e6d9499
JS
63
64// ============================================================================
65// implementation
66// ============================================================================
67
00dd3b18
MB
68// ============================================================================
69// Private class
70// ============================================================================
71
72// This reuses wxTabView to draw the tabs.
73class WXDLLEXPORT wxNotebookTabView: public wxTabView
74{
75DECLARE_DYNAMIC_CLASS(wxNotebookTabView)
76public:
77 wxNotebookTabView(wxNotebook* notebook, long style = wxTAB_STYLE_DRAW_BOX | wxTAB_STYLE_COLOUR_INTERIOR);
78 ~wxNotebookTabView(void);
79
80 // Called when a tab is activated
81 virtual void OnTabActivate(int activateId, int deactivateId);
82 // Allows vetoing
83 virtual bool OnTabPreActivate(int activateId, int deactivateId);
84
85protected:
86 wxNotebook* m_notebook;
87};
88
1e6d9499
JS
89// ----------------------------------------------------------------------------
90// wxNotebook construction
91// ----------------------------------------------------------------------------
92
93// common part of all ctors
94void wxNotebook::Init()
95{
96 m_tabView = (wxNotebookTabView*) NULL;
1e6d9499
JS
97 m_nSelection = -1;
98}
99
100// default for dynamic class
101wxNotebook::wxNotebook()
102{
103 Init();
104}
105
106// the same arguments as for wxControl
107wxNotebook::wxNotebook(wxWindow *parent,
108 wxWindowID id,
109 const wxPoint& pos,
110 const wxSize& size,
111 long style,
112 const wxString& name)
113{
114 Init();
115
116 Create(parent, id, pos, size, style, name);
117}
118
119// Create() function
120bool wxNotebook::Create(wxWindow *parent,
121 wxWindowID id,
122 const wxPoint& pos,
123 const wxSize& size,
124 long style,
125 const wxString& name)
126{
127 // base init
128 SetName(name);
129
130 m_windowId = id == -1 ? NewControlId() : id;
131
132 // It's like a normal window...
f60d0f94 133 if (!wxWindow::Create(parent, id, pos, size, style|wxNO_BORDER, name))
1e6d9499
JS
134 return FALSE;
135
a756f210 136 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
f60d0f94 137
1e6d9499
JS
138 SetTabView(new wxNotebookTabView(this));
139
140 return TRUE;
141}
142
143// dtor
144wxNotebook::~wxNotebook()
145{
146 delete m_tabView;
147}
148
149// ----------------------------------------------------------------------------
150// wxNotebook accessors
151// ----------------------------------------------------------------------------
1e6d9499
JS
152int wxNotebook::GetRowCount() const
153{
154 // TODO
155 return 0;
156}
157
158int wxNotebook::SetSelection(int nPage)
159{
160 if (nPage == -1)
161 return 0;
162
163 wxASSERT( IS_VALID_PAGE(nPage) );
164
aeab10d0
JS
165#if defined (__WIN16__)
166 m_tabView->SetTabSelection(nPage);
167#else
1e6d9499
JS
168 wxNotebookPage* pPage = GetPage(nPage);
169
170 m_tabView->SetTabSelection((int) (long) pPage);
aeab10d0 171#endif
1e6d9499
JS
172 // TODO
173 return 0;
174}
175
45f22d48 176#if 0
1e6d9499
JS
177void wxNotebook::AdvanceSelection(bool bForward)
178{
179 int nSel = GetSelection();
180 int nMax = GetPageCount() - 1;
181 if ( bForward )
182 SetSelection(nSel == nMax ? 0 : nSel + 1);
183 else
184 SetSelection(nSel == 0 ? nMax : nSel - 1);
185}
45f22d48 186#endif
1e6d9499
JS
187
188bool wxNotebook::SetPageText(int nPage, const wxString& strText)
189{
190 wxASSERT( IS_VALID_PAGE(nPage) );
aeab10d0
JS
191#if defined (__WIN16__)
192 m_tabView->SetTabText(nPage, strText);
193 Refresh();
194 return TRUE;
195#else
1e6d9499
JS
196 wxNotebookPage* page = GetPage(nPage);
197 if (page)
198 {
199 m_tabView->SetTabText((int) (long) page, strText);
200 Refresh();
201 return TRUE;
202 }
aeab10d0 203#endif
1e6d9499
JS
204 return FALSE;
205}
206
207wxString wxNotebook::GetPageText(int nPage) const
208{
209 wxASSERT( IS_VALID_PAGE(nPage) );
210
aeab10d0
JS
211#if defined (__WIN16__)
212 return m_tabView->GetTabText(nPage);
213#else
1e6d9499
JS
214 wxNotebookPage* page = ((wxNotebook*)this)->GetPage(nPage);
215 if (page)
216 return m_tabView->GetTabText((int) (long) page);
217 else
218 return wxEmptyString;
aeab10d0 219#endif
1e6d9499
JS
220}
221
222int wxNotebook::GetPageImage(int nPage) const
223{
224 wxASSERT( IS_VALID_PAGE(nPage) );
225
226 // TODO
227 return 0;
228}
229
230bool wxNotebook::SetPageImage(int nPage, int nImage)
231{
232 wxASSERT( IS_VALID_PAGE(nPage) );
233
234 // TODO
235 return FALSE;
236}
237
9806a47c
JS
238// set the size (the same for all pages)
239void wxNotebook::SetPageSize(const wxSize& size)
240{
241 // TODO
242}
243
244// set the padding between tabs (in pixels)
245void wxNotebook::SetPadding(const wxSize& padding)
246{
247 // TODO
248}
249
250// set the size of the tabs for wxNB_FIXEDWIDTH controls
251void wxNotebook::SetTabSize(const wxSize& sz)
252{
253 // TODO
254}
255
1e6d9499
JS
256// ----------------------------------------------------------------------------
257// wxNotebook operations
258// ----------------------------------------------------------------------------
259
260// remove one page from the notebook and delete it
261bool wxNotebook::DeletePage(int nPage)
262{
263 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
264
265 if (m_nSelection != -1)
266 {
45f22d48
JS
267 m_pages[m_nSelection]->Show(FALSE);
268 m_pages[m_nSelection]->Lower();
1e6d9499
JS
269 }
270
271 wxNotebookPage* pPage = GetPage(nPage);
aeab10d0
JS
272#if defined (__WIN16__)
273 m_tabView->RemoveTab(nPage);
274#else
1e6d9499 275 m_tabView->RemoveTab((int) (long) pPage);
aeab10d0 276#endif
1e6d9499 277
45f22d48
JS
278 m_pages.Remove(pPage);
279 delete pPage;
1e6d9499 280
45f22d48 281 if (m_pages.GetCount() == 0)
1e6d9499
JS
282 {
283 m_nSelection = -1;
284 m_tabView->SetTabSelection(-1, FALSE);
285 }
286 else if (m_nSelection > -1)
287 {
288 m_nSelection = -1;
aeab10d0
JS
289#if defined (__WIN16__)
290 m_tabView->SetTabSelection(0, FALSE);
291#else
1e6d9499 292 m_tabView->SetTabSelection((int) (long) GetPage(0), FALSE);
aeab10d0 293#endif
1e6d9499
JS
294 if (m_nSelection != 0)
295 ChangePage(-1, 0);
296 }
297
298 RefreshLayout(FALSE);
299
300 return TRUE;
301}
302
303bool wxNotebook::DeletePage(wxNotebookPage* page)
304{
305 int pagePos = FindPagePosition(page);
306 if (pagePos > -1)
307 return DeletePage(pagePos);
308 else
309 return FALSE;
310}
311
312// remove one page from the notebook
313bool wxNotebook::RemovePage(int nPage)
314{
315 wxCHECK( IS_VALID_PAGE(nPage), FALSE );
316
45f22d48
JS
317 m_pages[nPage]->Show(FALSE);
318 // m_pages[nPage]->Lower();
1e6d9499
JS
319
320 wxNotebookPage* pPage = GetPage(nPage);
aeab10d0
JS
321#if defined (__WIN16__)
322 m_tabView->RemoveTab(nPage);
323#else
1e6d9499 324 m_tabView->RemoveTab((int) (long) pPage);
aeab10d0 325#endif
1e6d9499 326
45f22d48 327 m_pages.Remove(pPage);
1e6d9499 328
45f22d48 329 if (m_pages.GetCount() == 0)
1e6d9499
JS
330 {
331 m_nSelection = -1;
332 m_tabView->SetTabSelection(-1, TRUE);
333 }
334 else if (m_nSelection > -1)
335 {
336 // Only change the selection if the page we
337 // deleted was the selection.
338 if (nPage == m_nSelection)
339 {
340 m_nSelection = -1;
341 // Select the first tab. Generates a ChangePage.
342 m_tabView->SetTabSelection((int) (long) GetPage(0), TRUE);
343 }
344 else
345 {
346 // We must adjust which tab we think is selected.
347 // If greater than the page we deleted, it must be moved down
348 // a notch.
349 if (m_nSelection > nPage)
350 m_nSelection -- ;
351 }
352 }
353
354 RefreshLayout(FALSE);
355
356 return TRUE;
357}
358
359bool wxNotebook::RemovePage(wxNotebookPage* page)
360{
361 int pagePos = FindPagePosition(page);
362 if (pagePos > -1)
363 return RemovePage(pagePos);
364 else
365 return FALSE;
366}
367
368// Find the position of the wxNotebookPage, -1 if not found.
369int wxNotebook::FindPagePosition(wxNotebookPage* page) const
370{
371 int nPageCount = GetPageCount();
372 int nPage;
373 for ( nPage = 0; nPage < nPageCount; nPage++ )
45f22d48 374 if (m_pages[nPage] == page)
1e6d9499
JS
375 return nPage;
376 return -1;
377}
378
379// remove all pages
380bool wxNotebook::DeleteAllPages()
381{
382 m_tabView->ClearTabs(TRUE);
383
384 int nPageCount = GetPageCount();
385 int nPage;
386 for ( nPage = 0; nPage < nPageCount; nPage++ )
45f22d48 387 delete m_pages[nPage];
1e6d9499 388
45f22d48 389 m_pages.Clear();
1e6d9499
JS
390
391 return TRUE;
392}
393
1e6d9499
JS
394// same as AddPage() but does it at given position
395bool wxNotebook::InsertPage(int nPage,
396 wxNotebookPage *pPage,
397 const wxString& strText,
398 bool bSelect,
399 int imageId)
400{
401 wxASSERT( pPage != NULL );
402 wxCHECK( IS_VALID_PAGE(nPage) || nPage == GetPageCount(), FALSE );
403
aeab10d0
JS
404// For 16 bit integers (tabs limited to 32768)
405#if defined (__WIN16__)
406 m_tabView->AddTab(nPage, strText);
407#else
1e6d9499 408 m_tabView->AddTab((int) (long) pPage, strText);
aeab10d0 409#endif
1e6d9499
JS
410 if (!bSelect)
411 pPage->Show(FALSE);
412
413 // save the pointer to the page
45f22d48 414 m_pages.Insert(pPage, nPage);
1e6d9499
JS
415
416 if (bSelect)
417 {
418 // This will cause ChangePage to be called, via OnSelPage
aeab10d0
JS
419#if defined (__WIN16__)
420 m_tabView->SetTabSelection(nPage, TRUE);
421#else
1e6d9499 422 m_tabView->SetTabSelection((int) (long) pPage, TRUE);
aeab10d0 423#endif
1e6d9499
JS
424 }
425
426 // some page must be selected: either this one or the first one if there is
427 // still no selection
428 if ( m_nSelection == -1 )
429 ChangePage(-1, 0);
430
431 RefreshLayout(FALSE);
432
433 return TRUE;
434}
435
436// ----------------------------------------------------------------------------
437// wxNotebook callbacks
438// ----------------------------------------------------------------------------
439
440// @@@ OnSize() is used for setting the font when it's called for the first
441// time because doing it in ::Create() doesn't work (for unknown reasons)
442void wxNotebook::OnSize(wxSizeEvent& event)
443{
444 static bool s_bFirstTime = TRUE;
445 if ( s_bFirstTime ) {
446 // TODO: any first-time-size processing.
447 s_bFirstTime = FALSE;
448 }
449
450 RefreshLayout();
451
452 // Processing continues to next OnSize
453 event.Skip();
454}
455
456// This was supposed to cure the non-display of the notebook
457// until the user resizes the window.
458// What's going on?
5180055b 459void wxNotebook::OnInternalIdle()
1e6d9499 460{
5180055b
JS
461 wxWindow::OnInternalIdle();
462
463#if 0
1e6d9499
JS
464 static bool s_bFirstTime = TRUE;
465 if ( s_bFirstTime ) {
466 /*
467 wxSize sz(GetSize());
468 sz.x ++;
469 SetSize(sz);
470 sz.x --;
471 SetSize(sz);
472 */
473
474 /*
475 wxSize sz(GetSize());
476 wxSizeEvent sizeEvent(sz, GetId());
477 sizeEvent.SetEventObject(this);
478 GetEventHandler()->ProcessEvent(sizeEvent);
479 Refresh();
480 */
481 s_bFirstTime = FALSE;
482 }
5180055b 483#endif
1e6d9499
JS
484}
485
486// Implementation: calculate the layout of the view rect
487// and resize the children if required
488bool wxNotebook::RefreshLayout(bool force)
489{
490 if (m_tabView)
491 {
492 wxRect oldRect = m_tabView->GetViewRect();
493
494 int cw, ch;
495 GetClientSize(& cw, & ch);
496
497 int tabHeight = m_tabView->GetTotalTabHeight();
498 wxRect rect;
499 rect.x = 4;
500 rect.y = tabHeight + 4;
501 rect.width = cw - 8;
502 rect.height = ch - 4 - rect.y ;
2e4df4bf 503
1e6d9499
JS
504 m_tabView->SetViewRect(rect);
505
25889d3c 506 m_tabView->LayoutTabs();
1e6d9499
JS
507
508 // Need to do it a 2nd time to get the tab height with
509 // the new view width, since changing the view width changes the
510 // tab layout.
511 tabHeight = m_tabView->GetTotalTabHeight();
512 rect.x = 4;
513 rect.y = tabHeight + 4;
514 rect.width = cw - 8;
515 rect.height = ch - 4 - rect.y ;
2e4df4bf 516
1e6d9499
JS
517 m_tabView->SetViewRect(rect);
518
25889d3c 519 m_tabView->LayoutTabs();
1e6d9499
JS
520
521 if (!force && (rect == oldRect))
522 return FALSE;
523
524 // fit the notebook page to the tab control's display area
525
45f22d48 526 unsigned int nCount = m_pages.Count();
1e6d9499 527 for ( unsigned int nPage = 0; nPage < nCount; nPage++ ) {
45f22d48 528 wxNotebookPage *pPage = m_pages[nPage];
10d1f413 529 wxRect clientRect = GetAvailableClientSize();
1e6d9499
JS
530 if (pPage->IsShown())
531 {
1e6d9499
JS
532 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
533 if ( pPage->GetAutoLayout() )
534 pPage->Layout();
535 }
10d1f413
MB
536 // MBN: this is probably just hiding a problem under the carpet,
537 // but: with OpenMotif 2.2 (not Lesstif), not moving the window
538 // may cause the tabs to be not clickable.
539 else
540 {
541 pPage->Move(clientRect.x, clientRect.y);
542 }
1e6d9499
JS
543 }
544 Refresh();
545 }
546 return TRUE;
547}
548
549void wxNotebook::OnSelChange(wxNotebookEvent& event)
550{
551 // is it our tab control?
552 if ( event.GetEventObject() == this )
553 {
554 if (event.GetSelection() != m_nSelection)
555 ChangePage(event.GetOldSelection(), event.GetSelection());
556 }
557
558 // we want to give others a chance to process this message as well
559 event.Skip();
560}
561
562void wxNotebook::OnSetFocus(wxFocusEvent& event)
563{
564 // set focus to the currently selected page if any
565 if ( m_nSelection != -1 )
45f22d48 566 m_pages[m_nSelection]->SetFocus();
1e6d9499
JS
567
568 event.Skip();
569}
570
571void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
572{
573 if ( event.IsWindowChange() ) {
574 // change pages
575 AdvanceSelection(event.GetDirection());
576 }
577 else {
578 // pass to the parent
579 if ( GetParent() ) {
580 event.SetCurrentFocus(this);
581 GetParent()->ProcessEvent(event);
582 }
583 }
584}
585
586// ----------------------------------------------------------------------------
587// wxNotebook base class virtuals
588// ----------------------------------------------------------------------------
589
590// override these 2 functions to do nothing: everything is done in OnSize
591
592void wxNotebook::SetConstraintSizes(bool /* recurse */)
593{
594 // don't set the sizes of the pages - their correct size is not yet known
595 wxControl::SetConstraintSizes(FALSE);
596}
597
598bool wxNotebook::DoPhase(int /* nPhase */)
599{
600 return TRUE;
601}
602
af111fc3 603void wxNotebook::Command(wxCommandEvent& WXUNUSED(event))
1e6d9499
JS
604{
605 wxFAIL_MSG("wxNotebook::Command not implemented");
606}
607
608// ----------------------------------------------------------------------------
609// wxNotebook helper functions
610// ----------------------------------------------------------------------------
611
612// hide the currently active panel and show the new one
613void wxNotebook::ChangePage(int nOldSel, int nSel)
614{
615 // cout << "ChangePage: " << nOldSel << ", " << nSel << "\n";
616 wxASSERT( nOldSel != nSel ); // impossible
617
618 if ( nOldSel != -1 ) {
45f22d48
JS
619 m_pages[nOldSel]->Show(FALSE);
620 m_pages[nOldSel]->Lower();
1e6d9499
JS
621 }
622
45f22d48 623 wxNotebookPage *pPage = m_pages[nSel];
1e6d9499
JS
624
625 wxRect clientRect = GetAvailableClientSize();
626 pPage->SetSize(clientRect.x, clientRect.y, clientRect.width, clientRect.height);
627
f6bcfd97
BP
628 Refresh();
629
1e6d9499
JS
630 pPage->Show(TRUE);
631 pPage->Raise();
632 pPage->SetFocus();
633
1e6d9499
JS
634 m_nSelection = nSel;
635}
636
637void wxNotebook::OnMouseEvent(wxMouseEvent& event)
638{
639 if (m_tabView)
640 m_tabView->OnEvent(event);
641}
642
643void wxNotebook::OnPaint(wxPaintEvent& WXUNUSED(event) )
644{
645 wxPaintDC dc(this);
646 if (m_tabView)
647 m_tabView->Draw(dc);
648}
649
650wxRect wxNotebook::GetAvailableClientSize()
651{
652 int cw, ch;
653 GetClientSize(& cw, & ch);
654
655 int tabHeight = m_tabView->GetTotalTabHeight();
656
657 // TODO: these margins should be configurable.
658 wxRect rect;
659 rect.x = 6;
660 rect.y = tabHeight + 6;
661 rect.width = cw - 12;
662 rect.height = ch - 4 - rect.y ;
663
664 return rect;
665}
666
667/*
668 * wxNotebookTabView
669 */
2e4df4bf 670
1e6d9499
JS
671IMPLEMENT_CLASS(wxNotebookTabView, wxTabView)
672
673wxNotebookTabView::wxNotebookTabView(wxNotebook *notebook, long style): wxTabView(style)
674{
675 m_notebook = notebook;
676
677 m_notebook->SetTabView(this);
678
679 SetWindow(m_notebook);
680}
681
682wxNotebookTabView::~wxNotebookTabView(void)
683{
684}
685
686// Called when a tab is activated
687void wxNotebookTabView::OnTabActivate(int activateId, int deactivateId)
688{
689 if (!m_notebook)
690 return;
691
692 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_notebook->GetId());
693
aeab10d0
JS
694#if defined (__WIN16__)
695 int activatePos = activateId;
696 int deactivatePos = deactivateId;
697#else
1e6d9499
JS
698 // Translate from wxTabView's ids (which aren't position-dependent)
699 // to wxNotebook's (which are).
700 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
701 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
702
703 int activatePos = m_notebook->FindPagePosition(pActive);
704 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
705
aeab10d0 706#endif
1e6d9499
JS
707 event.SetEventObject(m_notebook);
708 event.SetSelection(activatePos);
709 event.SetOldSelection(deactivatePos);
710 m_notebook->GetEventHandler()->ProcessEvent(event);
711}
712
1c507b17
JS
713// Allows Vetoing
714bool wxNotebookTabView::OnTabPreActivate(int activateId, int deactivateId)
715{
716 bool retval = TRUE;
717
718 if (m_notebook)
719 {
720 wxNotebookEvent event(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_notebook->GetId());
721
722#if defined (__WIN16__)
723 int activatePos = activateId;
724 int deactivatePos = deactivateId;
725#else
726 // Translate from wxTabView's ids (which aren't position-dependent)
727 // to wxNotebook's (which are).
728 wxNotebookPage* pActive = (wxNotebookPage*) activateId;
729 wxNotebookPage* pDeactive = (wxNotebookPage*) deactivateId;
730
731 int activatePos = m_notebook->FindPagePosition(pActive);
732 int deactivatePos = m_notebook->FindPagePosition(pDeactive);
733
734#endif
735 event.SetEventObject(m_notebook);
736 event.SetSelection(activatePos);
737 event.SetOldSelection(deactivatePos);
738 if (m_notebook->GetEventHandler()->ProcessEvent(event))
739 {
740 retval = event.IsAllowed();
741 }
742 }
743 return retval;
744}
1e6d9499 745