1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/aui/auibook.cpp
3 // Purpose:     wxaui: wx advanced user interface - notebook
4 // Author:      Benjamin I. Williams
5 // Modified by: Jens Lody
6 // Created:     2006-06-28
7 // Copyright:   (C) Copyright 2006, Kirix Corporation, All Rights Reserved
8 // Licence:     wxWindows Library Licence, Version 3.1
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 // ----------------------------------------------------------------------------
12 // headers
13 // ----------------------------------------------------------------------------
14 
15 #include "wx/wxprec.h"
16 
17 
18 #if wxUSE_AUI
19 
20 #include "wx/aui/auibook.h"
21 
22 #ifndef WX_PRECOMP
23     #include "wx/settings.h"
24     #include "wx/dcclient.h"
25     #include "wx/dcmemory.h"
26     #include "wx/frame.h"
27 #endif
28 
29 #include "wx/aui/tabmdi.h"
30 
31 #ifdef __WXMAC__
32 #include "wx/osx/private.h"
33 #endif
34 
35 #include "wx/arrimpl.cpp"
36 WX_DEFINE_OBJARRAY(wxAuiNotebookPageArray)
37 WX_DEFINE_OBJARRAY(wxAuiTabContainerButtonArray)
38 
39 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_PAGE_CLOSE, wxAuiNotebookEvent);
40 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_PAGE_CLOSED, wxAuiNotebookEvent);
41 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_PAGE_CHANGING, wxAuiNotebookEvent);
42 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEvent);
43 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_BUTTON, wxAuiNotebookEvent);
44 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_BEGIN_DRAG, wxAuiNotebookEvent);
45 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_END_DRAG, wxAuiNotebookEvent);
46 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_CANCEL_DRAG, wxAuiNotebookEvent);
47 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_DRAG_MOTION, wxAuiNotebookEvent);
48 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_ALLOW_DND, wxAuiNotebookEvent);
49 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_BG_DCLICK, wxAuiNotebookEvent);
50 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_DRAG_DONE, wxAuiNotebookEvent);
51 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_TAB_MIDDLE_UP, wxAuiNotebookEvent);
52 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_TAB_MIDDLE_DOWN, wxAuiNotebookEvent);
53 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_TAB_RIGHT_UP, wxAuiNotebookEvent);
54 wxDEFINE_EVENT(wxEVT_AUINOTEBOOK_TAB_RIGHT_DOWN, wxAuiNotebookEvent);
55 
56 wxIMPLEMENT_CLASS(wxAuiNotebook, wxControl);
57 wxIMPLEMENT_CLASS(wxAuiTabCtrl, wxControl);
58 wxIMPLEMENT_DYNAMIC_CLASS(wxAuiNotebookEvent, wxBookCtrlEvent);
59 
60 
61 // -- wxAuiTabContainer class implementation --
62 
63 
64 // wxAuiTabContainer is a class which contains information about each
65 // tab.  It also can render an entire tab control to a specified DC.
66 // It's not a window class itself, because this code will be used by
67 // the wxFrameMananger, where it is disadvantageous to have separate
68 // windows for each tab control in the case of "docked tabs"
69 
70 // A derived class, wxAuiTabCtrl, is an actual wxWindow-derived window
71 // which can be used as a tab control in the normal sense.
72 
73 
wxAuiTabContainer()74 wxAuiTabContainer::wxAuiTabContainer()
75 {
76     m_tabOffset = 0;
77     m_flags = 0;
78     m_art = new wxAuiDefaultTabArt;
79 
80     AddButton(wxAUI_BUTTON_LEFT, wxLEFT);
81     AddButton(wxAUI_BUTTON_RIGHT, wxRIGHT);
82     AddButton(wxAUI_BUTTON_WINDOWLIST, wxRIGHT);
83     AddButton(wxAUI_BUTTON_CLOSE, wxRIGHT);
84 }
85 
~wxAuiTabContainer()86 wxAuiTabContainer::~wxAuiTabContainer()
87 {
88     delete m_art;
89 }
90 
SetArtProvider(wxAuiTabArt * art)91 void wxAuiTabContainer::SetArtProvider(wxAuiTabArt* art)
92 {
93     delete m_art;
94     m_art = art;
95 
96     if (m_art)
97     {
98         m_art->SetFlags(m_flags);
99     }
100 }
101 
GetArtProvider() const102 wxAuiTabArt* wxAuiTabContainer::GetArtProvider() const
103 {
104     return m_art;
105 }
106 
SetFlags(unsigned int flags)107 void wxAuiTabContainer::SetFlags(unsigned int flags)
108 {
109     m_flags = flags;
110 
111     // check for new close button settings
112     RemoveButton(wxAUI_BUTTON_LEFT);
113     RemoveButton(wxAUI_BUTTON_RIGHT);
114     RemoveButton(wxAUI_BUTTON_WINDOWLIST);
115     RemoveButton(wxAUI_BUTTON_CLOSE);
116 
117 
118     if (flags & wxAUI_NB_SCROLL_BUTTONS)
119     {
120         AddButton(wxAUI_BUTTON_LEFT, wxLEFT);
121         AddButton(wxAUI_BUTTON_RIGHT, wxRIGHT);
122     }
123 
124     if (flags & wxAUI_NB_WINDOWLIST_BUTTON)
125     {
126         AddButton(wxAUI_BUTTON_WINDOWLIST, wxRIGHT);
127     }
128 
129     if (flags & wxAUI_NB_CLOSE_BUTTON)
130     {
131         AddButton(wxAUI_BUTTON_CLOSE, wxRIGHT);
132     }
133 
134     if (m_art)
135     {
136         m_art->SetFlags(m_flags);
137     }
138 }
139 
GetFlags() const140 unsigned int wxAuiTabContainer::GetFlags() const
141 {
142     return m_flags;
143 }
144 
145 
SetNormalFont(const wxFont & font)146 void wxAuiTabContainer::SetNormalFont(const wxFont& font)
147 {
148     m_art->SetNormalFont(font);
149 }
150 
SetSelectedFont(const wxFont & font)151 void wxAuiTabContainer::SetSelectedFont(const wxFont& font)
152 {
153     m_art->SetSelectedFont(font);
154 }
155 
SetMeasuringFont(const wxFont & font)156 void wxAuiTabContainer::SetMeasuringFont(const wxFont& font)
157 {
158     m_art->SetMeasuringFont(font);
159 }
160 
SetColour(const wxColour & colour)161 void wxAuiTabContainer::SetColour(const wxColour& colour)
162 {
163     m_art->SetColour(colour);
164 }
165 
SetActiveColour(const wxColour & colour)166 void wxAuiTabContainer::SetActiveColour(const wxColour& colour)
167 {
168     m_art->SetActiveColour(colour);
169 }
170 
SetRect(const wxRect & rect)171 void wxAuiTabContainer::SetRect(const wxRect& rect)
172 {
173     m_rect = rect;
174 
175     if (m_art)
176     {
177         m_art->SetSizingInfo(rect.GetSize(), m_pages.GetCount());
178     }
179 }
180 
AddPage(wxWindow * page,const wxAuiNotebookPage & info)181 bool wxAuiTabContainer::AddPage(wxWindow* page,
182                                 const wxAuiNotebookPage& info)
183 {
184     wxAuiNotebookPage page_info;
185     page_info = info;
186     page_info.window = page;
187     page_info.hover = false;
188 
189     m_pages.Add(page_info);
190 
191     // let the art provider know how many pages we have
192     if (m_art)
193     {
194         m_art->SetSizingInfo(m_rect.GetSize(), m_pages.GetCount());
195     }
196 
197     return true;
198 }
199 
InsertPage(wxWindow * page,const wxAuiNotebookPage & info,size_t idx)200 bool wxAuiTabContainer::InsertPage(wxWindow* page,
201                                    const wxAuiNotebookPage& info,
202                                    size_t idx)
203 {
204     wxAuiNotebookPage page_info;
205     page_info = info;
206     page_info.window = page;
207     page_info.hover = false;
208 
209     if (idx >= m_pages.GetCount())
210         m_pages.Add(page_info);
211     else
212         m_pages.Insert(page_info, idx);
213 
214     // let the art provider know how many pages we have
215     if (m_art)
216     {
217         m_art->SetSizingInfo(m_rect.GetSize(), m_pages.GetCount());
218     }
219 
220     return true;
221 }
222 
MovePage(wxWindow * page,size_t new_idx)223 bool wxAuiTabContainer::MovePage(wxWindow* page,
224                                  size_t new_idx)
225 {
226     int idx = GetIdxFromWindow(page);
227     if (idx == -1)
228         return false;
229 
230     // get page entry, make a copy of it
231     wxAuiNotebookPage p = GetPage(idx);
232 
233     // remove old page entry
234     RemovePage(page);
235 
236     // insert page where it should be
237     InsertPage(page, p, new_idx);
238 
239     return true;
240 }
241 
RemovePage(wxWindow * wnd)242 bool wxAuiTabContainer::RemovePage(wxWindow* wnd)
243 {
244     size_t i, page_count = m_pages.GetCount();
245     for (i = 0; i < page_count; ++i)
246     {
247         wxAuiNotebookPage& page = m_pages.Item(i);
248         if (page.window == wnd)
249         {
250             m_pages.RemoveAt(i);
251 
252             // let the art provider know how many pages we have
253             if (m_art)
254             {
255                 m_art->SetSizingInfo(m_rect.GetSize(), m_pages.GetCount());
256             }
257 
258             return true;
259         }
260     }
261 
262     return false;
263 }
264 
SetActivePage(wxWindow * wnd)265 bool wxAuiTabContainer::SetActivePage(wxWindow* wnd)
266 {
267     bool found = false;
268 
269     size_t i, page_count = m_pages.GetCount();
270     for (i = 0; i < page_count; ++i)
271     {
272         wxAuiNotebookPage& page = m_pages.Item(i);
273         if (page.window == wnd)
274         {
275             page.active = true;
276             found = true;
277         }
278         else
279         {
280             page.active = false;
281         }
282     }
283 
284     return found;
285 }
286 
SetNoneActive()287 void wxAuiTabContainer::SetNoneActive()
288 {
289     size_t i, page_count = m_pages.GetCount();
290     for (i = 0; i < page_count; ++i)
291     {
292         wxAuiNotebookPage& page = m_pages.Item(i);
293         page.active = false;
294     }
295 }
296 
SetActivePage(size_t page)297 bool wxAuiTabContainer::SetActivePage(size_t page)
298 {
299     if (page >= m_pages.GetCount())
300         return false;
301 
302     return SetActivePage(m_pages.Item(page).window);
303 }
304 
GetActivePage() const305 int wxAuiTabContainer::GetActivePage() const
306 {
307     size_t i, page_count = m_pages.GetCount();
308     for (i = 0; i < page_count; ++i)
309     {
310         wxAuiNotebookPage& page = m_pages.Item(i);
311         if (page.active)
312             return i;
313     }
314 
315     return -1;
316 }
317 
GetWindowFromIdx(size_t idx) const318 wxWindow* wxAuiTabContainer::GetWindowFromIdx(size_t idx) const
319 {
320     if (idx >= m_pages.GetCount())
321         return NULL;
322 
323     return m_pages[idx].window;
324 }
325 
GetIdxFromWindow(wxWindow * wnd) const326 int wxAuiTabContainer::GetIdxFromWindow(wxWindow* wnd) const
327 {
328     const size_t page_count = m_pages.GetCount();
329     for ( size_t i = 0; i < page_count; ++i )
330     {
331         wxAuiNotebookPage& page = m_pages.Item(i);
332         if (page.window == wnd)
333             return i;
334     }
335     return wxNOT_FOUND;
336 }
337 
GetPage(size_t idx)338 wxAuiNotebookPage& wxAuiTabContainer::GetPage(size_t idx)
339 {
340     wxASSERT_MSG(idx < m_pages.GetCount(), wxT("Invalid Page index"));
341 
342     return m_pages[idx];
343 }
344 
GetPage(size_t idx) const345 const wxAuiNotebookPage& wxAuiTabContainer::GetPage(size_t idx) const
346 {
347     wxASSERT_MSG(idx < m_pages.GetCount(), wxT("Invalid Page index"));
348 
349     return m_pages[idx];
350 }
351 
GetPages()352 wxAuiNotebookPageArray& wxAuiTabContainer::GetPages()
353 {
354     return m_pages;
355 }
356 
GetPageCount() const357 size_t wxAuiTabContainer::GetPageCount() const
358 {
359     return m_pages.GetCount();
360 }
361 
AddButton(int id,int location,const wxBitmap & normalBitmap,const wxBitmap & disabledBitmap)362 void wxAuiTabContainer::AddButton(int id,
363                                   int location,
364                                   const wxBitmap& normalBitmap,
365                                   const wxBitmap& disabledBitmap)
366 {
367     wxAuiTabContainerButton button;
368     button.id = id;
369     button.bitmap = normalBitmap;
370     button.disBitmap = disabledBitmap;
371     button.location = location;
372     button.curState = wxAUI_BUTTON_STATE_NORMAL;
373 
374     m_buttons.Add(button);
375 }
376 
RemoveButton(int id)377 void wxAuiTabContainer::RemoveButton(int id)
378 {
379     size_t i, button_count = m_buttons.GetCount();
380 
381     for (i = 0; i < button_count; ++i)
382     {
383         if (m_buttons.Item(i).id == id)
384         {
385             m_buttons.RemoveAt(i);
386             return;
387         }
388     }
389 }
390 
391 
392 
GetTabOffset() const393 size_t wxAuiTabContainer::GetTabOffset() const
394 {
395     return m_tabOffset;
396 }
397 
SetTabOffset(size_t offset)398 void wxAuiTabContainer::SetTabOffset(size_t offset)
399 {
400     m_tabOffset = offset;
401 }
402 
403 
404 
405 
406 // Render() renders the tab catalog to the specified DC
407 // It is a virtual function and can be overridden to
408 // provide custom drawing capabilities
Render(wxDC * raw_dc,wxWindow * wnd)409 void wxAuiTabContainer::Render(wxDC* raw_dc, wxWindow* wnd)
410 {
411     if (!raw_dc || !raw_dc->IsOk())
412         return;
413 
414     if (m_rect.IsEmpty())
415         return;
416 
417     wxMemoryDC dc;
418 
419     // use the same layout direction as the window DC uses to ensure that the
420     // text is rendered correctly
421     dc.SetLayoutDirection(raw_dc->GetLayoutDirection());
422 
423     wxBitmap bmp;
424     size_t i;
425     size_t page_count = m_pages.GetCount();
426     size_t button_count = m_buttons.GetCount();
427 
428     // create off-screen bitmap
429     bmp.Create(m_rect.GetWidth(), m_rect.GetHeight(),*raw_dc);
430     dc.SelectObject(bmp);
431 
432     if (!dc.IsOk())
433         return;
434 
435     // ensure we show as many tabs as possible
436     while (m_tabOffset > 0 && IsTabVisible(page_count-1, m_tabOffset-1, &dc, wnd))
437         --m_tabOffset;
438 
439     // find out if size of tabs is larger than can be
440     // afforded on screen
441     int total_width = 0;
442     int visible_width = 0;
443     for (i = 0; i < page_count; ++i)
444     {
445         wxAuiNotebookPage& page = m_pages.Item(i);
446 
447         // determine if a close button is on this tab
448         bool close_button = false;
449         if ((m_flags & wxAUI_NB_CLOSE_ON_ALL_TABS) != 0 ||
450             ((m_flags & wxAUI_NB_CLOSE_ON_ACTIVE_TAB) != 0 && page.active))
451         {
452             close_button = true;
453         }
454 
455 
456         int x_extent = 0;
457         wxSize size = m_art->GetTabSize(dc,
458                             wnd,
459                             page.caption,
460                             page.bitmap,
461                             page.active,
462                             close_button ?
463                               wxAUI_BUTTON_STATE_NORMAL :
464                               wxAUI_BUTTON_STATE_HIDDEN,
465                             &x_extent);
466 
467         if (i+1 < page_count)
468             total_width += x_extent;
469         else
470             total_width += size.x;
471 
472         if (i >= m_tabOffset)
473         {
474             if (i+1 < page_count)
475                 visible_width += x_extent;
476             else
477                 visible_width += size.x;
478         }
479     }
480 
481     if (total_width > m_rect.GetWidth() || m_tabOffset != 0)
482     {
483         // show left/right buttons
484         for (i = 0; i < button_count; ++i)
485         {
486             wxAuiTabContainerButton& button = m_buttons.Item(i);
487             if (button.id == wxAUI_BUTTON_LEFT ||
488                 button.id == wxAUI_BUTTON_RIGHT)
489             {
490                 button.curState &= ~wxAUI_BUTTON_STATE_HIDDEN;
491             }
492         }
493     }
494     else
495     {
496         // hide left/right buttons
497         for (i = 0; i < button_count; ++i)
498         {
499             wxAuiTabContainerButton& button = m_buttons.Item(i);
500             if (button.id == wxAUI_BUTTON_LEFT ||
501                 button.id == wxAUI_BUTTON_RIGHT)
502             {
503                 button.curState |= wxAUI_BUTTON_STATE_HIDDEN;
504             }
505         }
506     }
507 
508     // determine whether left button should be enabled
509     for (i = 0; i < button_count; ++i)
510     {
511         wxAuiTabContainerButton& button = m_buttons.Item(i);
512         if (button.id == wxAUI_BUTTON_LEFT)
513         {
514             if (m_tabOffset == 0)
515                 button.curState |= wxAUI_BUTTON_STATE_DISABLED;
516             else
517                 button.curState &= ~wxAUI_BUTTON_STATE_DISABLED;
518         }
519         if (button.id == wxAUI_BUTTON_RIGHT)
520         {
521             int button_width = 0;
522             for (i = 0; i < button_count; ++i)
523                 button_width += m_buttons.Item(button_count - i - 1).rect.GetWidth();
524 
525             if (visible_width < m_rect.GetWidth() - button_width)
526                 button.curState |= wxAUI_BUTTON_STATE_DISABLED;
527             else
528                 button.curState &= ~wxAUI_BUTTON_STATE_DISABLED;
529         }
530     }
531 
532 
533 
534     // draw background
535     m_art->DrawBackground(dc, wnd, m_rect);
536 
537     // draw buttons
538     int left_buttons_width = 0;
539     int right_buttons_width = 0;
540 
541     // draw the buttons on the right side
542     int offset = m_rect.x + m_rect.width;
543     for (i = 0; i < button_count; ++i)
544     {
545         wxAuiTabContainerButton& button = m_buttons.Item(button_count - i - 1);
546 
547         if (button.location != wxRIGHT)
548             continue;
549         if (button.curState & wxAUI_BUTTON_STATE_HIDDEN)
550             continue;
551 
552         wxRect button_rect = m_rect;
553         button_rect.SetY(1);
554         button_rect.SetWidth(offset);
555 
556         m_art->DrawButton(dc,
557                           wnd,
558                           button_rect,
559                           button.id,
560                           button.curState,
561                           wxRIGHT,
562                           &button.rect);
563 
564         offset -= button.rect.GetWidth();
565         right_buttons_width += button.rect.GetWidth();
566     }
567 
568 
569 
570     offset = 0;
571 
572     // draw the buttons on the left side
573 
574     for (i = 0; i < button_count; ++i)
575     {
576         wxAuiTabContainerButton& button = m_buttons.Item(button_count - i - 1);
577 
578         if (button.location != wxLEFT)
579             continue;
580         if (button.curState & wxAUI_BUTTON_STATE_HIDDEN)
581             continue;
582 
583         wxRect button_rect(offset, 1, 1000, m_rect.height);
584 
585         m_art->DrawButton(dc,
586                           wnd,
587                           button_rect,
588                           button.id,
589                           button.curState,
590                           wxLEFT,
591                           &button.rect);
592 
593         offset += button.rect.GetWidth();
594         left_buttons_width += button.rect.GetWidth();
595     }
596 
597     offset = left_buttons_width;
598 
599     if (offset == 0)
600         offset += m_art->GetIndentSize();
601 
602 
603     // prepare the tab-close-button array
604     // make sure tab button entries which aren't used are marked as hidden
605     for (i = page_count; i < m_tabCloseButtons.GetCount(); ++i)
606         m_tabCloseButtons.Item(i).curState = wxAUI_BUTTON_STATE_HIDDEN;
607 
608     // make sure there are enough tab button entries to accommodate all tabs
609     while (m_tabCloseButtons.GetCount() < page_count)
610     {
611         wxAuiTabContainerButton tempbtn;
612         tempbtn.id = wxAUI_BUTTON_CLOSE;
613         tempbtn.location = wxCENTER;
614         tempbtn.curState = wxAUI_BUTTON_STATE_HIDDEN;
615         m_tabCloseButtons.Add(tempbtn);
616     }
617 
618 
619     // buttons before the tab offset must be set to hidden
620     for (i = 0; i < m_tabOffset; ++i)
621     {
622         m_tabCloseButtons.Item(i).curState = wxAUI_BUTTON_STATE_HIDDEN;
623     }
624 
625 
626     // draw the tabs
627 
628     size_t active = 999;
629     int active_offset = 0;
630     wxRect active_rect;
631 
632     int x_extent = 0;
633     wxRect rect = m_rect;
634     rect.y = 0;
635     rect.height = m_rect.height;
636 
637     for (i = m_tabOffset; i < page_count; ++i)
638     {
639         wxAuiNotebookPage& page = m_pages.Item(i);
640         wxAuiTabContainerButton& tab_button = m_tabCloseButtons.Item(i);
641 
642         // determine if a close button is on this tab
643         if ((m_flags & wxAUI_NB_CLOSE_ON_ALL_TABS) != 0 ||
644             ((m_flags & wxAUI_NB_CLOSE_ON_ACTIVE_TAB) != 0 && page.active))
645         {
646             if (tab_button.curState == wxAUI_BUTTON_STATE_HIDDEN)
647             {
648                 tab_button.id = wxAUI_BUTTON_CLOSE;
649                 tab_button.curState = wxAUI_BUTTON_STATE_NORMAL;
650                 tab_button.location = wxCENTER;
651             }
652         }
653         else
654         {
655             tab_button.curState = wxAUI_BUTTON_STATE_HIDDEN;
656         }
657 
658         rect.x = offset;
659         rect.width = m_rect.width - right_buttons_width - offset - wnd->FromDIP(2);
660 
661         if (rect.width <= 0)
662             break;
663 
664         m_art->DrawTab(dc,
665                        wnd,
666                        page,
667                        rect,
668                        tab_button.curState,
669                        &page.rect,
670                        &tab_button.rect,
671                        &x_extent);
672 
673         if (page.active)
674         {
675             active = i;
676             active_offset = offset;
677             active_rect = rect;
678         }
679 
680         offset += x_extent;
681     }
682 
683 
684     // make sure to deactivate buttons which are off the screen to the right
685     for (++i; i < m_tabCloseButtons.GetCount(); ++i)
686     {
687         m_tabCloseButtons.Item(i).curState = wxAUI_BUTTON_STATE_HIDDEN;
688     }
689 
690 
691     // draw the active tab again so it stands in the foreground
692     if (active >= m_tabOffset && active < m_pages.GetCount())
693     {
694         wxAuiNotebookPage& page = m_pages.Item(active);
695 
696         wxAuiTabContainerButton& tab_button = m_tabCloseButtons.Item(active);
697 
698         rect.x = active_offset;
699         m_art->DrawTab(dc,
700                        wnd,
701                        page,
702                        active_rect,
703                        tab_button.curState,
704                        &page.rect,
705                        &tab_button.rect,
706                        &x_extent);
707     }
708 
709 
710     raw_dc->Blit(m_rect.x, m_rect.y,
711                  m_rect.GetWidth(), m_rect.GetHeight(),
712                  &dc, 0, 0);
713 }
714 
715 // Is the tab visible?
IsTabVisible(int tabPage,int tabOffset,wxDC * dc,wxWindow * wnd)716 bool wxAuiTabContainer::IsTabVisible(int tabPage, int tabOffset, wxDC* dc, wxWindow* wnd)
717 {
718     if (!dc || !dc->IsOk())
719         return false;
720 
721     size_t i;
722     size_t page_count = m_pages.GetCount();
723     size_t button_count = m_buttons.GetCount();
724 
725     // Hasn't been rendered yet; assume it's visible
726     if (m_tabCloseButtons.GetCount() < page_count)
727         return true;
728 
729     // First check if both buttons are disabled - if so, there's no need to
730     // check further for visibility.
731     int arrowButtonVisibleCount = 0;
732     for (i = 0; i < button_count; ++i)
733     {
734         wxAuiTabContainerButton& button = m_buttons.Item(i);
735         if (button.id == wxAUI_BUTTON_LEFT ||
736             button.id == wxAUI_BUTTON_RIGHT)
737         {
738             if ((button.curState & wxAUI_BUTTON_STATE_HIDDEN) == 0)
739                 arrowButtonVisibleCount ++;
740         }
741     }
742 
743     // Tab must be visible
744     if (arrowButtonVisibleCount == 0)
745         return true;
746 
747     // If tab is less than the given offset, it must be invisible by definition
748     if (tabPage < tabOffset)
749         return false;
750 
751     // draw buttons
752     int left_buttons_width = 0;
753     int right_buttons_width = 0;
754 
755     // calculate size of the buttons on the right side
756     int offset = m_rect.x + m_rect.width;
757     for (i = 0; i < button_count; ++i)
758     {
759         wxAuiTabContainerButton& button = m_buttons.Item(button_count - i - 1);
760 
761         if (button.location != wxRIGHT)
762             continue;
763         if (button.curState & wxAUI_BUTTON_STATE_HIDDEN)
764             continue;
765 
766         offset -= button.rect.GetWidth();
767         right_buttons_width += button.rect.GetWidth();
768     }
769 
770     offset = 0;
771 
772     // calculate size of the buttons on the left side
773     for (i = 0; i < button_count; ++i)
774     {
775         wxAuiTabContainerButton& button = m_buttons.Item(button_count - i - 1);
776 
777         if (button.location != wxLEFT)
778             continue;
779         if (button.curState & wxAUI_BUTTON_STATE_HIDDEN)
780             continue;
781 
782         offset += button.rect.GetWidth();
783         left_buttons_width += button.rect.GetWidth();
784     }
785 
786     offset = left_buttons_width;
787 
788     if (offset == 0)
789         offset += m_art->GetIndentSize();
790 
791     wxRect rect = m_rect;
792 
793     // See if the given page is visible at the given tab offset (effectively scroll position)
794     for (i = tabOffset; i < page_count; ++i)
795     {
796         wxAuiNotebookPage& page = m_pages.Item(i);
797         wxAuiTabContainerButton& tab_button = m_tabCloseButtons.Item(i);
798 
799         rect.width = m_rect.width - right_buttons_width - offset - wnd->FromDIP(2);
800 
801         if (rect.width <= 0)
802             return false; // haven't found the tab, and we've run out of space, so return false
803 
804         int x_extent = 0;
805         m_art->GetTabSize(*dc,
806                             wnd,
807                             page.caption,
808                             page.bitmap,
809                             page.active,
810                             tab_button.curState,
811                             &x_extent);
812 
813         offset += x_extent;
814 
815         if (i == (size_t) tabPage)
816         {
817             // If not all of the tab is visible, and supposing there's space to display it all,
818             // we could do better so we return false.
819             if (((m_rect.width - right_buttons_width - offset - wnd->FromDIP(2)) <= 0) && ((m_rect.width - right_buttons_width - left_buttons_width) > x_extent))
820                 return false;
821             else
822                 return true;
823         }
824     }
825 
826     // Shouldn't really get here, but if it does, assume the tab is visible to prevent
827     // further looping in calling code.
828     return true;
829 }
830 
831 // Make the tab visible if it wasn't already
MakeTabVisible(int tabPage,wxWindow * win)832 void wxAuiTabContainer::MakeTabVisible(int tabPage, wxWindow* win)
833 {
834     wxClientDC dc(win);
835     if (!IsTabVisible(tabPage, GetTabOffset(), & dc, win))
836     {
837         int i;
838         for (i = 0; i < (int) m_pages.GetCount(); i++)
839         {
840             if (IsTabVisible(tabPage, i, & dc, win))
841             {
842                 SetTabOffset(i);
843                 win->Refresh();
844                 return;
845             }
846         }
847     }
848 }
849 
850 // TabHitTest() tests if a tab was hit, passing the window pointer
851 // back if that condition was fulfilled.  The function returns
852 // true if a tab was hit, otherwise false
TabHitTest(int x,int y,wxWindow ** hit) const853 bool wxAuiTabContainer::TabHitTest(int x, int y, wxWindow** hit) const
854 {
855     if (!m_rect.Contains(x,y))
856         return false;
857 
858     wxAuiTabContainerButton* btn = NULL;
859     if (ButtonHitTest(x, y, &btn) && !(btn->curState & wxAUI_BUTTON_STATE_DISABLED))
860     {
861         if (m_buttons.Index(*btn) != wxNOT_FOUND)
862             return false;
863     }
864 
865     size_t i, page_count = m_pages.GetCount();
866 
867     for (i = m_tabOffset; i < page_count; ++i)
868     {
869         wxAuiNotebookPage& page = m_pages.Item(i);
870         if (page.rect.Contains(x,y))
871         {
872             if (hit)
873                 *hit = page.window;
874             return true;
875         }
876     }
877 
878     return false;
879 }
880 
881 // ButtonHitTest() tests if a button was hit. The function returns
882 // true if a button was hit, otherwise false
ButtonHitTest(int x,int y,wxAuiTabContainerButton ** hit) const883 bool wxAuiTabContainer::ButtonHitTest(int x, int y,
884                                       wxAuiTabContainerButton** hit) const
885 {
886     if (!m_rect.Contains(x,y))
887         return false;
888 
889     size_t i, button_count;
890 
891 
892     button_count = m_buttons.GetCount();
893     for (i = 0; i < button_count; ++i)
894     {
895         wxAuiTabContainerButton& button = m_buttons.Item(i);
896         if (button.rect.Contains(x,y) &&
897             !(button.curState & wxAUI_BUTTON_STATE_HIDDEN ))
898         {
899             if (hit)
900                 *hit = &button;
901             return true;
902         }
903     }
904 
905     button_count = m_tabCloseButtons.GetCount();
906     for (i = 0; i < button_count; ++i)
907     {
908         wxAuiTabContainerButton& button = m_tabCloseButtons.Item(i);
909         if (button.rect.Contains(x,y) &&
910             !(button.curState & (wxAUI_BUTTON_STATE_HIDDEN |
911                                    wxAUI_BUTTON_STATE_DISABLED)))
912         {
913             if (hit)
914                 *hit = &button;
915             return true;
916         }
917     }
918 
919     return false;
920 }
921 
922 
923 
924 // the utility function ShowWnd() is the same as show,
925 // except it handles wxAuiMDIChildFrame windows as well,
926 // as the Show() method on this class is "unplugged"
ShowWnd(wxWindow * wnd,bool show)927 static void ShowWnd(wxWindow* wnd, bool show)
928 {
929 #if wxUSE_MDI
930     if (wxDynamicCast(wnd, wxAuiMDIChildFrame))
931     {
932         wxAuiMDIChildFrame* cf = (wxAuiMDIChildFrame*)wnd;
933         cf->wxWindow::Show(show);
934     }
935     else
936 #endif
937     {
938         wnd->Show(show);
939     }
940 }
941 
942 
943 // DoShowHide() this function shows the active window, then
944 // hides all of the other windows (in that order)
DoShowHide()945 void wxAuiTabContainer::DoShowHide()
946 {
947     wxAuiNotebookPageArray& pages = GetPages();
948     size_t i, page_count = pages.GetCount();
949 
950     // show new active page first
951     for (i = 0; i < page_count; ++i)
952     {
953         wxAuiNotebookPage& page = pages.Item(i);
954         if (page.active)
955         {
956             ShowWnd(page.window, true);
957             break;
958         }
959     }
960 
961     // hide all other pages
962     for (i = 0; i < page_count; ++i)
963     {
964         wxAuiNotebookPage& page = pages.Item(i);
965         if (!page.active)
966             ShowWnd(page.window, false);
967     }
968 }
969 
970 
971 
972 
973 
974 
975 // -- wxAuiTabCtrl class implementation --
976 
977 
978 
wxBEGIN_EVENT_TABLE(wxAuiTabCtrl,wxControl)979 wxBEGIN_EVENT_TABLE(wxAuiTabCtrl, wxControl)
980     EVT_PAINT(wxAuiTabCtrl::OnPaint)
981     EVT_ERASE_BACKGROUND(wxAuiTabCtrl::OnEraseBackground)
982     EVT_SIZE(wxAuiTabCtrl::OnSize)
983     EVT_LEFT_DOWN(wxAuiTabCtrl::OnLeftDown)
984     EVT_LEFT_DCLICK(wxAuiTabCtrl::OnLeftDClick)
985     EVT_LEFT_UP(wxAuiTabCtrl::OnLeftUp)
986     EVT_MIDDLE_DOWN(wxAuiTabCtrl::OnMiddleDown)
987     EVT_MIDDLE_UP(wxAuiTabCtrl::OnMiddleUp)
988     EVT_RIGHT_DOWN(wxAuiTabCtrl::OnRightDown)
989     EVT_RIGHT_UP(wxAuiTabCtrl::OnRightUp)
990     EVT_MOTION(wxAuiTabCtrl::OnMotion)
991     EVT_LEAVE_WINDOW(wxAuiTabCtrl::OnLeaveWindow)
992     EVT_AUINOTEBOOK_BUTTON(wxID_ANY, wxAuiTabCtrl::OnButton)
993     EVT_SET_FOCUS(wxAuiTabCtrl::OnSetFocus)
994     EVT_KILL_FOCUS(wxAuiTabCtrl::OnKillFocus)
995     EVT_CHAR(wxAuiTabCtrl::OnChar)
996     EVT_MOUSE_CAPTURE_LOST(wxAuiTabCtrl::OnCaptureLost)
997     EVT_SYS_COLOUR_CHANGED(wxAuiTabCtrl::OnSysColourChanged)
998 wxEND_EVENT_TABLE()
999 
1000 
1001 wxAuiTabCtrl::wxAuiTabCtrl(wxWindow* parent,
1002                            wxWindowID id,
1003                            const wxPoint& pos,
1004                            const wxSize& size,
1005                            long style) : wxControl(parent, id, pos, size, style)
1006 {
1007     SetName(wxT("wxAuiTabCtrl"));
1008     m_clickPt = wxDefaultPosition;
1009     m_isDragging = false;
1010     m_hoverButton = NULL;
1011     m_pressedButton = NULL;
1012 }
1013 
~wxAuiTabCtrl()1014 wxAuiTabCtrl::~wxAuiTabCtrl()
1015 {
1016 }
1017 
OnPaint(wxPaintEvent &)1018 void wxAuiTabCtrl::OnPaint(wxPaintEvent&)
1019 {
1020     wxPaintDC dc(this);
1021 
1022     dc.SetFont(GetFont());
1023 
1024     if (GetPageCount() > 0)
1025         Render(&dc, this);
1026 }
1027 
OnSysColourChanged(wxSysColourChangedEvent & event)1028 void wxAuiTabCtrl::OnSysColourChanged(wxSysColourChangedEvent &event)
1029 {
1030     event.Skip();
1031 
1032     if (m_art)
1033     {
1034         m_art->UpdateColoursFromSystem();
1035     }
1036 }
1037 
OnEraseBackground(wxEraseEvent & WXUNUSED (evt))1038 void wxAuiTabCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
1039 {
1040 }
1041 
OnSize(wxSizeEvent & evt)1042 void wxAuiTabCtrl::OnSize(wxSizeEvent& evt)
1043 {
1044     wxSize s = evt.GetSize();
1045     wxRect r(0, 0, s.GetWidth(), s.GetHeight());
1046     SetRect(r);
1047 }
1048 
OnLeftDown(wxMouseEvent & evt)1049 void wxAuiTabCtrl::OnLeftDown(wxMouseEvent& evt)
1050 {
1051     CaptureMouse();
1052     m_clickPt = wxDefaultPosition;
1053     m_isDragging = false;
1054     m_clickTab = NULL;
1055     m_pressedButton = NULL;
1056 
1057 
1058     wxWindow* wnd;
1059     if (TabHitTest(evt.m_x, evt.m_y, &wnd))
1060     {
1061         int new_selection = GetIdxFromWindow(wnd);
1062 
1063         // wxAuiNotebooks always want to receive this event
1064         // even if the tab is already active, because they may
1065         // have multiple tab controls
1066         if ((new_selection != GetActivePage() ||
1067             wxDynamicCast(GetParent(), wxAuiNotebook)) && !m_hoverButton)
1068         {
1069             wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_PAGE_CHANGING, m_windowId);
1070             e.SetSelection(new_selection);
1071             e.SetOldSelection(GetActivePage());
1072             e.SetEventObject(this);
1073             GetEventHandler()->ProcessEvent(e);
1074         }
1075 
1076         m_clickPt.x = evt.m_x;
1077         m_clickPt.y = evt.m_y;
1078         m_clickTab = wnd;
1079     }
1080 
1081     if (m_hoverButton)
1082     {
1083         m_pressedButton = m_hoverButton;
1084         m_pressedButton->curState = wxAUI_BUTTON_STATE_PRESSED;
1085         Refresh();
1086         Update();
1087     }
1088 }
1089 
OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED (event))1090 void wxAuiTabCtrl::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
1091 {
1092     if (m_isDragging)
1093     {
1094         m_isDragging = false;
1095 
1096         wxAuiNotebookEvent evt(wxEVT_AUINOTEBOOK_CANCEL_DRAG, m_windowId);
1097         evt.SetSelection(GetIdxFromWindow(m_clickTab));
1098         evt.SetOldSelection(evt.GetSelection());
1099         evt.SetEventObject(this);
1100         GetEventHandler()->ProcessEvent(evt);
1101     }
1102 }
1103 
OnLeftUp(wxMouseEvent & evt)1104 void wxAuiTabCtrl::OnLeftUp(wxMouseEvent& evt)
1105 {
1106     if (GetCapture() == this)
1107         ReleaseMouse();
1108 
1109     if (m_isDragging)
1110     {
1111         m_isDragging = false;
1112 
1113         wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_END_DRAG, m_windowId);
1114         e.SetSelection(GetIdxFromWindow(m_clickTab));
1115         e.SetOldSelection(e.GetSelection());
1116         e.SetEventObject(this);
1117         GetEventHandler()->ProcessEvent(e);
1118 
1119         return;
1120     }
1121 
1122     if (m_pressedButton)
1123     {
1124         // make sure we're still clicking the button
1125         wxAuiTabContainerButton* button = NULL;
1126         if (!ButtonHitTest(evt.m_x, evt.m_y, &button) ||
1127             button->curState & wxAUI_BUTTON_STATE_DISABLED)
1128             return;
1129 
1130         if (button != m_pressedButton)
1131         {
1132             m_pressedButton = NULL;
1133             return;
1134         }
1135 
1136         Refresh();
1137         Update();
1138 
1139         if (!(m_pressedButton->curState & wxAUI_BUTTON_STATE_DISABLED))
1140         {
1141             wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_BUTTON, m_windowId);
1142             e.SetSelection(GetIdxFromWindow(m_clickTab));
1143             e.SetInt(m_pressedButton->id);
1144             e.SetEventObject(this);
1145             GetEventHandler()->ProcessEvent(e);
1146         }
1147 
1148         m_pressedButton = NULL;
1149     }
1150 
1151     m_clickPt = wxDefaultPosition;
1152     m_isDragging = false;
1153     m_clickTab = NULL;
1154 }
1155 
OnMiddleUp(wxMouseEvent & evt)1156 void wxAuiTabCtrl::OnMiddleUp(wxMouseEvent& evt)
1157 {
1158     wxWindow* wnd = NULL;
1159     if (!TabHitTest(evt.m_x, evt.m_y, &wnd))
1160         return;
1161 
1162     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_TAB_MIDDLE_UP, m_windowId);
1163     e.SetEventObject(this);
1164     e.SetSelection(GetIdxFromWindow(wnd));
1165     GetEventHandler()->ProcessEvent(e);
1166 }
1167 
OnMiddleDown(wxMouseEvent & evt)1168 void wxAuiTabCtrl::OnMiddleDown(wxMouseEvent& evt)
1169 {
1170     wxWindow* wnd = NULL;
1171     if (!TabHitTest(evt.m_x, evt.m_y, &wnd))
1172         return;
1173 
1174     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_TAB_MIDDLE_DOWN, m_windowId);
1175     e.SetEventObject(this);
1176     e.SetSelection(GetIdxFromWindow(wnd));
1177     GetEventHandler()->ProcessEvent(e);
1178 }
1179 
OnRightUp(wxMouseEvent & evt)1180 void wxAuiTabCtrl::OnRightUp(wxMouseEvent& evt)
1181 {
1182     wxWindow* wnd = NULL;
1183     if (!TabHitTest(evt.m_x, evt.m_y, &wnd))
1184         return;
1185 
1186     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_TAB_RIGHT_UP, m_windowId);
1187     e.SetEventObject(this);
1188     e.SetSelection(GetIdxFromWindow(wnd));
1189     GetEventHandler()->ProcessEvent(e);
1190 }
1191 
OnRightDown(wxMouseEvent & evt)1192 void wxAuiTabCtrl::OnRightDown(wxMouseEvent& evt)
1193 {
1194     wxWindow* wnd = NULL;
1195     if (!TabHitTest(evt.m_x, evt.m_y, &wnd))
1196         return;
1197 
1198     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_TAB_RIGHT_DOWN, m_windowId);
1199     e.SetEventObject(this);
1200     e.SetSelection(GetIdxFromWindow(wnd));
1201     GetEventHandler()->ProcessEvent(e);
1202 }
1203 
OnLeftDClick(wxMouseEvent & evt)1204 void wxAuiTabCtrl::OnLeftDClick(wxMouseEvent& evt)
1205 {
1206     wxWindow* wnd;
1207     wxAuiTabContainerButton* button;
1208     if (!TabHitTest(evt.m_x, evt.m_y, &wnd) && !ButtonHitTest(evt.m_x, evt.m_y, &button))
1209     {
1210         wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_BG_DCLICK, m_windowId);
1211         e.SetEventObject(this);
1212         GetEventHandler()->ProcessEvent(e);
1213     }
1214 }
1215 
OnMotion(wxMouseEvent & evt)1216 void wxAuiTabCtrl::OnMotion(wxMouseEvent& evt)
1217 {
1218     wxPoint pos = evt.GetPosition();
1219 
1220     // check if the mouse is hovering above a button
1221     wxAuiTabContainerButton* button;
1222     if (ButtonHitTest(pos.x, pos.y, &button) && !(button->curState & wxAUI_BUTTON_STATE_DISABLED))
1223     {
1224         if (m_hoverButton && button != m_hoverButton)
1225         {
1226             m_hoverButton->curState = wxAUI_BUTTON_STATE_NORMAL;
1227             m_hoverButton = NULL;
1228             Refresh();
1229             Update();
1230         }
1231 
1232         if (button->curState != wxAUI_BUTTON_STATE_HOVER)
1233         {
1234             button->curState = wxAUI_BUTTON_STATE_HOVER;
1235             Refresh();
1236             Update();
1237 
1238             m_hoverButton = button;
1239             return;
1240         }
1241     }
1242     else
1243     {
1244         if (m_hoverButton)
1245         {
1246             m_hoverButton->curState = wxAUI_BUTTON_STATE_NORMAL;
1247             m_hoverButton = NULL;
1248             Refresh();
1249             Update();
1250         }
1251     }
1252 
1253     wxWindow* wnd = NULL;
1254     if (evt.Moving() && TabHitTest(evt.m_x, evt.m_y, &wnd))
1255     {
1256         SetHoverTab(wnd);
1257 
1258 #if wxUSE_TOOLTIPS
1259         wxString tooltip(m_pages[GetIdxFromWindow(wnd)].tooltip);
1260 
1261         // If the text changes, set it else, keep old, to avoid
1262         // 'moving tooltip' effect
1263         if (GetToolTipText() != tooltip)
1264             SetToolTip(tooltip);
1265 #endif // wxUSE_TOOLTIPS
1266     }
1267     else
1268     {
1269         SetHoverTab(NULL);
1270 
1271 #if wxUSE_TOOLTIPS
1272         UnsetToolTip();
1273 #endif // wxUSE_TOOLTIPS
1274     }
1275 
1276     if (!evt.LeftIsDown() || m_clickPt == wxDefaultPosition)
1277         return;
1278 
1279     if (m_isDragging)
1280     {
1281         wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_DRAG_MOTION, m_windowId);
1282         e.SetSelection(GetIdxFromWindow(m_clickTab));
1283         e.SetOldSelection(e.GetSelection());
1284         e.SetEventObject(this);
1285         GetEventHandler()->ProcessEvent(e);
1286         return;
1287     }
1288 
1289 
1290     int drag_x_threshold = wxSystemSettings::GetMetric(wxSYS_DRAG_X, this);
1291     int drag_y_threshold = wxSystemSettings::GetMetric(wxSYS_DRAG_Y, this);
1292 
1293     if (abs(pos.x - m_clickPt.x) > drag_x_threshold ||
1294         abs(pos.y - m_clickPt.y) > drag_y_threshold)
1295     {
1296         wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_BEGIN_DRAG, m_windowId);
1297         e.SetSelection(GetIdxFromWindow(m_clickTab));
1298         e.SetOldSelection(e.GetSelection());
1299         e.SetEventObject(this);
1300         GetEventHandler()->ProcessEvent(e);
1301 
1302         m_isDragging = true;
1303     }
1304 }
1305 
OnLeaveWindow(wxMouseEvent & WXUNUSED (event))1306 void wxAuiTabCtrl::OnLeaveWindow(wxMouseEvent& WXUNUSED(event))
1307 {
1308     if (m_hoverButton)
1309     {
1310         m_hoverButton->curState = wxAUI_BUTTON_STATE_NORMAL;
1311         m_hoverButton = NULL;
1312         Refresh();
1313         Update();
1314     }
1315 
1316     SetHoverTab(NULL);
1317 }
1318 
OnButton(wxAuiNotebookEvent & event)1319 void wxAuiTabCtrl::OnButton(wxAuiNotebookEvent& event)
1320 {
1321     int button = event.GetInt();
1322 
1323     if (button == wxAUI_BUTTON_LEFT || button == wxAUI_BUTTON_RIGHT)
1324     {
1325         if (button == wxAUI_BUTTON_LEFT)
1326         {
1327             if (GetTabOffset() > 0)
1328             {
1329                 SetTabOffset(GetTabOffset()-1);
1330                 Refresh();
1331                 Update();
1332             }
1333         }
1334         else
1335         {
1336             SetTabOffset(GetTabOffset()+1);
1337             Refresh();
1338             Update();
1339         }
1340     }
1341     else if (button == wxAUI_BUTTON_WINDOWLIST)
1342     {
1343         int idx = GetArtProvider()->ShowDropDown(this, m_pages, GetActivePage());
1344 
1345         if (idx != -1)
1346         {
1347             wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_PAGE_CHANGING, m_windowId);
1348             e.SetSelection(idx);
1349             e.SetOldSelection(GetActivePage());
1350             e.SetEventObject(this);
1351             GetEventHandler()->ProcessEvent(e);
1352         }
1353     }
1354     else
1355     {
1356         event.Skip();
1357     }
1358 }
1359 
OnSetFocus(wxFocusEvent & WXUNUSED (event))1360 void wxAuiTabCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
1361 {
1362     Refresh();
1363 }
1364 
OnKillFocus(wxFocusEvent & WXUNUSED (event))1365 void wxAuiTabCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
1366 {
1367     Refresh();
1368 }
1369 
OnChar(wxKeyEvent & event)1370 void wxAuiTabCtrl::OnChar(wxKeyEvent& event)
1371 {
1372     if (GetActivePage() == -1)
1373     {
1374         event.Skip();
1375         return;
1376     }
1377 
1378     // We can't leave tab processing to the system; on Windows, tabs and keys
1379     // get eaten by the system and not processed properly if we specify both
1380     // wxTAB_TRAVERSAL and wxWANTS_CHARS. And if we specify just wxTAB_TRAVERSAL,
1381     // we don't key arrow key events.
1382 
1383     int key = event.GetKeyCode();
1384 
1385     if (key == WXK_NUMPAD_PAGEUP)
1386         key = WXK_PAGEUP;
1387     if (key == WXK_NUMPAD_PAGEDOWN)
1388         key = WXK_PAGEDOWN;
1389     if (key == WXK_NUMPAD_HOME)
1390         key = WXK_HOME;
1391     if (key == WXK_NUMPAD_END)
1392         key = WXK_END;
1393     if (key == WXK_NUMPAD_LEFT)
1394         key = WXK_LEFT;
1395     if (key == WXK_NUMPAD_RIGHT)
1396         key = WXK_RIGHT;
1397 
1398     if (key == WXK_TAB || key == WXK_PAGEUP || key == WXK_PAGEDOWN)
1399     {
1400         bool bCtrlDown = event.ControlDown();
1401         bool bShiftDown = event.ShiftDown();
1402 
1403         bool bForward = (key == WXK_TAB && !bShiftDown) || (key == WXK_PAGEDOWN);
1404         bool bWindowChange = (key == WXK_PAGEUP) || (key == WXK_PAGEDOWN) || bCtrlDown;
1405         bool bFromTab = (key == WXK_TAB);
1406 
1407         if (bFromTab && !bWindowChange)
1408         {
1409             // Handle ordinary tabs via Navigate. This is needed at least for wxGTK to tab properly.
1410             Navigate(bForward ? wxNavigationKeyEvent::IsForward : wxNavigationKeyEvent::IsBackward);
1411             return;
1412         }
1413 
1414         wxAuiNotebook* nb = wxDynamicCast(GetParent(), wxAuiNotebook);
1415         if (!nb)
1416         {
1417             event.Skip();
1418             return;
1419         }
1420 
1421         wxNavigationKeyEvent keyEvent;
1422         keyEvent.SetDirection(bForward);
1423         keyEvent.SetWindowChange(bWindowChange);
1424         keyEvent.SetFromTab(bFromTab);
1425         keyEvent.SetEventObject(nb);
1426 
1427         if (!nb->GetEventHandler()->ProcessEvent(keyEvent))
1428         {
1429             // Not processed? Do an explicit tab into the page.
1430             wxWindow* win = GetWindowFromIdx(GetActivePage());
1431             if (win)
1432                 win->SetFocus();
1433         }
1434         return;
1435     }
1436 
1437     if (m_pages.GetCount() < 2)
1438     {
1439         event.Skip();
1440         return;
1441     }
1442 
1443     int newPage = -1;
1444 
1445     int forwardKey, backwardKey;
1446     if (GetLayoutDirection() == wxLayout_RightToLeft)
1447     {
1448         forwardKey = WXK_LEFT;
1449         backwardKey = WXK_RIGHT;
1450     }
1451     else
1452      {
1453         forwardKey = WXK_RIGHT;
1454         backwardKey = WXK_LEFT;
1455     }
1456 
1457     if (key == forwardKey)
1458     {
1459         if (m_pages.GetCount() > 1)
1460         {
1461             if (GetActivePage() == -1)
1462                 newPage = 0;
1463             else if (GetActivePage() < (int) (m_pages.GetCount() - 1))
1464                 newPage = GetActivePage() + 1;
1465         }
1466     }
1467     else if (key == backwardKey)
1468     {
1469         if (m_pages.GetCount() > 1)
1470         {
1471             if (GetActivePage() == -1)
1472                 newPage = (int) (m_pages.GetCount() - 1);
1473             else if (GetActivePage() > 0)
1474                 newPage = GetActivePage() - 1;
1475         }
1476     }
1477     else if (key == WXK_HOME)
1478     {
1479         newPage = 0;
1480     }
1481     else if (key == WXK_END)
1482     {
1483         newPage = (int) (m_pages.GetCount() - 1);
1484     }
1485     else
1486         event.Skip();
1487 
1488     if (newPage != -1)
1489     {
1490         wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_PAGE_CHANGING, m_windowId);
1491         e.SetSelection(newPage);
1492         e.SetOldSelection(newPage);
1493         e.SetEventObject(this);
1494         this->GetEventHandler()->ProcessEvent(e);
1495     }
1496     else
1497         event.Skip();
1498 }
1499 
1500 // wxTabFrame is an interesting case.  It's important that all child pages
1501 // of the multi-notebook control are all actually children of that control
1502 // (and not grandchildren).  wxTabFrame facilitates this.  There is one
1503 // instance of wxTabFrame for each tab control inside the multi-notebook.
1504 // It's important to know that wxTabFrame is not a real window, but it merely
1505 // used to capture the dimensions/positioning of the internal tab control and
1506 // it's managed page windows
1507 
1508 class wxTabFrame : public wxWindow
1509 {
1510 public:
1511 
wxTabFrame()1512     wxTabFrame()
1513     {
1514         m_tabs = NULL;
1515         m_rect = wxRect(wxPoint(0,0), FromDIP(wxSize(200,200)));
1516         m_tabCtrlHeight = FromDIP(20);
1517     }
1518 
~wxTabFrame()1519     ~wxTabFrame()
1520     {
1521         wxDELETE(m_tabs);
1522     }
1523 
SetTabCtrlHeight(int h)1524     void SetTabCtrlHeight(int h)
1525     {
1526         m_tabCtrlHeight = h;
1527     }
1528 
1529 protected:
DoSetSize(int x,int y,int width,int height,int WXUNUSED (sizeFlags=wxSIZE_AUTO))1530     void DoSetSize(int x, int y,
1531                    int width, int height,
1532                    int WXUNUSED(sizeFlags = wxSIZE_AUTO)) wxOVERRIDE
1533     {
1534         m_rect = wxRect(x, y, width, height);
1535         DoSizing();
1536     }
1537 
DoGetClientSize(int * x,int * y) const1538     void DoGetClientSize(int* x, int* y) const wxOVERRIDE
1539     {
1540         *x = m_rect.width;
1541         *y = m_rect.height;
1542     }
1543 
1544 public:
Show(bool WXUNUSED (show=true))1545     bool Show( bool WXUNUSED(show = true) ) wxOVERRIDE { return false; }
1546 
DoSizing()1547     void DoSizing()
1548     {
1549         if (!m_tabs)
1550             return;
1551 
1552         if (m_tabs->IsFrozen() || m_tabs->GetParent()->IsFrozen())
1553             return;
1554 
1555         m_tab_rect = wxRect(m_rect.x, m_rect.y, m_rect.width, m_tabCtrlHeight);
1556         if (m_tabs->GetFlags() & wxAUI_NB_BOTTOM)
1557         {
1558             m_tab_rect = wxRect (m_rect.x, m_rect.y + m_rect.height - m_tabCtrlHeight, m_rect.width, m_tabCtrlHeight);
1559             m_tabs->SetSize     (m_rect.x, m_rect.y + m_rect.height - m_tabCtrlHeight, m_rect.width, m_tabCtrlHeight);
1560             m_tabs->SetRect     (wxRect(0, 0, m_rect.width, m_tabCtrlHeight));
1561         }
1562         else //TODO: if (GetFlags() & wxAUI_NB_TOP)
1563         {
1564             m_tab_rect = wxRect (m_rect.x, m_rect.y, m_rect.width, m_tabCtrlHeight);
1565             m_tabs->SetSize     (m_rect.x, m_rect.y, m_rect.width, m_tabCtrlHeight);
1566             m_tabs->SetRect     (wxRect(0, 0,        m_rect.width, m_tabCtrlHeight));
1567         }
1568         // TODO: else if (GetFlags() & wxAUI_NB_LEFT){}
1569         // TODO: else if (GetFlags() & wxAUI_NB_RIGHT){}
1570 
1571         m_tabs->Refresh();
1572         m_tabs->Update();
1573 
1574         wxAuiNotebookPageArray& pages = m_tabs->GetPages();
1575         size_t i, page_count = pages.GetCount();
1576 
1577         for (i = 0; i < page_count; ++i)
1578         {
1579             wxAuiNotebookPage& page = pages.Item(i);
1580             int border_space = m_tabs->GetArtProvider()->GetAdditionalBorderSpace(page.window);
1581 
1582             int height = m_rect.height - m_tabCtrlHeight - border_space;
1583             if ( height < 0 )
1584             {
1585                 // avoid passing negative height to wxWindow::SetSize(), this
1586                 // results in assert failures/GTK+ warnings
1587                 height = 0;
1588             }
1589             int width = m_rect.width - 2 * border_space;
1590             if (width < 0)
1591                 width = 0;
1592 
1593             if (m_tabs->GetFlags() & wxAUI_NB_BOTTOM)
1594             {
1595                 page.window->SetSize(m_rect.x + border_space,
1596                                      m_rect.y + border_space,
1597                                      width,
1598                                      height);
1599             }
1600             else //TODO: if (GetFlags() & wxAUI_NB_TOP)
1601             {
1602                 page.window->SetSize(m_rect.x + border_space,
1603                                      m_rect.y + m_tabCtrlHeight,
1604                                      width,
1605                                      height);
1606             }
1607             // TODO: else if (GetFlags() & wxAUI_NB_LEFT){}
1608             // TODO: else if (GetFlags() & wxAUI_NB_RIGHT){}
1609         }
1610     }
1611 
1612 protected:
DoGetSize(int * x,int * y) const1613     void DoGetSize(int* x, int* y) const wxOVERRIDE
1614     {
1615         if (x)
1616             *x = m_rect.GetWidth();
1617         if (y)
1618             *y = m_rect.GetHeight();
1619     }
1620 
1621 public:
Update()1622     void Update() wxOVERRIDE
1623     {
1624         // does nothing
1625     }
1626 
1627     wxRect m_rect;
1628     wxRect m_tab_rect;
1629     wxAuiTabCtrl* m_tabs;
1630     int m_tabCtrlHeight;
1631 };
1632 
1633 
1634 const int wxAuiBaseTabCtrlId = 5380;
1635 
1636 
1637 // -- wxAuiNotebook class implementation --
1638 
1639 #define EVT_AUI_RANGE(id1, id2, event, func) \
1640     wx__DECLARE_EVT2(event, id1, id2, wxAuiNotebookEventHandler(func))
1641 
wxBEGIN_EVENT_TABLE(wxAuiNotebook,wxControl)1642 wxBEGIN_EVENT_TABLE(wxAuiNotebook, wxControl)
1643     EVT_SIZE(wxAuiNotebook::OnSize)
1644     EVT_CHILD_FOCUS(wxAuiNotebook::OnChildFocusNotebook)
1645     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1646                       wxEVT_AUINOTEBOOK_PAGE_CHANGING,
1647                       wxAuiNotebook::OnTabClicked)
1648     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1649                       wxEVT_AUINOTEBOOK_BEGIN_DRAG,
1650                       wxAuiNotebook::OnTabBeginDrag)
1651     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1652                       wxEVT_AUINOTEBOOK_END_DRAG,
1653                       wxAuiNotebook::OnTabEndDrag)
1654     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1655                       wxEVT_AUINOTEBOOK_CANCEL_DRAG,
1656                       wxAuiNotebook::OnTabCancelDrag)
1657     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1658                       wxEVT_AUINOTEBOOK_DRAG_MOTION,
1659                       wxAuiNotebook::OnTabDragMotion)
1660     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1661                       wxEVT_AUINOTEBOOK_BUTTON,
1662                       wxAuiNotebook::OnTabButton)
1663     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1664                       wxEVT_AUINOTEBOOK_TAB_MIDDLE_DOWN,
1665                       wxAuiNotebook::OnTabMiddleDown)
1666     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1667                       wxEVT_AUINOTEBOOK_TAB_MIDDLE_UP,
1668                       wxAuiNotebook::OnTabMiddleUp)
1669     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1670                       wxEVT_AUINOTEBOOK_TAB_RIGHT_DOWN,
1671                       wxAuiNotebook::OnTabRightDown)
1672     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1673                       wxEVT_AUINOTEBOOK_TAB_RIGHT_UP,
1674                       wxAuiNotebook::OnTabRightUp)
1675     EVT_AUI_RANGE(wxAuiBaseTabCtrlId, wxAuiBaseTabCtrlId+500,
1676                       wxEVT_AUINOTEBOOK_BG_DCLICK,
1677                       wxAuiNotebook::OnTabBgDClick)
1678     EVT_NAVIGATION_KEY(wxAuiNotebook::OnNavigationKeyNotebook)
1679     EVT_SYS_COLOUR_CHANGED(wxAuiNotebook::OnSysColourChanged)
1680 wxEND_EVENT_TABLE()
1681 
1682 void wxAuiNotebook::OnSysColourChanged(wxSysColourChangedEvent &event)
1683 {
1684     event.Skip(true);
1685     wxAuiTabArt* art = m_tabs.GetArtProvider();
1686     art->UpdateColoursFromSystem();
1687 
1688     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
1689     size_t i, pane_count = all_panes.GetCount();
1690     for (i = 0; i < pane_count; ++i)
1691     {
1692         wxAuiPaneInfo& pane = all_panes.Item(i);
1693         if (pane.name == wxT("dummy"))
1694             continue;
1695         wxTabFrame* tab_frame = (wxTabFrame*)pane.window;
1696         wxAuiTabCtrl* tabctrl = tab_frame->m_tabs;
1697         tabctrl->GetArtProvider()->UpdateColoursFromSystem();
1698         tabctrl->Refresh();
1699     }
1700     Refresh();
1701 }
1702 
Init()1703 void wxAuiNotebook::Init()
1704 {
1705     m_curPage = -1;
1706     m_tabIdCounter = wxAuiBaseTabCtrlId;
1707     m_dummyWnd = NULL;
1708     m_tabCtrlHeight = FromDIP(20);
1709     m_requestedBmpSize = wxDefaultSize;
1710     m_requestedTabCtrlHeight = -1;
1711 }
1712 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)1713 bool wxAuiNotebook::Create(wxWindow* parent,
1714                            wxWindowID id,
1715                            const wxPoint& pos,
1716                            const wxSize& size,
1717                            long style)
1718 {
1719     if (!wxControl::Create(parent, id, pos, size, style))
1720         return false;
1721 
1722     InitNotebook(style);
1723 
1724     return true;
1725 }
1726 
1727 // InitNotebook() contains common initialization
1728 // code called by all constructors
InitNotebook(long style)1729 void wxAuiNotebook::InitNotebook(long style)
1730 {
1731     SetName(wxT("wxAuiNotebook"));
1732     m_curPage = -1;
1733     m_tabIdCounter = wxAuiBaseTabCtrlId;
1734     m_dummyWnd = NULL;
1735     m_flags = (unsigned int)style;
1736     m_tabCtrlHeight = FromDIP(20);
1737 
1738     m_normalFont = *wxNORMAL_FONT;
1739     m_selectedFont = *wxNORMAL_FONT;
1740     m_selectedFont.SetWeight(wxFONTWEIGHT_BOLD);
1741 
1742     SetArtProvider(new wxAuiDefaultTabArt);
1743 
1744     m_dummyWnd = new wxWindow(this, wxID_ANY, wxPoint(0,0), wxSize(0,0));
1745     m_dummyWnd->SetSize(FromDIP(wxSize(200, 200)));
1746     m_dummyWnd->Show(false);
1747 
1748     m_mgr.SetManagedWindow(this);
1749     m_mgr.SetFlags(wxAUI_MGR_DEFAULT);
1750     m_mgr.SetDockSizeConstraint(1.0, 1.0); // no dock size constraint
1751 
1752     m_mgr.AddPane(m_dummyWnd,
1753               wxAuiPaneInfo().Name(wxT("dummy")).Bottom().CaptionVisible(false).Show(false));
1754 
1755     m_mgr.Update();
1756 }
1757 
~wxAuiNotebook()1758 wxAuiNotebook::~wxAuiNotebook()
1759 {
1760     // Indicate we're deleting pages
1761     SendDestroyEvent();
1762 
1763     while ( GetPageCount() > 0 )
1764         DeletePage(0);
1765 
1766     m_mgr.UnInit();
1767 }
1768 
SetArtProvider(wxAuiTabArt * art)1769 void wxAuiNotebook::SetArtProvider(wxAuiTabArt* art)
1770 {
1771     m_tabs.SetArtProvider(art);
1772 
1773     // Update the height and do nothing else if it did something but otherwise
1774     // (i.e. if the new art provider uses the same height as the old one) we
1775     // need to manually set the art provider for all tabs ourselves.
1776     if ( !UpdateTabCtrlHeight() )
1777     {
1778         wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
1779         const size_t pane_count = all_panes.GetCount();
1780         for (size_t i = 0; i < pane_count; ++i)
1781         {
1782             wxAuiPaneInfo& pane = all_panes.Item(i);
1783             if (pane.name == wxT("dummy"))
1784                 continue;
1785             wxTabFrame* tab_frame = (wxTabFrame*)pane.window;
1786             wxAuiTabCtrl* tabctrl = tab_frame->m_tabs;
1787             tabctrl->SetArtProvider(art->Clone());
1788         }
1789     }
1790 }
1791 
1792 // SetTabCtrlHeight() is the highest-level override of the
1793 // tab height.  A call to this function effectively enforces a
1794 // specified tab ctrl height, overriding all other considerations,
1795 // such as text or bitmap height.  It overrides any call to
1796 // SetUniformBitmapSize().  Specifying a height of -1 reverts
1797 // any previous call and returns to the default behaviour
1798 
SetTabCtrlHeight(int height)1799 void wxAuiNotebook::SetTabCtrlHeight(int height)
1800 {
1801     m_requestedTabCtrlHeight = height;
1802 
1803     // if window is already initialized, recalculate the tab height
1804     if (m_dummyWnd)
1805     {
1806         UpdateTabCtrlHeight();
1807     }
1808 }
1809 
1810 
1811 // SetUniformBitmapSize() ensures that all tabs will have
1812 // the same height, even if some tabs don't have bitmaps
1813 // Passing wxDefaultSize to this function will instruct
1814 // the control to use dynamic tab height-- so when a tab
1815 // with a large bitmap is added, the tab ctrl's height will
1816 // automatically increase to accommodate the bitmap
1817 
SetUniformBitmapSize(const wxSize & size)1818 void wxAuiNotebook::SetUniformBitmapSize(const wxSize& size)
1819 {
1820     m_requestedBmpSize = size;
1821 
1822     // if window is already initialized, recalculate the tab height
1823     if (m_dummyWnd)
1824     {
1825         UpdateTabCtrlHeight();
1826     }
1827 }
1828 
1829 // UpdateTabCtrlHeight() does the actual tab resizing. It's meant
1830 // to be used internally
UpdateTabCtrlHeight()1831 bool wxAuiNotebook::UpdateTabCtrlHeight()
1832 {
1833     // get the tab ctrl height we will use
1834     int height = CalculateTabCtrlHeight();
1835 
1836     // if the tab control height needs to change, update
1837     // all of our tab controls with the new height
1838     if (m_tabCtrlHeight == height)
1839         return false;
1840 
1841     wxAuiTabArt* art = m_tabs.GetArtProvider();
1842 
1843     m_tabCtrlHeight = height;
1844 
1845     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
1846     size_t i, pane_count = all_panes.GetCount();
1847     for (i = 0; i < pane_count; ++i)
1848     {
1849         wxAuiPaneInfo& pane = all_panes.Item(i);
1850         if (pane.name == wxT("dummy"))
1851             continue;
1852         wxTabFrame* tab_frame = (wxTabFrame*)pane.window;
1853         wxAuiTabCtrl* tabctrl = tab_frame->m_tabs;
1854         tab_frame->SetTabCtrlHeight(m_tabCtrlHeight);
1855         tabctrl->SetArtProvider(art->Clone());
1856         tab_frame->DoSizing();
1857     }
1858 
1859     return true;
1860 }
1861 
UpdateHintWindowSize()1862 void wxAuiNotebook::UpdateHintWindowSize()
1863 {
1864     wxSize size = CalculateNewSplitSize();
1865 
1866     // the placeholder hint window should be set to this size
1867     wxAuiPaneInfo& info = m_mgr.GetPane(wxT("dummy"));
1868     if (info.IsOk())
1869     {
1870         info.MinSize(size);
1871         info.BestSize(size);
1872         m_dummyWnd->SetSize(size);
1873     }
1874 }
1875 
1876 
1877 // calculates the size of the new split
CalculateNewSplitSize()1878 wxSize wxAuiNotebook::CalculateNewSplitSize()
1879 {
1880     // count number of tab controls
1881     int tab_ctrl_count = 0;
1882     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
1883     size_t i, pane_count = all_panes.GetCount();
1884     for (i = 0; i < pane_count; ++i)
1885     {
1886         wxAuiPaneInfo& pane = all_panes.Item(i);
1887         if (pane.name == wxT("dummy"))
1888             continue;
1889         tab_ctrl_count++;
1890     }
1891 
1892     wxSize new_split_size;
1893 
1894     // if there is only one tab control, the first split
1895     // should happen around the middle
1896     if (tab_ctrl_count < 2)
1897     {
1898         new_split_size = GetClientSize();
1899         new_split_size.x /= 2;
1900         new_split_size.y /= 2;
1901     }
1902     else
1903     {
1904         // this is in place of a more complicated calculation
1905         // that needs to be implemented
1906         new_split_size = FromDIP(wxSize(180,180));
1907     }
1908 
1909     return new_split_size;
1910 }
1911 
CalculateTabCtrlHeight()1912 int wxAuiNotebook::CalculateTabCtrlHeight()
1913 {
1914     // if a fixed tab ctrl height is specified,
1915     // just return that instead of calculating a
1916     // tab height
1917     if (m_requestedTabCtrlHeight != -1)
1918         return m_requestedTabCtrlHeight;
1919 
1920     // find out new best tab height
1921     wxAuiTabArt* art = m_tabs.GetArtProvider();
1922 
1923     return art->GetBestTabCtrlSize(this,
1924                                    m_tabs.GetPages(),
1925                                    m_requestedBmpSize);
1926 }
1927 
1928 
GetArtProvider() const1929 wxAuiTabArt* wxAuiNotebook::GetArtProvider() const
1930 {
1931     return m_tabs.GetArtProvider();
1932 }
1933 
SetWindowStyleFlag(long style)1934 void wxAuiNotebook::SetWindowStyleFlag(long style)
1935 {
1936     wxControl::SetWindowStyleFlag(style);
1937 
1938     m_flags = (unsigned int)style;
1939 
1940     // if the control is already initialized
1941     if (m_mgr.GetManagedWindow() == (wxWindow*)this)
1942     {
1943         // let all of the tab children know about the new style
1944 
1945         wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
1946         size_t i, pane_count = all_panes.GetCount();
1947         for (i = 0; i < pane_count; ++i)
1948         {
1949             wxAuiPaneInfo& pane = all_panes.Item(i);
1950             if (pane.name == wxT("dummy"))
1951                 continue;
1952             wxTabFrame* tabframe = (wxTabFrame*)pane.window;
1953             wxAuiTabCtrl* tabctrl = tabframe->m_tabs;
1954             tabctrl->SetFlags(m_flags);
1955             tabframe->DoSizing();
1956             tabctrl->Refresh();
1957             tabctrl->Update();
1958         }
1959     }
1960 }
1961 
1962 
AddPage(wxWindow * page,const wxString & caption,bool select,const wxBitmap & bitmap)1963 bool wxAuiNotebook::AddPage(wxWindow* page,
1964                             const wxString& caption,
1965                             bool select,
1966                             const wxBitmap& bitmap)
1967 {
1968     return InsertPage(GetPageCount(), page, caption, select, bitmap);
1969 }
1970 
InsertPage(size_t page_idx,wxWindow * page,const wxString & caption,bool select,const wxBitmap & bitmap)1971 bool wxAuiNotebook::InsertPage(size_t page_idx,
1972                                wxWindow* page,
1973                                const wxString& caption,
1974                                bool select,
1975                                const wxBitmap& bitmap)
1976 {
1977     wxASSERT_MSG(page, wxT("page pointer must be non-NULL"));
1978     if (!page)
1979         return false;
1980 
1981     page->Reparent(this);
1982 
1983     wxAuiNotebookPage info;
1984     info.window = page;
1985     info.caption = caption;
1986     info.bitmap = bitmap;
1987     info.active = false;
1988 
1989     // if there are currently no tabs, the first added
1990     // tab must be active
1991     if (m_tabs.GetPageCount() == 0)
1992         info.active = true;
1993 
1994     m_tabs.InsertPage(page, info, page_idx);
1995 
1996     // if that was the first page added, even if
1997     // select is false, it must become the "current page"
1998     // (though no select events will be fired)
1999     if (!select && m_tabs.GetPageCount() == 1)
2000         select = true;
2001         //m_curPage = GetPageIndex(page);
2002 
2003     wxAuiTabCtrl* active_tabctrl = GetActiveTabCtrl();
2004     if (page_idx >= active_tabctrl->GetPageCount())
2005         active_tabctrl->AddPage(page, info);
2006     else
2007         active_tabctrl->InsertPage(page, info, page_idx);
2008 
2009     // Note that we don't need to call DoSizing() if the height has changed, as
2010     // it's already called from UpdateTabCtrlHeight() itself in this case.
2011     if ( !UpdateTabCtrlHeight() )
2012         DoSizing();
2013 
2014     active_tabctrl->DoShowHide();
2015 
2016     // adjust selected index
2017     if(m_curPage >= (int) page_idx)
2018         m_curPage++;
2019 
2020     if (select)
2021     {
2022         SetSelectionToWindow(page);
2023     }
2024 
2025     return true;
2026 }
2027 
2028 
2029 // DeletePage() removes a tab from the multi-notebook,
2030 // and destroys the window as well
DeletePage(size_t page_idx)2031 bool wxAuiNotebook::DeletePage(size_t page_idx)
2032 {
2033     if (page_idx >= m_tabs.GetPageCount())
2034         return false;
2035 
2036     wxWindow* wnd = m_tabs.GetWindowFromIdx(page_idx);
2037 
2038     // hide the window in advance, as this will
2039     // prevent flicker
2040     ShowWnd(wnd, false);
2041 
2042     if (!RemovePage(page_idx))
2043         return false;
2044 
2045 #if wxUSE_MDI
2046     // actually destroy the window now
2047     if (wxDynamicCast(wnd, wxAuiMDIChildFrame))
2048     {
2049         // delete the child frame with pending delete, as is
2050         // customary with frame windows
2051         if (!wxPendingDelete.Member(wnd))
2052             wxPendingDelete.Append(wnd);
2053     }
2054     else
2055 #endif
2056     {
2057         wnd->Destroy();
2058     }
2059 
2060     return true;
2061 }
2062 
2063 
2064 
2065 // RemovePage() removes a tab from the multi-notebook,
2066 // but does not destroy the window
RemovePage(size_t page_idx)2067 bool wxAuiNotebook::RemovePage(size_t page_idx)
2068 {
2069     // save active window pointer
2070     wxWindow* active_wnd = NULL;
2071     if (m_curPage >= 0)
2072         active_wnd = m_tabs.GetWindowFromIdx(m_curPage);
2073 
2074     // save pointer of window being deleted
2075     wxWindow* wnd = m_tabs.GetWindowFromIdx(page_idx);
2076     wxWindow* new_active = NULL;
2077 
2078     // make sure we found the page
2079     if (!wnd)
2080         return false;
2081 
2082     ShowWnd(wnd, false);
2083 
2084     // find out which onscreen tab ctrl owns this tab
2085     wxAuiTabCtrl* ctrl;
2086     int ctrl_idx;
2087     if (!FindTab(wnd, &ctrl, &ctrl_idx))
2088         return false;
2089 
2090     bool is_curpage = (m_curPage == (int)page_idx);
2091     bool is_active_in_split = ctrl->GetPage(ctrl_idx).active;
2092 
2093 
2094     // remove the tab from main catalog
2095     if (!m_tabs.RemovePage(wnd))
2096         return false;
2097 
2098     // remove the tab from the onscreen tab ctrl
2099     ctrl->RemovePage(wnd);
2100 
2101     if (is_active_in_split)
2102     {
2103         int ctrl_new_page_count = (int)ctrl->GetPageCount();
2104 
2105         if (ctrl_idx >= ctrl_new_page_count)
2106             ctrl_idx = ctrl_new_page_count-1;
2107 
2108         if (ctrl_idx >= 0 && ctrl_idx < (int)ctrl->GetPageCount())
2109         {
2110             // set new page as active in the tab split
2111             ctrl->SetActivePage(ctrl_idx);
2112 
2113             // if the page deleted was the current page for the
2114             // entire tab control, then record the window
2115             // pointer of the new active page for activation
2116             if (is_curpage)
2117             {
2118                 new_active = ctrl->GetWindowFromIdx(ctrl_idx);
2119             }
2120         }
2121     }
2122     else
2123     {
2124         // we are not deleting the active page, so keep it the same
2125         new_active = active_wnd;
2126     }
2127 
2128 
2129     if (!new_active)
2130     {
2131         // we haven't yet found a new page to active,
2132         // so select the next page from the main tab
2133         // catalogue
2134 
2135         if (page_idx < m_tabs.GetPageCount())
2136         {
2137             new_active = m_tabs.GetPage(page_idx).window;
2138         }
2139 
2140         if (!new_active && m_tabs.GetPageCount() > 0)
2141         {
2142             new_active = m_tabs.GetPage(0).window;
2143         }
2144     }
2145 
2146 
2147     RemoveEmptyTabFrames();
2148 
2149     m_curPage = wxNOT_FOUND;
2150 
2151     // set new active pane unless we're being destroyed anyhow
2152     if (new_active && !m_isBeingDeleted)
2153         SetSelectionToWindow(new_active);
2154 
2155     return true;
2156 }
2157 
2158 // GetPageIndex() returns the index of the page, or -1 if the
2159 // page could not be located in the notebook
GetPageIndex(wxWindow * page_wnd) const2160 int wxAuiNotebook::GetPageIndex(wxWindow* page_wnd) const
2161 {
2162     return m_tabs.GetIdxFromWindow(page_wnd);
2163 }
2164 
2165 
2166 
2167 // SetPageText() changes the tab caption of the specified page
SetPageText(size_t page_idx,const wxString & text)2168 bool wxAuiNotebook::SetPageText(size_t page_idx, const wxString& text)
2169 {
2170     if (page_idx >= m_tabs.GetPageCount())
2171         return false;
2172 
2173     // update our own tab catalog
2174     wxAuiNotebookPage& page_info = m_tabs.GetPage(page_idx);
2175     page_info.caption = text;
2176 
2177     // update what's on screen
2178     wxAuiTabCtrl* ctrl;
2179     int ctrl_idx;
2180     if (FindTab(page_info.window, &ctrl, &ctrl_idx))
2181     {
2182         wxAuiNotebookPage& info = ctrl->GetPage(ctrl_idx);
2183         info.caption = text;
2184         ctrl->Refresh();
2185         ctrl->Update();
2186     }
2187 
2188     return true;
2189 }
2190 
2191 // returns the page caption
GetPageText(size_t page_idx) const2192 wxString wxAuiNotebook::GetPageText(size_t page_idx) const
2193 {
2194     if (page_idx >= m_tabs.GetPageCount())
2195         return wxEmptyString;
2196 
2197     // update our own tab catalog
2198     const wxAuiNotebookPage& page_info = m_tabs.GetPage(page_idx);
2199     return page_info.caption;
2200 }
2201 
SetPageToolTip(size_t page_idx,const wxString & text)2202 bool wxAuiNotebook::SetPageToolTip(size_t page_idx, const wxString& text)
2203 {
2204     if (page_idx >= m_tabs.GetPageCount())
2205         return false;
2206 
2207     // update our own tab catalog
2208     wxAuiNotebookPage& page_info = m_tabs.GetPage(page_idx);
2209     page_info.tooltip = text;
2210 
2211     wxAuiTabCtrl* ctrl;
2212     int ctrl_idx;
2213     if (!FindTab(page_info.window, &ctrl, &ctrl_idx))
2214         return false;
2215 
2216     wxAuiNotebookPage& info = ctrl->GetPage(ctrl_idx);
2217     info.tooltip = text;
2218 
2219     // NB: we don't update the tooltip if it is already being displayed, it
2220     //     typically never happens, no need to code that
2221     return true;
2222 }
2223 
GetPageToolTip(size_t page_idx) const2224 wxString wxAuiNotebook::GetPageToolTip(size_t page_idx) const
2225 {
2226     if (page_idx >= m_tabs.GetPageCount())
2227         return wxString();
2228 
2229     const wxAuiNotebookPage& page_info = m_tabs.GetPage(page_idx);
2230     return page_info.tooltip;
2231 }
2232 
SetPageBitmap(size_t page_idx,const wxBitmap & bitmap)2233 bool wxAuiNotebook::SetPageBitmap(size_t page_idx, const wxBitmap& bitmap)
2234 {
2235     if (page_idx >= m_tabs.GetPageCount())
2236         return false;
2237 
2238     // update our own tab catalog
2239     wxAuiNotebookPage& page_info = m_tabs.GetPage(page_idx);
2240     page_info.bitmap = bitmap;
2241 
2242     // tab height might have changed
2243     UpdateTabCtrlHeight();
2244 
2245     // update what's on screen
2246     wxAuiTabCtrl* ctrl;
2247     int ctrl_idx;
2248     if (FindTab(page_info.window, &ctrl, &ctrl_idx))
2249     {
2250         wxAuiNotebookPage& info = ctrl->GetPage(ctrl_idx);
2251         info.bitmap = bitmap;
2252         ctrl->Refresh();
2253         ctrl->Update();
2254     }
2255 
2256     return true;
2257 }
2258 
2259 // returns the page bitmap
GetPageBitmap(size_t page_idx) const2260 wxBitmap wxAuiNotebook::GetPageBitmap(size_t page_idx) const
2261 {
2262     if (page_idx >= m_tabs.GetPageCount())
2263         return wxBitmap();
2264 
2265     // update our own tab catalog
2266     const wxAuiNotebookPage& page_info = m_tabs.GetPage(page_idx);
2267     return page_info.bitmap;
2268 }
2269 
2270 // GetSelection() returns the index of the currently active page
GetSelection() const2271 int wxAuiNotebook::GetSelection() const
2272 {
2273     return m_curPage;
2274 }
2275 
2276 // SetSelection() sets the currently active page
SetSelection(size_t new_page)2277 int wxAuiNotebook::SetSelection(size_t new_page)
2278 {
2279     return DoModifySelection(new_page, true);
2280 }
2281 
SetSelectionToWindow(wxWindow * win)2282 void wxAuiNotebook::SetSelectionToWindow(wxWindow *win)
2283 {
2284     const int idx = m_tabs.GetIdxFromWindow(win);
2285     wxCHECK_RET( idx != wxNOT_FOUND, wxT("invalid notebook page") );
2286 
2287 
2288     // since a tab was clicked, let the parent know that we received
2289     // the focus, even if we will assign that focus immediately
2290     // to the child tab in the SetSelection call below
2291     // (the child focus event will also let wxAuiManager, if any,
2292     // know that the notebook control has been activated)
2293 
2294     wxWindow* parent = GetParent();
2295     if (parent)
2296     {
2297         wxChildFocusEvent eventFocus(this);
2298         parent->GetEventHandler()->ProcessEvent(eventFocus);
2299     }
2300 
2301 
2302     SetSelection(idx);
2303 }
2304 
2305 // GetPageCount() returns the total number of
2306 // pages managed by the multi-notebook
GetPageCount() const2307 size_t wxAuiNotebook::GetPageCount() const
2308 {
2309     return m_tabs.GetPageCount();
2310 }
2311 
2312 // GetPage() returns the wxWindow pointer of the
2313 // specified page
GetPage(size_t page_idx) const2314 wxWindow* wxAuiNotebook::GetPage(size_t page_idx) const
2315 {
2316     wxASSERT(page_idx < m_tabs.GetPageCount());
2317 
2318     return m_tabs.GetWindowFromIdx(page_idx);
2319 }
2320 
2321 // DoSizing() performs all sizing operations in each tab control
DoSizing()2322 void wxAuiNotebook::DoSizing()
2323 {
2324     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2325     size_t i, pane_count = all_panes.GetCount();
2326     for (i = 0; i < pane_count; ++i)
2327     {
2328         if (all_panes.Item(i).name == wxT("dummy"))
2329             continue;
2330 
2331         wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2332         tabframe->DoSizing();
2333     }
2334 }
2335 
2336 // GetActiveTabCtrl() returns the active tab control.  It is
2337 // called to determine which control gets new windows being added
GetActiveTabCtrl()2338 wxAuiTabCtrl* wxAuiNotebook::GetActiveTabCtrl()
2339 {
2340     if (m_curPage >= 0 && m_curPage < (int)m_tabs.GetPageCount())
2341     {
2342         wxAuiTabCtrl* ctrl;
2343         int idx;
2344 
2345         // find the tab ctrl with the current page
2346         if (FindTab(m_tabs.GetPage(m_curPage).window,
2347                     &ctrl, &idx))
2348         {
2349             return ctrl;
2350         }
2351     }
2352 
2353     // no current page, just find the first tab ctrl
2354     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2355     size_t i, pane_count = all_panes.GetCount();
2356     for (i = 0; i < pane_count; ++i)
2357     {
2358         if (all_panes.Item(i).name == wxT("dummy"))
2359             continue;
2360 
2361         wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2362         return tabframe->m_tabs;
2363     }
2364 
2365     // If there is no tabframe at all, create one
2366     wxTabFrame* tabframe = new wxTabFrame;
2367     tabframe->SetTabCtrlHeight(m_tabCtrlHeight);
2368     tabframe->m_tabs = new wxAuiTabCtrl(this,
2369                                         m_tabIdCounter++,
2370                                         wxDefaultPosition,
2371                                         wxDefaultSize,
2372                                         wxNO_BORDER|wxWANTS_CHARS);
2373     tabframe->m_tabs->SetFlags(m_flags);
2374     tabframe->m_tabs->SetArtProvider(m_tabs.GetArtProvider()->Clone());
2375     m_mgr.AddPane(tabframe,
2376                   wxAuiPaneInfo().Center().CaptionVisible(false));
2377 
2378     m_mgr.Update();
2379 
2380     return tabframe->m_tabs;
2381 }
2382 
2383 // FindTab() finds the tab control that currently contains the window as well
2384 // as the index of the window in the tab control.  It returns true if the
2385 // window was found, otherwise false.
FindTab(wxWindow * page,wxAuiTabCtrl ** ctrl,int * idx)2386 bool wxAuiNotebook::FindTab(wxWindow* page, wxAuiTabCtrl** ctrl, int* idx)
2387 {
2388     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2389     size_t i, pane_count = all_panes.GetCount();
2390     for (i = 0; i < pane_count; ++i)
2391     {
2392         if (all_panes.Item(i).name == wxT("dummy"))
2393             continue;
2394 
2395         wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2396 
2397         int page_idx = tabframe->m_tabs->GetIdxFromWindow(page);
2398         if (page_idx != -1)
2399         {
2400             *ctrl = tabframe->m_tabs;
2401             *idx = page_idx;
2402             return true;
2403         }
2404     }
2405 
2406     return false;
2407 }
2408 
Split(size_t page,int direction)2409 void wxAuiNotebook::Split(size_t page, int direction)
2410 {
2411     wxSize cli_size = GetClientSize();
2412 
2413     // get the page's window pointer
2414     wxWindow* wnd = GetPage(page);
2415     if (!wnd)
2416         return;
2417 
2418     // notebooks with 1 or less pages can't be split
2419     if (GetPageCount() < 2)
2420         return;
2421 
2422     // find out which tab control the page currently belongs to
2423     wxAuiTabCtrl *src_tabs, *dest_tabs;
2424     int src_idx = -1;
2425     src_tabs = NULL;
2426     if (!FindTab(wnd, &src_tabs, &src_idx))
2427         return;
2428     if (!src_tabs || src_idx == -1)
2429         return;
2430 
2431     // choose a split size
2432     wxSize split_size;
2433     if (GetPageCount() > 2)
2434     {
2435         split_size = CalculateNewSplitSize();
2436     }
2437     else
2438     {
2439         // because there are two panes, always split them
2440         // equally
2441         split_size = GetClientSize();
2442         split_size.x /= 2;
2443         split_size.y /= 2;
2444     }
2445 
2446 
2447     // create a new tab frame
2448     wxTabFrame* new_tabs = new wxTabFrame;
2449     new_tabs->m_rect = wxRect(wxPoint(0,0), split_size);
2450     new_tabs->SetTabCtrlHeight(m_tabCtrlHeight);
2451     new_tabs->m_tabs = new wxAuiTabCtrl(this,
2452                                         m_tabIdCounter++,
2453                                         wxDefaultPosition,
2454                                         wxDefaultSize,
2455                                         wxNO_BORDER|wxWANTS_CHARS);
2456     new_tabs->m_tabs->SetArtProvider(m_tabs.GetArtProvider()->Clone());
2457     new_tabs->m_tabs->SetFlags(m_flags);
2458     dest_tabs = new_tabs->m_tabs;
2459 
2460     // create a pane info structure with the information
2461     // about where the pane should be added
2462     wxAuiPaneInfo paneInfo = wxAuiPaneInfo().Bottom().CaptionVisible(false);
2463     wxPoint mouse_pt;
2464 
2465     if (direction == wxLEFT)
2466     {
2467         paneInfo.Left();
2468         mouse_pt = wxPoint(0, cli_size.y/2);
2469     }
2470     else if (direction == wxRIGHT)
2471     {
2472         paneInfo.Right();
2473         mouse_pt = wxPoint(cli_size.x, cli_size.y/2);
2474     }
2475     else if (direction == wxTOP)
2476     {
2477         paneInfo.Top();
2478         mouse_pt = wxPoint(cli_size.x/2, 0);
2479     }
2480     else if (direction == wxBOTTOM)
2481     {
2482         paneInfo.Bottom();
2483         mouse_pt = wxPoint(cli_size.x/2, cli_size.y);
2484     }
2485 
2486     m_mgr.AddPane(new_tabs, paneInfo, mouse_pt);
2487     m_mgr.Update();
2488 
2489     // remove the page from the source tabs
2490     wxAuiNotebookPage page_info = src_tabs->GetPage(src_idx);
2491     page_info.active = false;
2492     src_tabs->RemovePage(page_info.window);
2493     if (src_tabs->GetPageCount() > 0)
2494     {
2495         src_tabs->SetActivePage((size_t)0);
2496         src_tabs->DoShowHide();
2497         src_tabs->Refresh();
2498     }
2499 
2500 
2501     // add the page to the destination tabs
2502     dest_tabs->InsertPage(page_info.window, page_info, 0);
2503 
2504     if (src_tabs->GetPageCount() == 0)
2505     {
2506         RemoveEmptyTabFrames();
2507     }
2508 
2509     DoSizing();
2510     dest_tabs->DoShowHide();
2511     dest_tabs->Refresh();
2512 
2513     // force the set selection function reset the selection
2514     m_curPage = -1;
2515 
2516     // set the active page to the one we just split off
2517     SetSelectionToPage(page_info);
2518 
2519     UpdateHintWindowSize();
2520 }
2521 
2522 
OnSize(wxSizeEvent & evt)2523 void wxAuiNotebook::OnSize(wxSizeEvent& evt)
2524 {
2525     UpdateHintWindowSize();
2526 
2527     evt.Skip();
2528 }
2529 
OnTabClicked(wxAuiNotebookEvent & evt)2530 void wxAuiNotebook::OnTabClicked(wxAuiNotebookEvent& evt)
2531 {
2532     wxAuiTabCtrl* ctrl = (wxAuiTabCtrl*)evt.GetEventObject();
2533     wxASSERT(ctrl != NULL);
2534 
2535     wxWindow* wnd = ctrl->GetWindowFromIdx(evt.GetSelection());
2536     wxASSERT(wnd != NULL);
2537 
2538     SetSelectionToWindow(wnd);
2539 }
2540 
OnTabBgDClick(wxAuiNotebookEvent & evt)2541 void wxAuiNotebook::OnTabBgDClick(wxAuiNotebookEvent& evt)
2542 {
2543     // select the tab ctrl which received the db click
2544     int selection;
2545     wxWindow* wnd;
2546     wxAuiTabCtrl* ctrl = (wxAuiTabCtrl*)evt.GetEventObject();
2547     if (   (ctrl != NULL)
2548         && ((selection = ctrl->GetActivePage()) != wxNOT_FOUND)
2549         && ((wnd = ctrl->GetWindowFromIdx(selection)) != NULL))
2550     {
2551         SetSelectionToWindow(wnd);
2552     }
2553 
2554     // notify owner that the tabbar background has been double-clicked
2555     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_BG_DCLICK, m_windowId);
2556     e.SetEventObject(this);
2557     GetEventHandler()->ProcessEvent(e);
2558 }
2559 
OnTabBeginDrag(wxAuiNotebookEvent &)2560 void wxAuiNotebook::OnTabBeginDrag(wxAuiNotebookEvent&)
2561 {
2562     m_lastDragX = 0;
2563 }
2564 
OnTabDragMotion(wxAuiNotebookEvent & evt)2565 void wxAuiNotebook::OnTabDragMotion(wxAuiNotebookEvent& evt)
2566 {
2567     wxPoint screen_pt = ::wxGetMousePosition();
2568     wxPoint client_pt = ScreenToClient(screen_pt);
2569     wxPoint zero(0,0);
2570 
2571     wxAuiTabCtrl* src_tabs = (wxAuiTabCtrl*)evt.GetEventObject();
2572     wxAuiTabCtrl* dest_tabs = GetTabCtrlFromPoint(client_pt);
2573 
2574     if (dest_tabs == src_tabs)
2575     {
2576         if (src_tabs)
2577         {
2578             src_tabs->SetCursor(wxCursor(wxCURSOR_ARROW));
2579         }
2580 
2581         // always hide the hint for inner-tabctrl drag
2582         m_mgr.HideHint();
2583 
2584         // if tab moving is not allowed, leave
2585         if (!(m_flags & wxAUI_NB_TAB_MOVE))
2586         {
2587             return;
2588         }
2589 
2590         wxPoint pt = dest_tabs->ScreenToClient(screen_pt);
2591         wxWindow* dest_location_tab;
2592 
2593         // this is an inner-tab drag/reposition
2594         if (dest_tabs->TabHitTest(pt.x, pt.y, &dest_location_tab))
2595         {
2596             int src_idx = evt.GetSelection();
2597             int dest_idx = dest_tabs->GetIdxFromWindow(dest_location_tab);
2598 
2599             // prevent jumpy drag
2600             if ((src_idx == dest_idx) || dest_idx == -1 ||
2601                 (src_idx > dest_idx && m_lastDragX <= pt.x) ||
2602                 (src_idx < dest_idx && m_lastDragX >= pt.x))
2603             {
2604                 m_lastDragX = pt.x;
2605                 return;
2606             }
2607 
2608 
2609             wxWindow* src_tab = dest_tabs->GetWindowFromIdx(src_idx);
2610             dest_tabs->MovePage(src_tab, dest_idx);
2611             m_tabs.MovePage(m_tabs.GetPage(src_idx).window, dest_idx);
2612             dest_tabs->SetActivePage((size_t)dest_idx);
2613             dest_tabs->DoShowHide();
2614             dest_tabs->Refresh();
2615             m_lastDragX = pt.x;
2616 
2617         }
2618 
2619         return;
2620     }
2621 
2622 
2623     // if external drag is allowed, check if the tab is being dragged
2624     // over a different wxAuiNotebook control
2625     if (m_flags & wxAUI_NB_TAB_EXTERNAL_MOVE)
2626     {
2627         wxWindow* tab_ctrl = ::wxFindWindowAtPoint(screen_pt);
2628 
2629         // if we aren't over any window, stop here
2630         if (!tab_ctrl)
2631             return;
2632 
2633         // make sure we are not over the hint window
2634         if (!wxDynamicCast(tab_ctrl, wxFrame))
2635         {
2636             while (tab_ctrl)
2637             {
2638                 if (wxDynamicCast(tab_ctrl, wxAuiTabCtrl))
2639                     break;
2640                 tab_ctrl = tab_ctrl->GetParent();
2641             }
2642 
2643             if (tab_ctrl)
2644             {
2645                 wxAuiNotebook* nb = (wxAuiNotebook*)tab_ctrl->GetParent();
2646 
2647                 if (nb != this)
2648                 {
2649                     wxRect hint_rect = tab_ctrl->GetClientRect();
2650                     tab_ctrl->ClientToScreen(&hint_rect.x, &hint_rect.y);
2651                     m_mgr.ShowHint(hint_rect);
2652                     return;
2653                 }
2654             }
2655         }
2656         else
2657         {
2658             if (!dest_tabs)
2659             {
2660                 // we are either over a hint window, or not over a tab
2661                 // window, and there is no where to drag to, so exit
2662                 return;
2663             }
2664         }
2665     }
2666 
2667 
2668     // if there are less than two panes, split can't happen, so leave
2669     if (m_tabs.GetPageCount() < 2)
2670         return;
2671 
2672     // if tab moving is not allowed, leave
2673     if (!(m_flags & wxAUI_NB_TAB_SPLIT))
2674         return;
2675 
2676 
2677     if (src_tabs)
2678     {
2679         src_tabs->SetCursor(wxCursor(wxCURSOR_SIZING));
2680     }
2681 
2682 
2683     if (dest_tabs)
2684     {
2685         wxRect hint_rect = dest_tabs->GetRect();
2686         ClientToScreen(&hint_rect.x, &hint_rect.y);
2687         m_mgr.ShowHint(hint_rect);
2688     }
2689     else
2690     {
2691         m_mgr.DrawHintRect(m_dummyWnd, client_pt, zero);
2692     }
2693 }
2694 
2695 
2696 
OnTabEndDrag(wxAuiNotebookEvent & evt)2697 void wxAuiNotebook::OnTabEndDrag(wxAuiNotebookEvent& evt)
2698 {
2699     m_mgr.HideHint();
2700 
2701 
2702     wxAuiTabCtrl* src_tabs = (wxAuiTabCtrl*)evt.GetEventObject();
2703     wxCHECK_RET( src_tabs, wxT("no source object?") );
2704 
2705     src_tabs->SetCursor(wxCursor(wxCURSOR_ARROW));
2706 
2707     // get the mouse position, which will be used to determine the drop point
2708     wxPoint mouse_screen_pt = ::wxGetMousePosition();
2709     wxPoint mouse_client_pt = ScreenToClient(mouse_screen_pt);
2710 
2711     // Update our selection (it may be updated again below but the code below
2712     // can also return without doing anything else and this ensures that the
2713     // selection is updated even then).
2714     m_curPage = src_tabs->GetActivePage();
2715 
2716     // check for an external move
2717     if (m_flags & wxAUI_NB_TAB_EXTERNAL_MOVE)
2718     {
2719         wxWindow* tab_ctrl = ::wxFindWindowAtPoint(mouse_screen_pt);
2720 
2721         while (tab_ctrl)
2722         {
2723             if (wxDynamicCast(tab_ctrl, wxAuiTabCtrl))
2724                 break;
2725             tab_ctrl = tab_ctrl->GetParent();
2726         }
2727 
2728         if (tab_ctrl)
2729         {
2730             wxAuiNotebook* nb = (wxAuiNotebook*)tab_ctrl->GetParent();
2731 
2732             if (nb != this)
2733             {
2734                 // find out from the destination control
2735                 // if it's ok to drop this tab here
2736                 wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_ALLOW_DND, m_windowId);
2737                 e.SetSelection(evt.GetSelection());
2738                 e.SetOldSelection(evt.GetSelection());
2739                 e.SetEventObject(this);
2740                 e.SetDragSource(this);
2741                 e.Veto(); // dropping must be explicitly approved by control owner
2742 
2743                 nb->GetEventHandler()->ProcessEvent(e);
2744 
2745                 if (!e.IsAllowed())
2746                 {
2747                     // no answer or negative answer
2748                     m_mgr.HideHint();
2749                     return;
2750                 }
2751 
2752                 // drop was allowed
2753                 int src_idx = evt.GetSelection();
2754                 wxWindow* src_page = src_tabs->GetWindowFromIdx(src_idx);
2755 
2756                 // Check that it's not an impossible parent relationship
2757                 wxWindow* p = nb;
2758                 while (p && !p->IsTopLevel())
2759                 {
2760                     if (p == src_page)
2761                     {
2762                         return;
2763                     }
2764                     p = p->GetParent();
2765                 }
2766 
2767                 // get main index of the page
2768                 int main_idx = m_tabs.GetIdxFromWindow(src_page);
2769                 wxCHECK_RET( main_idx != wxNOT_FOUND, wxT("no source page?") );
2770 
2771 
2772                 // make a copy of the page info
2773                 wxAuiNotebookPage page_info = m_tabs.GetPage(main_idx);
2774 
2775                 // remove the page from the source notebook
2776                 RemovePage(main_idx);
2777 
2778                 // reparent the page
2779                 src_page->Reparent(nb);
2780 
2781 
2782                 // found out the insert idx
2783                 wxAuiTabCtrl* dest_tabs = (wxAuiTabCtrl*)tab_ctrl;
2784                 wxPoint pt = dest_tabs->ScreenToClient(mouse_screen_pt);
2785 
2786                 wxWindow* target = NULL;
2787                 int insert_idx = -1;
2788                 dest_tabs->TabHitTest(pt.x, pt.y, &target);
2789                 if (target)
2790                 {
2791                     insert_idx = dest_tabs->GetIdxFromWindow(target);
2792                 }
2793 
2794 
2795                 // add the page to the new notebook
2796                 if (insert_idx == -1)
2797                     insert_idx = dest_tabs->GetPageCount();
2798                 dest_tabs->InsertPage(page_info.window, page_info, insert_idx);
2799                 nb->m_tabs.InsertPage(page_info.window, page_info, insert_idx);
2800 
2801                 nb->DoSizing();
2802                 dest_tabs->SetActivePage(insert_idx);
2803                 dest_tabs->DoShowHide();
2804                 dest_tabs->Refresh();
2805 
2806                 // set the selection in the destination tab control
2807                 nb->DoModifySelection(insert_idx, false);
2808 
2809                 // notify owner that the tab has been dragged
2810                 wxAuiNotebookEvent e2(wxEVT_AUINOTEBOOK_DRAG_DONE, m_windowId);
2811                 e2.SetSelection(evt.GetSelection());
2812                 e2.SetOldSelection(evt.GetSelection());
2813                 e2.SetEventObject(this);
2814                 GetEventHandler()->ProcessEvent(e2);
2815 
2816                 return;
2817             }
2818         }
2819     }
2820 
2821 
2822 
2823 
2824     // only perform a tab split if it's allowed
2825     wxAuiTabCtrl* dest_tabs = NULL;
2826 
2827     if ((m_flags & wxAUI_NB_TAB_SPLIT) && m_tabs.GetPageCount() >= 2)
2828     {
2829         // If the pointer is in an existing tab frame, do a tab insert
2830         wxWindow* hit_wnd = ::wxFindWindowAtPoint(mouse_screen_pt);
2831         wxTabFrame* tab_frame = (wxTabFrame*)GetTabFrameFromTabCtrl(hit_wnd);
2832         int insert_idx = -1;
2833         if (tab_frame)
2834         {
2835             dest_tabs = tab_frame->m_tabs;
2836 
2837             if (dest_tabs == src_tabs)
2838             {
2839                 m_curPage = evt.GetSelection();
2840                 return;
2841             }
2842 
2843             wxPoint pt = dest_tabs->ScreenToClient(mouse_screen_pt);
2844             wxWindow* target = NULL;
2845             dest_tabs->TabHitTest(pt.x, pt.y, &target);
2846             if (target)
2847             {
2848                 insert_idx = dest_tabs->GetIdxFromWindow(target);
2849             }
2850         }
2851         else
2852         {
2853             wxPoint zero(0,0);
2854             wxRect rect = m_mgr.CalculateHintRect(m_dummyWnd,
2855                                                   mouse_client_pt,
2856                                                   zero);
2857             if (rect.IsEmpty())
2858             {
2859                 // there is no suitable drop location here, exit out
2860                 return;
2861             }
2862 
2863             // If there is no tabframe at all, create one
2864             wxTabFrame* new_tabs = new wxTabFrame;
2865             new_tabs->m_rect = wxRect(wxPoint(0,0), CalculateNewSplitSize());
2866             new_tabs->SetTabCtrlHeight(m_tabCtrlHeight);
2867             new_tabs->m_tabs = new wxAuiTabCtrl(this,
2868                                                 m_tabIdCounter++,
2869                                                 wxDefaultPosition,
2870                                                 wxDefaultSize,
2871                                                 wxNO_BORDER|wxWANTS_CHARS);
2872             new_tabs->m_tabs->SetArtProvider(m_tabs.GetArtProvider()->Clone());
2873             new_tabs->m_tabs->SetFlags(m_flags);
2874 
2875             m_mgr.AddPane(new_tabs,
2876                           wxAuiPaneInfo().Bottom().CaptionVisible(false),
2877                           mouse_client_pt);
2878             m_mgr.Update();
2879             dest_tabs = new_tabs->m_tabs;
2880         }
2881 
2882 
2883 
2884         // remove the page from the source tabs
2885         wxAuiNotebookPage page_info = src_tabs->GetPage(evt.GetSelection());
2886         page_info.active = false;
2887         src_tabs->RemovePage(page_info.window);
2888         if (src_tabs->GetPageCount() > 0)
2889         {
2890             src_tabs->SetActivePage((size_t)0);
2891             src_tabs->DoShowHide();
2892             src_tabs->Refresh();
2893         }
2894 
2895 
2896 
2897         // add the page to the destination tabs
2898         if (insert_idx == -1)
2899             insert_idx = dest_tabs->GetPageCount();
2900         dest_tabs->InsertPage(page_info.window, page_info, insert_idx);
2901 
2902         if (src_tabs->GetPageCount() == 0)
2903         {
2904             RemoveEmptyTabFrames();
2905         }
2906 
2907         DoSizing();
2908         dest_tabs->DoShowHide();
2909         dest_tabs->Refresh();
2910 
2911         // force the set selection function reset the selection
2912         m_curPage = -1;
2913 
2914         // set the active page to the one we just split off
2915         SetSelectionToPage(page_info);
2916 
2917         UpdateHintWindowSize();
2918     }
2919 
2920     // notify owner that the tab has been dragged
2921     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_DRAG_DONE, m_windowId);
2922     e.SetSelection(evt.GetSelection());
2923     e.SetOldSelection(evt.GetSelection());
2924     e.SetEventObject(this);
2925     GetEventHandler()->ProcessEvent(e);
2926 }
2927 
2928 
2929 
OnTabCancelDrag(wxAuiNotebookEvent & command_evt)2930 void wxAuiNotebook::OnTabCancelDrag(wxAuiNotebookEvent& command_evt)
2931 {
2932     wxAuiNotebookEvent& evt = (wxAuiNotebookEvent&)command_evt;
2933 
2934     m_mgr.HideHint();
2935 
2936     wxAuiTabCtrl* src_tabs = (wxAuiTabCtrl*)evt.GetEventObject();
2937     wxCHECK_RET( src_tabs, wxT("no source object?") );
2938 
2939     src_tabs->SetCursor(wxCursor(wxCURSOR_ARROW));
2940 }
2941 
GetTabCtrlFromPoint(const wxPoint & pt)2942 wxAuiTabCtrl* wxAuiNotebook::GetTabCtrlFromPoint(const wxPoint& pt)
2943 {
2944     // if we've just removed the last tab from the source
2945     // tab set, the remove the tab control completely
2946     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2947     size_t i, pane_count = all_panes.GetCount();
2948     for (i = 0; i < pane_count; ++i)
2949     {
2950         if (all_panes.Item(i).name == wxT("dummy"))
2951             continue;
2952 
2953         wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2954         if (tabframe->m_tab_rect.Contains(pt))
2955             return tabframe->m_tabs;
2956     }
2957 
2958     return NULL;
2959 }
2960 
GetTabFrameFromTabCtrl(wxWindow * tab_ctrl)2961 wxWindow* wxAuiNotebook::GetTabFrameFromTabCtrl(wxWindow* tab_ctrl)
2962 {
2963     // if we've just removed the last tab from the source
2964     // tab set, the remove the tab control completely
2965     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2966     size_t i, pane_count = all_panes.GetCount();
2967     for (i = 0; i < pane_count; ++i)
2968     {
2969         if (all_panes.Item(i).name == wxT("dummy"))
2970             continue;
2971 
2972         wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2973         if (tabframe->m_tabs == tab_ctrl)
2974         {
2975             return tabframe;
2976         }
2977     }
2978 
2979     return NULL;
2980 }
2981 
RemoveEmptyTabFrames()2982 void wxAuiNotebook::RemoveEmptyTabFrames()
2983 {
2984     // if we've just removed the last tab from the source
2985     // tab set, the remove the tab control completely
2986     wxAuiPaneInfoArray all_panes = m_mgr.GetAllPanes();
2987     size_t i, pane_count = all_panes.GetCount();
2988     for (i = 0; i < pane_count; ++i)
2989     {
2990         if (all_panes.Item(i).name == wxT("dummy"))
2991             continue;
2992 
2993         wxTabFrame* tab_frame = (wxTabFrame*)all_panes.Item(i).window;
2994         if (tab_frame->m_tabs->GetPageCount() == 0)
2995         {
2996             m_mgr.DetachPane(tab_frame);
2997 
2998             // use pending delete because sometimes during
2999             // window closing, refreshs are pending
3000             if (!wxPendingDelete.Member(tab_frame->m_tabs))
3001                 wxPendingDelete.Append(tab_frame->m_tabs);
3002 
3003             tab_frame->m_tabs = NULL;
3004 
3005             delete tab_frame;
3006         }
3007     }
3008 
3009 
3010     // check to see if there is still a center pane;
3011     // if there isn't, make a frame the center pane
3012     wxAuiPaneInfoArray panes = m_mgr.GetAllPanes();
3013     pane_count = panes.GetCount();
3014     wxWindow* first_good = NULL;
3015     bool center_found = false;
3016     for (i = 0; i < pane_count; ++i)
3017     {
3018         if (panes.Item(i).name == wxT("dummy"))
3019             continue;
3020         if (panes.Item(i).dock_direction == wxAUI_DOCK_CENTRE)
3021             center_found = true;
3022         if (!first_good)
3023             first_good = panes.Item(i).window;
3024     }
3025 
3026     if (!center_found && first_good)
3027     {
3028         m_mgr.GetPane(first_good).Centre();
3029     }
3030 
3031     if (!m_isBeingDeleted)
3032         m_mgr.Update();
3033 }
3034 
OnChildFocusNotebook(wxChildFocusEvent & evt)3035 void wxAuiNotebook::OnChildFocusNotebook(wxChildFocusEvent& evt)
3036 {
3037     evt.Skip();
3038 
3039     // if we're dragging a tab, don't change the current selection.
3040     // This code prevents a bug that used to happen when the hint window
3041     // was hidden.  In the bug, the focus would return to the notebook
3042     // child, which would then enter this handler and call
3043     // SetSelection, which is not desired turn tab dragging.
3044 
3045     wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
3046     size_t i, pane_count = all_panes.GetCount();
3047     for (i = 0; i < pane_count; ++i)
3048     {
3049         wxAuiPaneInfo& pane = all_panes.Item(i);
3050         if (pane.name == wxT("dummy"))
3051             continue;
3052         wxTabFrame* tabframe = (wxTabFrame*)pane.window;
3053         if (tabframe->m_tabs->IsDragging())
3054             return;
3055     }
3056 
3057 
3058     // find the page containing the focused child
3059     wxWindow* win = evt.GetWindow();
3060     while ( win )
3061     {
3062         // pages have the notebook as the parent, so stop when we reach one
3063         // (and also stop in the impossible case of no parent at all)
3064         wxWindow* const parent = win->GetParent();
3065         if ( !parent || parent == this )
3066             break;
3067 
3068         win = parent;
3069     }
3070 
3071     // change the tab selection to this page
3072     int idx = m_tabs.GetIdxFromWindow(win);
3073     if (idx != -1 && idx != m_curPage)
3074     {
3075         SetSelection(idx);
3076     }
3077 }
3078 
OnNavigationKeyNotebook(wxNavigationKeyEvent & event)3079 void wxAuiNotebook::OnNavigationKeyNotebook(wxNavigationKeyEvent& event)
3080 {
3081     if ( event.IsWindowChange() ) {
3082         // change pages
3083         // FIXME: the problem with this is that if we have a split notebook,
3084         // we selection may go all over the place.
3085         AdvanceSelection(event.GetDirection());
3086     }
3087     else {
3088         // we get this event in 3 cases
3089         //
3090         // a) one of our pages might have generated it because the user TABbed
3091         // out from it in which case we should propagate the event upwards and
3092         // our parent will take care of setting the focus to prev/next sibling
3093         //
3094         // or
3095         //
3096         // b) the parent panel wants to give the focus to us so that we
3097         // forward it to our selected page. We can't deal with this in
3098         // OnSetFocus() because we don't know which direction the focus came
3099         // from in this case and so can't choose between setting the focus to
3100         // first or last panel child
3101         //
3102         // or
3103         //
3104         // c) we ourselves (see MSWTranslateMessage) generated the event
3105         //
3106         wxWindow * const parent = GetParent();
3107 
3108         // the wxObject* casts are required to avoid MinGW GCC 2.95.3 ICE
3109         const bool isFromParent = event.GetEventObject() == (wxObject*) parent;
3110         const bool isFromSelf = event.GetEventObject() == (wxObject*) this;
3111 
3112         if ( isFromParent || isFromSelf )
3113         {
3114             // no, it doesn't come from child, case (b) or (c): forward to a
3115             // page but only if direction is backwards (TAB) or from ourselves,
3116             if ( GetSelection() != wxNOT_FOUND &&
3117                     (!event.GetDirection() || isFromSelf) )
3118             {
3119                 // so that the page knows that the event comes from it's parent
3120                 // and is being propagated downwards
3121                 event.SetEventObject(this);
3122 
3123                 wxWindow *page = GetPage(GetSelection());
3124                 if ( !page->GetEventHandler()->ProcessEvent(event) )
3125                 {
3126                     page->SetFocus();
3127                 }
3128                 //else: page manages focus inside it itself
3129             }
3130             else // otherwise set the focus to the notebook itself
3131             {
3132                 SetFocus();
3133             }
3134         }
3135         else
3136         {
3137             // it comes from our child, case (a), pass to the parent, but only
3138             // if the direction is forwards. Otherwise set the focus to the
3139             // notebook itself. The notebook is always the 'first' control of a
3140             // page.
3141             if ( !event.GetDirection() )
3142             {
3143                 SetFocus();
3144             }
3145             else if ( parent )
3146             {
3147                 event.SetCurrentFocus(this);
3148                 parent->GetEventHandler()->ProcessEvent(event);
3149             }
3150         }
3151     }
3152 }
3153 
OnTabButton(wxAuiNotebookEvent & evt)3154 void wxAuiNotebook::OnTabButton(wxAuiNotebookEvent& evt)
3155 {
3156     wxAuiTabCtrl* tabs = (wxAuiTabCtrl*)evt.GetEventObject();
3157 
3158     int button_id = evt.GetInt();
3159 
3160     if (button_id == wxAUI_BUTTON_CLOSE)
3161     {
3162         int selection = evt.GetSelection();
3163 
3164         if (selection == -1)
3165         {
3166             // if the close button is to the right, use the active
3167             // page selection to determine which page to close
3168             selection = tabs->GetActivePage();
3169         }
3170 
3171         if (selection != -1)
3172         {
3173             wxWindow* close_wnd = tabs->GetWindowFromIdx(selection);
3174 
3175             // ask owner if it's ok to close the tab
3176             wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_PAGE_CLOSE, m_windowId);
3177             e.SetSelection(m_tabs.GetIdxFromWindow(close_wnd));
3178             const int idx = m_tabs.GetIdxFromWindow(close_wnd);
3179             e.SetSelection(idx);
3180             e.SetOldSelection(evt.GetSelection());
3181             e.SetEventObject(this);
3182             GetEventHandler()->ProcessEvent(e);
3183             if (!e.IsAllowed())
3184                 return;
3185 
3186 
3187 #if wxUSE_MDI
3188             if (wxDynamicCast(close_wnd, wxAuiMDIChildFrame))
3189             {
3190                 close_wnd->Close();
3191             }
3192             else
3193 #endif
3194             {
3195                 int main_idx = m_tabs.GetIdxFromWindow(close_wnd);
3196                 wxCHECK_RET( main_idx != wxNOT_FOUND, wxT("no page to delete?") );
3197 
3198                 DeletePage(main_idx);
3199             }
3200 
3201             // notify owner that the tab has been closed
3202             wxAuiNotebookEvent e2(wxEVT_AUINOTEBOOK_PAGE_CLOSED, m_windowId);
3203             e2.SetSelection(idx);
3204             e2.SetEventObject(this);
3205             GetEventHandler()->ProcessEvent(e2);
3206         }
3207     }
3208 }
3209 
3210 
OnTabMiddleDown(wxAuiNotebookEvent & evt)3211 void wxAuiNotebook::OnTabMiddleDown(wxAuiNotebookEvent& evt)
3212 {
3213     // patch event through to owner
3214     wxAuiTabCtrl* tabs = (wxAuiTabCtrl*)evt.GetEventObject();
3215     wxWindow* wnd = tabs->GetWindowFromIdx(evt.GetSelection());
3216 
3217     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_TAB_MIDDLE_DOWN, m_windowId);
3218     e.SetSelection(m_tabs.GetIdxFromWindow(wnd));
3219     e.SetEventObject(this);
3220     GetEventHandler()->ProcessEvent(e);
3221 }
3222 
OnTabMiddleUp(wxAuiNotebookEvent & evt)3223 void wxAuiNotebook::OnTabMiddleUp(wxAuiNotebookEvent& evt)
3224 {
3225     // if the wxAUI_NB_MIDDLE_CLICK_CLOSE is specified, middle
3226     // click should act like a tab close action.  However, first
3227     // give the owner an opportunity to handle the middle up event
3228     // for custom action
3229 
3230     wxAuiTabCtrl* tabs = (wxAuiTabCtrl*)evt.GetEventObject();
3231     wxWindow* wnd = tabs->GetWindowFromIdx(evt.GetSelection());
3232 
3233     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_TAB_MIDDLE_UP, m_windowId);
3234     e.SetSelection(m_tabs.GetIdxFromWindow(wnd));
3235     e.SetEventObject(this);
3236     if (GetEventHandler()->ProcessEvent(e))
3237         return;
3238     if (!e.IsAllowed())
3239         return;
3240 
3241     // check if we are supposed to close on middle-up
3242     if ((m_flags & wxAUI_NB_MIDDLE_CLICK_CLOSE) == 0)
3243         return;
3244 
3245     // simulate the user pressing the close button on the tab
3246     evt.SetInt(wxAUI_BUTTON_CLOSE);
3247     OnTabButton(evt);
3248 }
3249 
OnTabRightDown(wxAuiNotebookEvent & evt)3250 void wxAuiNotebook::OnTabRightDown(wxAuiNotebookEvent& evt)
3251 {
3252     // patch event through to owner
3253     wxAuiTabCtrl* tabs = (wxAuiTabCtrl*)evt.GetEventObject();
3254     wxWindow* wnd = tabs->GetWindowFromIdx(evt.GetSelection());
3255 
3256     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_TAB_RIGHT_DOWN, m_windowId);
3257     e.SetSelection(m_tabs.GetIdxFromWindow(wnd));
3258     e.SetEventObject(this);
3259     GetEventHandler()->ProcessEvent(e);
3260 }
3261 
OnTabRightUp(wxAuiNotebookEvent & evt)3262 void wxAuiNotebook::OnTabRightUp(wxAuiNotebookEvent& evt)
3263 {
3264     // patch event through to owner
3265     wxAuiTabCtrl* tabs = (wxAuiTabCtrl*)evt.GetEventObject();
3266     wxWindow* wnd = tabs->GetWindowFromIdx(evt.GetSelection());
3267 
3268     wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_TAB_RIGHT_UP, m_windowId);
3269     e.SetSelection(m_tabs.GetIdxFromWindow(wnd));
3270     e.SetEventObject(this);
3271     GetEventHandler()->ProcessEvent(e);
3272 }
3273 
3274 // Sets the normal font
SetNormalFont(const wxFont & font)3275 void wxAuiNotebook::SetNormalFont(const wxFont& font)
3276 {
3277     m_normalFont = font;
3278     GetArtProvider()->SetNormalFont(font);
3279 }
3280 
3281 // Sets the selected tab font
SetSelectedFont(const wxFont & font)3282 void wxAuiNotebook::SetSelectedFont(const wxFont& font)
3283 {
3284     m_selectedFont = font;
3285     GetArtProvider()->SetSelectedFont(font);
3286 }
3287 
3288 // Sets the measuring font
SetMeasuringFont(const wxFont & font)3289 void wxAuiNotebook::SetMeasuringFont(const wxFont& font)
3290 {
3291     GetArtProvider()->SetMeasuringFont(font);
3292 }
3293 
3294 // Sets the tab font
SetFont(const wxFont & font)3295 bool wxAuiNotebook::SetFont(const wxFont& font)
3296 {
3297     wxControl::SetFont(font);
3298 
3299     wxFont normalFont(font);
3300     wxFont selectedFont(normalFont);
3301     selectedFont.SetWeight(wxFONTWEIGHT_BOLD);
3302 
3303     SetNormalFont(normalFont);
3304     SetSelectedFont(selectedFont);
3305     SetMeasuringFont(selectedFont);
3306 
3307     return true;
3308 }
3309 
3310 // Gets the tab control height
GetTabCtrlHeight() const3311 int wxAuiNotebook::GetTabCtrlHeight() const
3312 {
3313     return m_tabCtrlHeight;
3314 }
3315 
3316 // Gets the height of the notebook for a given page height
GetHeightForPageHeight(int pageHeight)3317 int wxAuiNotebook::GetHeightForPageHeight(int pageHeight)
3318 {
3319     UpdateTabCtrlHeight();
3320 
3321     int tabCtrlHeight = GetTabCtrlHeight();
3322     int decorHeight = 2;
3323     return tabCtrlHeight + pageHeight + decorHeight;
3324 }
3325 
3326 // Shows the window menu
ShowWindowMenu()3327 bool wxAuiNotebook::ShowWindowMenu()
3328 {
3329     wxAuiTabCtrl* tabCtrl = GetActiveTabCtrl();
3330 
3331     int idx = tabCtrl->GetArtProvider()->ShowDropDown(tabCtrl, tabCtrl->GetPages(), tabCtrl->GetActivePage());
3332 
3333     if (idx != -1)
3334     {
3335         wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_PAGE_CHANGING, tabCtrl->GetId());
3336         e.SetSelection(idx);
3337         e.SetOldSelection(tabCtrl->GetActivePage());
3338         e.SetEventObject(tabCtrl);
3339         GetEventHandler()->ProcessEvent(e);
3340 
3341         return true;
3342     }
3343     else
3344         return false;
3345 }
3346 
DoThaw()3347 void wxAuiNotebook::DoThaw()
3348 {
3349     DoSizing();
3350 
3351     wxBookCtrlBase::DoThaw();
3352 }
3353 
SetPageSize(const wxSize & WXUNUSED (size))3354 void wxAuiNotebook::SetPageSize (const wxSize& WXUNUSED(size))
3355 {
3356     wxFAIL_MSG("Not implemented for wxAuiNotebook");
3357 }
3358 
HitTest(const wxPoint & pt,long * flags) const3359 int wxAuiNotebook::HitTest (const wxPoint &pt, long *flags) const
3360 {
3361     wxWindow *w = NULL;
3362     long position = wxBK_HITTEST_NOWHERE;
3363     const wxAuiPaneInfoArray& all_panes = const_cast<wxAuiManager&>(m_mgr).GetAllPanes();
3364     const size_t pane_count = all_panes.GetCount();
3365     for (size_t i = 0; i < pane_count; ++i)
3366     {
3367         if (all_panes.Item(i).name == wxT("dummy"))
3368             continue;
3369 
3370         wxTabFrame* tabframe = (wxTabFrame*) all_panes.Item(i).window;
3371         if (tabframe->m_tab_rect.Contains(pt))
3372         {
3373             wxPoint tabpos = tabframe->m_tabs->ScreenToClient(ClientToScreen(pt));
3374             if (tabframe->m_tabs->TabHitTest(tabpos.x, tabpos.y, &w))
3375                 position = wxBK_HITTEST_ONITEM;
3376             break;
3377         }
3378         else if (tabframe->m_rect.Contains(pt))
3379         {
3380             w = tabframe->m_tabs->GetWindowFromIdx(tabframe->m_tabs->GetActivePage());
3381             if (w)
3382                 position = wxBK_HITTEST_ONPAGE;
3383             break;
3384         }
3385     }
3386 
3387     if (flags)
3388         *flags = position;
3389     return w ? GetPageIndex(w) : wxNOT_FOUND;
3390 }
3391 
GetPageImage(size_t WXUNUSED (n)) const3392 int wxAuiNotebook::GetPageImage(size_t WXUNUSED(n)) const
3393 {
3394     wxFAIL_MSG("Not implemented for wxAuiNotebook");
3395     return -1;
3396 }
3397 
SetPageImage(size_t n,int imageId)3398 bool wxAuiNotebook::SetPageImage(size_t n, int imageId)
3399 {
3400     return SetPageBitmap(n, GetImageList()->GetBitmap(imageId));
3401 }
3402 
ChangeSelection(size_t n)3403 int wxAuiNotebook::ChangeSelection(size_t n)
3404 {
3405     return DoModifySelection(n, false);
3406 }
3407 
AddPage(wxWindow * page,const wxString & text,bool select,int imageId)3408 bool wxAuiNotebook::AddPage(wxWindow *page, const wxString &text, bool select,
3409                             int imageId)
3410 {
3411     if(HasImageList())
3412     {
3413         return AddPage(page, text, select, GetImageList()->GetBitmap(imageId));
3414     }
3415     else
3416     {
3417         return AddPage(page, text, select, wxNullBitmap);
3418     }
3419 }
3420 
DeleteAllPages()3421 bool wxAuiNotebook::DeleteAllPages()
3422 {
3423     size_t count = GetPageCount();
3424     for(size_t i = 0; i < count; i++)
3425     {
3426         DeletePage(0);
3427     }
3428     return true;
3429 }
3430 
InsertPage(size_t index,wxWindow * page,const wxString & text,bool select,int imageId)3431 bool wxAuiNotebook::InsertPage(size_t index, wxWindow *page,
3432                                const wxString &text, bool select,
3433                                int imageId)
3434 {
3435     if(HasImageList())
3436     {
3437         return InsertPage(index, page, text, select,
3438                           GetImageList()->GetBitmap(imageId));
3439     }
3440     else
3441     {
3442         return InsertPage(index, page, text, select, wxNullBitmap);
3443     }
3444 }
3445 
3446 namespace
3447 {
3448 
3449 // Helper class to calculate the best size of a wxAuiNotebook
3450 class wxAuiLayoutObject
3451 {
3452 public:
3453     enum
3454     {
3455         DockDir_Center,
3456         DockDir_Left,
3457         DockDir_Right,
3458         DockDir_Vertical,   // Merge elements from here vertically
3459         DockDir_Top,
3460         DockDir_Bottom,
3461         DockDir_None
3462     };
3463 
wxAuiLayoutObject(const wxSize & size,const wxAuiPaneInfo & pInfo)3464     wxAuiLayoutObject(const wxSize &size, const wxAuiPaneInfo &pInfo)
3465         : m_size(size)
3466     {
3467         m_pInfo = &pInfo;
3468         /*
3469             To speed up the sorting of the panes, the direction is mapped to a
3470             useful increasing value. This avoids complicated comparison of the
3471             enum values during the sort. The size calculation is done from the
3472             inner to the outermost direction. Therefore CENTER < LEFT/RIGHT <
3473             TOP/BOTTOM (It doesn't matter it LEFT or RIGHT is done first, as
3474             both extend the best size horizontally; the same applies for
3475             TOP/BOTTOM in vertical direction)
3476          */
3477         switch ( pInfo.dock_direction )
3478         {
3479             case wxAUI_DOCK_CENTER: m_dir = DockDir_Center; break;
3480             case wxAUI_DOCK_LEFT:   m_dir = DockDir_Left; break;
3481             case wxAUI_DOCK_RIGHT:  m_dir = DockDir_Right; break;
3482             case wxAUI_DOCK_TOP:    m_dir = DockDir_Top; break;
3483             case wxAUI_DOCK_BOTTOM: m_dir = DockDir_Bottom; break;
3484             default:                m_dir = DockDir_None;
3485         }
3486     }
MergeLayout(const wxAuiLayoutObject & lo2)3487     void MergeLayout(const wxAuiLayoutObject &lo2)
3488     {
3489         if ( this == &lo2 )
3490             return;
3491 
3492         bool mergeHorizontal;
3493         if ( m_pInfo->dock_layer != lo2.m_pInfo->dock_layer || m_dir != lo2.m_dir )
3494             mergeHorizontal = lo2.m_dir < DockDir_Vertical;
3495         else if ( m_pInfo->dock_row != lo2.m_pInfo->dock_row )
3496             mergeHorizontal = true;
3497         else
3498             mergeHorizontal = lo2.m_dir >= DockDir_Vertical;
3499 
3500         if ( mergeHorizontal )
3501         {
3502             m_size.x += lo2.m_size.x;
3503             if ( lo2.m_size.y > m_size.y )
3504                 m_size.y = lo2.m_size.y;
3505         }
3506         else
3507         {
3508             if ( lo2.m_size.x > m_size.x )
3509                 m_size.x = lo2.m_size.x;
3510             m_size.y += lo2.m_size.y;
3511         }
3512     }
3513 
3514     wxSize m_size;
3515     const wxAuiPaneInfo *m_pInfo;
3516     unsigned char m_dir;
3517 
3518     /*
3519         As the caulculation is done from the inner to the outermost pane, the
3520         panes are sorted in the following order: layer, direction, row,
3521         position.
3522      */
operator <(const wxAuiLayoutObject & lo2) const3523     bool operator<(const wxAuiLayoutObject& lo2) const
3524     {
3525         int diff = m_pInfo->dock_layer - lo2.m_pInfo->dock_layer;
3526         if ( diff )
3527             return diff < 0;
3528         diff = m_dir - lo2.m_dir;
3529         if ( diff )
3530             return diff < 0;
3531         diff = m_pInfo->dock_row - lo2.m_pInfo->dock_row;
3532         if ( diff )
3533             return diff < 0;
3534         return m_pInfo->dock_pos < lo2.m_pInfo->dock_pos;
3535     }
3536 };
3537 
3538 } // anonymous namespace
3539 
DoGetBestSize() const3540 wxSize wxAuiNotebook::DoGetBestSize() const
3541 {
3542     /*
3543         The best size of the wxAuiNotebook is a combination of all panes inside
3544         the object. To be able to efficiently  calculate the dimensions (i.e.
3545         without iterating over the panes multiple times) the panes need to be
3546         processed in a specific order. Therefore we need to collect them in the
3547         following variable which is sorted later on.
3548      */
3549     wxVector<wxAuiLayoutObject> layouts;
3550     const wxAuiPaneInfoArray& all_panes =
3551         const_cast<wxAuiManager&>(m_mgr).GetAllPanes();
3552     const size_t pane_count = all_panes.GetCount();
3553     const int tabHeight = GetTabCtrlHeight();
3554     for ( size_t n = 0; n < pane_count; ++n )
3555     {
3556         const wxAuiPaneInfo &pInfo = all_panes[n];
3557         if ( pInfo.name == wxT("dummy") || pInfo.IsFloating() )
3558             continue;
3559 
3560         const wxTabFrame* tabframe = (wxTabFrame*) all_panes.Item(n).window;
3561         const wxAuiNotebookPageArray &pages = tabframe->m_tabs->GetPages();
3562 
3563         wxSize bestPageSize;
3564         for ( size_t pIdx = 0; pIdx < pages.GetCount(); pIdx++ )
3565             bestPageSize.IncTo(pages[pIdx].window->GetBestSize());
3566 
3567         bestPageSize.y += tabHeight;
3568         // Store the current pane with its largest window dimensions
3569         layouts.push_back(wxAuiLayoutObject(bestPageSize, pInfo));
3570     }
3571 
3572     if ( layouts.empty() )
3573         return wxSize(0, 0);
3574 
3575     wxVectorSort(layouts);
3576 
3577     /*
3578         The sizes of the panes are merged here. As the center pane is always at
3579         position 0 all sizes are merged there. As panes can be stacked using
3580         the dock_pos property, different positions are merged at the first
3581         (i.e. dock_pos = 0) element before being merged with the center pane.
3582      */
3583     size_t pos = 0;
3584     for ( size_t n = 1; n < layouts.size(); n++ )
3585     {
3586         if ( layouts[n].m_pInfo->dock_layer == layouts[pos].m_pInfo->dock_layer &&
3587              layouts[n].m_dir == layouts[pos].m_dir &&
3588              layouts[n].m_pInfo->dock_row == layouts[pos].m_pInfo->dock_row )
3589         {
3590             layouts[pos].MergeLayout(layouts[n]);
3591         }
3592         else
3593         {
3594             layouts[0].MergeLayout(layouts[pos]);
3595             pos = n;
3596         }
3597     }
3598     layouts[0].MergeLayout(layouts[pos]);
3599 
3600     return layouts[0].m_size;
3601 }
3602 
DoModifySelection(size_t n,bool events)3603 int wxAuiNotebook::DoModifySelection(size_t n, bool events)
3604 {
3605     wxWindow* wnd = m_tabs.GetWindowFromIdx(n);
3606     if (!wnd)
3607         return m_curPage;
3608 
3609     // don't change the page unless necessary;
3610     // however, clicking again on a tab should give it the focus.
3611     if ((int)n == m_curPage)
3612     {
3613         wxAuiTabCtrl* ctrl;
3614         int ctrl_idx;
3615         if (FindTab(wnd, &ctrl, &ctrl_idx))
3616         {
3617             if (FindFocus() != ctrl)
3618                 ctrl->SetFocus();
3619         }
3620         return m_curPage;
3621     }
3622 
3623     bool vetoed = false;
3624 
3625     wxAuiNotebookEvent evt(wxEVT_AUINOTEBOOK_PAGE_CHANGING, m_windowId);
3626 
3627     if(events)
3628     {
3629         evt.SetSelection(n);
3630         evt.SetOldSelection(m_curPage);
3631         evt.SetEventObject(this);
3632         GetEventHandler()->ProcessEvent(evt);
3633         vetoed = !evt.IsAllowed();
3634     }
3635 
3636     if (!vetoed)
3637     {
3638         int old_curpage = m_curPage;
3639         m_curPage = n;
3640 
3641         wxAuiTabCtrl* ctrl;
3642         int ctrl_idx;
3643         if (FindTab(wnd, &ctrl, &ctrl_idx))
3644         {
3645             m_tabs.SetActivePage(wnd);
3646 
3647             ctrl->SetActivePage(ctrl_idx);
3648             DoSizing();
3649             ctrl->DoShowHide();
3650 
3651             ctrl->MakeTabVisible(ctrl_idx, ctrl);
3652 
3653             // set fonts
3654             wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
3655             size_t i, pane_count = all_panes.GetCount();
3656             for (i = 0; i < pane_count; ++i)
3657             {
3658                 wxAuiPaneInfo& pane = all_panes.Item(i);
3659                 if (pane.name == wxT("dummy"))
3660                     continue;
3661                 wxAuiTabCtrl* tabctrl = ((wxTabFrame*)pane.window)->m_tabs;
3662                 if (tabctrl != ctrl)
3663                     tabctrl->SetSelectedFont(m_normalFont);
3664                 else
3665                     tabctrl->SetSelectedFont(m_selectedFont);
3666                 tabctrl->Refresh();
3667             }
3668 
3669             // Set the focus to the page if we're not currently focused on the tab.
3670             // This is Firefox-like behaviour.
3671             if (wnd->IsShownOnScreen() && FindFocus() != ctrl)
3672                 wnd->SetFocus();
3673 
3674             // program allows the page change
3675             if(events)
3676             {
3677                 evt.SetEventType(wxEVT_AUINOTEBOOK_PAGE_CHANGED);
3678                 (void)GetEventHandler()->ProcessEvent(evt);
3679             }
3680 
3681             return old_curpage;
3682         }
3683     }
3684 
3685     return m_curPage;
3686 }
3687 
SetHoverTab(wxWindow * wnd)3688 void wxAuiTabCtrl::SetHoverTab(wxWindow* wnd)
3689 {
3690     bool hoverChanged = false;
3691 
3692     const size_t page_count = m_pages.GetCount();
3693     for ( size_t i = 0; i < page_count; ++i )
3694     {
3695         wxAuiNotebookPage& page = m_pages.Item(i);
3696         bool oldHover = page.hover;
3697         page.hover = (page.window == wnd);
3698         if ( oldHover != page.hover )
3699             hoverChanged = true;
3700     }
3701 
3702     if ( hoverChanged )
3703     {
3704         Refresh();
3705         Update();
3706     }
3707 }
3708 
3709 
3710 #endif // wxUSE_AUI
3711