1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/aui/auibook.cpp
3 // Purpose: wxaui: wx advanced user interface - notebook
4 // Author: Benjamin I. Williams
7 // Copyright: (C) Copyright 2006, Kirix Corporation, All Rights Reserved
8 // Licence: wxWindows Library Licence, Version 3.1
9 ///////////////////////////////////////////////////////////////////////////////
11 // ----------------------------------------------------------------------------
13 // ----------------------------------------------------------------------------
15 #include "wx/wxprec.h"
23 #include "wx/aui/auibook.h"
26 #include "wx/settings.h"
30 #include "wx/aui/tabmdi.h"
31 #include "wx/dcbuffer.h"
33 #include "wx/arrimpl.cpp"
34 WX_DEFINE_OBJARRAY(wxAuiNotebookPageArray
)
35 WX_DEFINE_OBJARRAY(wxAuiTabContainerButtonArray
)
37 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING
)
38 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED
)
39 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_BUTTON
)
40 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG
)
41 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG
)
42 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION
)
46 IMPLEMENT_DYNAMIC_CLASS(wxAuiNotebookEvent
, wxEvent
)
52 // This functions are here for this proof of concept
53 // and will be factored out later. See dockart.cpp
54 static wxColor
StepColour(const wxColor
& c
, int percent
)
56 int r
= c
.Red(), g
= c
.Green(), b
= c
.Blue();
57 return wxColour((unsigned char)wxMin((r
*percent
)/100,255),
58 (unsigned char)wxMin((g
*percent
)/100,255),
59 (unsigned char)wxMin((b
*percent
)/100,255));
62 // This functions are here for this proof of concept
63 // and will be factored out later. See dockart.cpp
64 static wxBitmap
BitmapFromBits(const unsigned char bits
[], int w
, int h
,
65 const wxColour
& color
)
67 wxImage img
= wxBitmap((const char*)bits
, w
, h
).ConvertToImage();
68 img
.Replace(0,0,0,123,123,123);
69 img
.Replace(255,255,255,color
.Red(),color
.Green(),color
.Blue());
70 img
.SetMaskColour(123,123,123);
74 static void DrawButton(wxDC
& dc
,
77 const wxColour
& bkcolour
,
82 if (button_state
== wxAUI_BUTTON_STATE_PRESSED
)
88 if (button_state
== wxAUI_BUTTON_STATE_HOVER
||
89 button_state
== wxAUI_BUTTON_STATE_PRESSED
)
91 dc
.SetBrush(wxBrush(StepColour(bkcolour
, 120)));
92 dc
.SetPen(wxPen(StepColour(bkcolour
, 70)));
94 // draw the background behind the button
95 dc
.DrawRectangle(rect
.x
, rect
.y
, 15, 15);
98 // draw the button itself
99 dc
.DrawBitmap(bmp
, rect
.x
, rect
.y
, true);
106 // -- wxDefaultTabArt class implementation --
108 wxDefaultTabArt::wxDefaultTabArt()
110 m_normal_font
= *wxNORMAL_FONT
;
111 m_selected_font
= *wxNORMAL_FONT
;
112 m_selected_font
.SetWeight(wxBOLD
);
113 m_measuring_font
= m_selected_font
;
115 wxColour base_colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
117 wxColour background_colour
= StepColour(base_colour
, 95);
118 wxColour normaltab_colour
= base_colour
;
119 wxColour selectedtab_colour
= *wxWHITE
;
121 m_bkbrush
= wxBrush(background_colour
);
122 m_normal_bkbrush
= wxBrush(normaltab_colour
);
123 m_normal_bkpen
= wxPen(normaltab_colour
);
124 m_selected_bkbrush
= wxBrush(selectedtab_colour
);
125 m_selected_bkpen
= wxPen(selectedtab_colour
);
128 wxDefaultTabArt::~wxDefaultTabArt()
132 void wxDefaultTabArt::DrawBackground(
137 dc
->SetBrush(m_bkbrush
);
138 dc
->SetPen(*wxTRANSPARENT_PEN
);
139 dc
->DrawRectangle(-1, -1, rect
.GetWidth()+2, rect
.GetHeight()+2);
142 dc
->SetPen(*wxGREY_PEN
);
143 dc
->DrawLine(0, rect
.GetHeight()-1, rect
.GetWidth(), rect
.GetHeight()-1);
146 // DrawTab() draws an individual tab.
149 // in_rect - rectangle the tab should be confined to
150 // caption - tab's caption
151 // active - whether or not the tab is active
152 // out_rect - actual output rectangle
153 // x_extent - the advance x; where the next tab should start
155 void wxDefaultTabArt::DrawTab(wxDC
* dc
,
156 const wxRect
& in_rect
,
157 const wxString
& caption_text
,
162 wxCoord normal_textx
, normal_texty
;
163 wxCoord selected_textx
, selected_texty
;
164 wxCoord measured_textx
, measured_texty
;
165 wxCoord textx
, texty
;
168 // if the caption is empty, measure some temporary text
169 wxString caption
= caption_text
;
170 if (caption_text
.empty())
174 dc
->SetFont(m_measuring_font
);
175 dc
->GetTextExtent(caption
, &measured_textx
, &measured_texty
);
177 dc
->SetFont(m_selected_font
);
178 dc
->GetTextExtent(caption
, &selected_textx
, &selected_texty
);
180 dc
->SetFont(m_normal_font
);
181 dc
->GetTextExtent(caption
, &normal_textx
, &normal_texty
);
183 caption
= caption_text
;
185 wxCoord tab_height
= measured_texty
+ 4;
186 wxCoord tab_width
= measured_textx
+ tab_height
+ 5;
187 wxCoord tab_x
= in_rect
.x
;
188 wxCoord tab_y
= in_rect
.y
+ in_rect
.height
- tab_height
;
191 // select pen, brush and font for the tab to be drawn
195 dc
->SetPen(m_selected_bkpen
);
196 dc
->SetBrush(m_selected_bkbrush
);
197 dc
->SetFont(m_selected_font
);
198 textx
= selected_textx
;
199 texty
= selected_texty
;
203 dc
->SetPen(m_normal_bkpen
);
204 dc
->SetBrush(m_normal_bkbrush
);
205 dc
->SetFont(m_normal_font
);
206 textx
= normal_textx
;
207 texty
= normal_texty
;
215 points
[0].y
= tab_y
+ tab_height
- 1;
216 points
[1].x
= tab_x
+ tab_height
- 3;
217 points
[1].y
= tab_y
+ 2;
218 points
[2].x
= tab_x
+ tab_height
+ 3;
220 points
[3].x
= tab_x
+ tab_width
- 2;
222 points
[4].x
= tab_x
+ tab_width
;
223 points
[4].y
= tab_y
+ 2;
224 points
[5].x
= tab_x
+ tab_width
;
225 points
[5].y
= tab_y
+ tab_height
- 1;
226 points
[6] = points
[0];
229 dc
->DrawPolygon(6, points
);
231 dc
->SetPen(*wxGREY_PEN
);
233 //dc->DrawLines(active ? 6 : 7, points);
234 dc
->DrawLines(7, points
);
238 dc
->DrawText(caption
,
239 tab_x
+ (tab_height
/3) + (tab_width
/2) - (textx
/2),
240 tab_y
+ tab_height
- texty
- 2);
242 *out_rect
= wxRect(tab_x
, tab_y
, tab_width
, tab_height
);
243 *x_extent
= tab_width
- (tab_height
/2) - 1;
247 void wxDefaultTabArt::SetNormalFont(const wxFont
& font
)
249 m_normal_font
= font
;
252 void wxDefaultTabArt::SetSelectedFont(const wxFont
& font
)
254 m_selected_font
= font
;
257 void wxDefaultTabArt::SetMeasuringFont(const wxFont
& font
)
259 m_measuring_font
= font
;
267 // -- wxAuiTabContainer class implementation --
270 // wxAuiTabContainer is a class which contains information about each
271 // tab. It also can render an entire tab control to a specified DC.
272 // It's not a window class itself, because this code will be used by
273 // the wxFrameMananger, where it is disadvantageous to have separate
274 // windows for each tab control in the case of "docked tabs"
276 // A derived class, wxAuiTabCtrl, is an actual wxWindow-derived window
277 // which can be used as a tab control in the normal sense.
280 wxAuiTabContainer::wxAuiTabContainer()
282 m_art
= new wxDefaultTabArt
;
285 wxAuiTabContainer::~wxAuiTabContainer()
290 void wxAuiTabContainer::SetArtProvider(wxTabArt
* art
)
296 wxTabArt
* wxAuiTabContainer::GetArtProvider()
301 void wxAuiTabContainer::SetNormalFont(const wxFont
& font
)
303 m_art
->SetNormalFont(font
);
306 void wxAuiTabContainer::SetSelectedFont(const wxFont
& font
)
308 m_art
->SetSelectedFont(font
);
311 void wxAuiTabContainer::SetMeasuringFont(const wxFont
& font
)
313 m_art
->SetMeasuringFont(font
);
316 void wxAuiTabContainer::SetRect(const wxRect
& rect
)
321 bool wxAuiTabContainer::AddPage(wxWindow
* page
,
322 const wxAuiNotebookPage
& info
)
324 wxAuiNotebookPage page_info
;
326 page_info
.window
= page
;
328 m_pages
.Add(page_info
);
333 bool wxAuiTabContainer::InsertPage(wxWindow
* page
,
334 const wxAuiNotebookPage
& info
,
337 wxAuiNotebookPage page_info
;
339 page_info
.window
= page
;
341 if (idx
>= m_pages
.GetCount())
342 m_pages
.Add(page_info
);
344 m_pages
.Insert(page_info
, idx
);
349 bool wxAuiTabContainer::RemovePage(wxWindow
* wnd
)
351 size_t i
, page_count
= m_pages
.GetCount();
352 for (i
= 0; i
< page_count
; ++i
)
354 wxAuiNotebookPage
& page
= m_pages
.Item(i
);
355 if (page
.window
== wnd
)
365 bool wxAuiTabContainer::SetActivePage(wxWindow
* wnd
)
369 size_t i
, page_count
= m_pages
.GetCount();
370 for (i
= 0; i
< page_count
; ++i
)
372 wxAuiNotebookPage
& page
= m_pages
.Item(i
);
373 if (page
.window
== wnd
)
387 void wxAuiTabContainer::SetNoneActive()
389 size_t i
, page_count
= m_pages
.GetCount();
390 for (i
= 0; i
< page_count
; ++i
)
392 wxAuiNotebookPage
& page
= m_pages
.Item(i
);
397 bool wxAuiTabContainer::SetActivePage(size_t page
)
399 if (page
>= m_pages
.GetCount())
402 return SetActivePage(m_pages
.Item(page
).window
);
405 int wxAuiTabContainer::GetActivePage() const
407 size_t i
, page_count
= m_pages
.GetCount();
408 for (i
= 0; i
< page_count
; ++i
)
410 wxAuiNotebookPage
& page
= m_pages
.Item(i
);
418 wxWindow
* wxAuiTabContainer::GetWindowFromIdx(size_t idx
) const
420 if (idx
>= m_pages
.GetCount())
423 return m_pages
[idx
].window
;
426 int wxAuiTabContainer::GetIdxFromWindow(wxWindow
* wnd
) const
428 size_t i
, page_count
= m_pages
.GetCount();
429 for (i
= 0; i
< page_count
; ++i
)
431 wxAuiNotebookPage
& page
= m_pages
.Item(i
);
432 if (page
.window
== wnd
)
438 wxAuiNotebookPage
& wxAuiTabContainer::GetPage(size_t idx
)
440 wxASSERT_MSG(idx
< m_pages
.GetCount(), wxT("Invalid Page index"));
445 wxAuiNotebookPageArray
& wxAuiTabContainer::GetPages()
450 size_t wxAuiTabContainer::GetPageCount() const
452 return m_pages
.GetCount();
455 void wxAuiTabContainer::AddButton(int id
, int location
, const wxBitmap
& bmp
)
457 wxAuiTabContainerButton button
;
460 button
.location
= location
;
461 button
.cur_state
= wxAUI_BUTTON_STATE_NORMAL
;
463 m_buttons
.Add(button
);
468 // Render() renders the tab catalog to the specified DC
469 // It is a virtual function and can be overridden to
470 // provide custom drawing capabilities
471 void wxAuiTabContainer::Render(wxDC
* raw_dc
)
475 bmp
.Create(m_rect
.GetWidth(), m_rect
.GetHeight());
476 dc
.SelectObject(bmp
);
478 m_art
->DrawBackground(&dc
, m_rect
);
483 // draw the buttons on the right side
484 offset
= m_rect
.x
+ m_rect
.width
;
485 size_t button_count
= m_buttons
.GetCount();
486 for (i
= 0; i
< button_count
; ++i
)
488 wxAuiTabContainerButton
& button
= m_buttons
.Item(button_count
- i
- 1);
490 if (button
.location
!= wxRIGHT
)
493 wxRect
button_rect(offset
- button
.bitmap
.GetWidth(), 1,
494 button
.bitmap
.GetWidth(), button
.bitmap
.GetHeight());
496 button
.rect
= button_rect
;
498 DrawButton(dc
, button
.rect
, button
.bitmap
,
499 //m_bkbrush.GetColour(),
503 offset
-= button
.bitmap
.GetWidth();
510 // draw the buttons on the left side
512 for (i
= 0; i
< button_count
; ++i
)
514 wxAuiTabContainerButton
& button
= m_buttons
.Item(button_count
- i
- 1);
516 if (button
.location
!= wxLEFT
)
519 wxRect
button_rect(offset
, 1,
520 button
.bitmap
.GetWidth(),
521 button
.bitmap
.GetHeight());
523 button
.rect
= button_rect
;
525 DrawButton(dc
, button
.rect
, button
.bitmap
,
526 //m_bkbrush.GetColour(),
530 offset
+= button
.bitmap
.GetWidth();
535 size_t page_count
= m_pages
.GetCount();
538 int active_offset
= 0;
541 wxRect rect
= m_rect
;
544 rect
.height
= m_rect
.height
;
546 for (i
= 0; i
< page_count
; ++i
)
548 wxAuiNotebookPage
& page
= m_pages
.Item(i
);
562 active_offset
= offset
;
568 // draw the active tab again so it stands in the foreground
569 if (active
< m_pages
.GetCount())
571 wxAuiNotebookPage
& page
= m_pages
.Item(active
);
573 rect
.x
= active_offset
;
583 raw_dc
->Blit(m_rect
.x
, m_rect
.y
, m_rect
.GetWidth(), m_rect
.GetHeight(), &dc
, 0, 0);
587 // TabHitTest() tests if a tab was hit, passing the window pointer
588 // back if that condition was fulfilled. The function returns
589 // true if a tab was hit, otherwise false
590 bool wxAuiTabContainer::TabHitTest(int x
, int y
, wxWindow
** hit
) const
592 if (!m_rect
.Contains(x
,y
))
595 size_t i
, page_count
= m_pages
.GetCount();
597 for (i
= 0; i
< page_count
; ++i
)
599 wxAuiNotebookPage
& page
= m_pages
.Item(i
);
600 if (page
.rect
.Contains(x
,y
))
610 // ButtonHitTest() tests if a button was hit. The function returns
611 // true if a button was hit, otherwise false
612 bool wxAuiTabContainer::ButtonHitTest(int x
, int y
,
613 wxAuiTabContainerButton
** hit
) const
615 if (!m_rect
.Contains(x
,y
))
618 size_t i
, button_count
= m_buttons
.GetCount();
620 for (i
= 0; i
< button_count
; ++i
)
622 wxAuiTabContainerButton
& button
= m_buttons
.Item(i
);
623 if (button
.rect
.Contains(x
,y
))
635 // the utility function ShowWnd() is the same as show,
636 // except it handles wxTabMDIChildFrame windows as well,
637 // as the Show() method on this class is "unplugged"
638 static void ShowWnd(wxWindow
* wnd
, bool show
)
640 if (wnd
->IsKindOf(CLASSINFO(wxTabMDIChildFrame
)))
642 wxTabMDIChildFrame
* cf
= (wxTabMDIChildFrame
*)wnd
;
652 // DoShowHide() this function shows the active window, then
653 // hides all of the other windows (in that order)
654 void wxAuiTabContainer::DoShowHide()
656 wxAuiNotebookPageArray
& pages
= GetPages();
657 size_t i
, page_count
= pages
.GetCount();
659 // show new active page first
660 for (i
= 0; i
< page_count
; ++i
)
662 wxAuiNotebookPage
& page
= pages
.Item(i
);
665 ShowWnd(page
.window
, true);
670 // hide all other pages
671 for (i
= 0; i
< page_count
; ++i
)
673 wxAuiNotebookPage
& page
= pages
.Item(i
);
674 ShowWnd(page
.window
, page
.active
);
683 // -- wxAuiTabCtrl class implementation --
686 const int wxAuiButtonClose
= 101;
688 BEGIN_EVENT_TABLE(wxAuiTabCtrl
, wxControl
)
689 EVT_PAINT(wxAuiTabCtrl::OnPaint
)
690 EVT_ERASE_BACKGROUND(wxAuiTabCtrl::OnEraseBackground
)
691 EVT_SIZE(wxAuiTabCtrl::OnSize
)
692 EVT_LEFT_DOWN(wxAuiTabCtrl::OnLeftDown
)
693 EVT_LEFT_UP(wxAuiTabCtrl::OnLeftUp
)
694 EVT_MOTION(wxAuiTabCtrl::OnMotion
)
695 EVT_LEAVE_WINDOW(wxAuiTabCtrl::OnLeaveWindow
)
699 wxAuiTabCtrl::wxAuiTabCtrl(wxWindow
* parent
,
703 long style
) : wxControl(parent
, id
, pos
, size
, style
)
705 m_click_pt
= wxDefaultPosition
;
706 m_is_dragging
= false;
707 m_hover_button
= NULL
;
709 // FIXME: copied from dockart-- needs to put in a common place
710 #if defined( __WXMAC__ )
711 static unsigned char close_bits
[]={
712 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFE, 0x03, 0xF8, 0x01, 0xF0, 0x19, 0xF3,
713 0xB8, 0xE3, 0xF0, 0xE1, 0xE0, 0xE0, 0xF0, 0xE1, 0xB8, 0xE3, 0x19, 0xF3,
714 0x01, 0xF0, 0x03, 0xF8, 0x0F, 0xFE, 0xFF, 0xFF };
715 #elif defined( __WXGTK__)
716 static unsigned char close_bits
[]={
717 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
718 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
719 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
721 static unsigned char close_bits
[]={
722 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xfb,0xcf,0xf9,
723 0x9f,0xfc,0x3f,0xfe,0x3f,0xfe,0x9f,0xfc,0xcf,0xf9,0xef,0xfb,
724 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
727 AddButton(101, wxRIGHT
, BitmapFromBits(close_bits
, 16, 16, *wxBLACK
));
731 void wxAuiTabCtrl::OnPaint(wxPaintEvent
&)
735 dc
.SetFont(GetFont());
737 if (GetPageCount() > 0)
741 void wxAuiTabCtrl::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
745 void wxAuiTabCtrl::OnSize(wxSizeEvent
& evt
)
747 wxSize s
= evt
.GetSize();
748 wxRect
r(0, 0, s
.GetWidth(), s
.GetHeight());
752 void wxAuiTabCtrl::OnLeftDown(wxMouseEvent
& evt
)
755 m_click_pt
= wxDefaultPosition
;
756 m_is_dragging
= false;
760 if (TabHitTest(evt
.m_x
, evt
.m_y
, &wnd
))
762 wxAuiNotebookEvent
e(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING
, m_windowId
);
763 e
.SetSelection(GetIdxFromWindow(wnd
));
764 e
.SetOldSelection(GetActivePage());
765 e
.SetEventObject(this);
766 GetEventHandler()->ProcessEvent(e
);
768 m_click_pt
.x
= evt
.m_x
;
769 m_click_pt
.y
= evt
.m_y
;
770 m_click_tab
= e
.GetSelection();
775 m_hover_button
->cur_state
= wxAUI_BUTTON_STATE_PRESSED
;
781 void wxAuiTabCtrl::OnLeftUp(wxMouseEvent
&)
783 if (GetCapture() == this)
788 wxAuiNotebookEvent
evt(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG
, m_windowId
);
789 evt
.SetSelection(m_click_tab
);
790 evt
.SetOldSelection(m_click_tab
);
791 evt
.SetEventObject(this);
792 GetEventHandler()->ProcessEvent(evt
);
798 m_hover_button
->cur_state
= wxAUI_BUTTON_STATE_HOVER
;
802 wxAuiNotebookEvent
evt(wxEVT_COMMAND_AUINOTEBOOK_BUTTON
, m_windowId
);
803 evt
.SetInt(m_hover_button
->id
);
804 evt
.SetEventObject(this);
805 GetEventHandler()->ProcessEvent(evt
);
808 m_click_pt
= wxDefaultPosition
;
809 m_is_dragging
= false;
813 void wxAuiTabCtrl::OnMotion(wxMouseEvent
& evt
)
815 wxPoint pos
= evt
.GetPosition();
817 // check if the mouse is hovering above a button
818 wxAuiTabContainerButton
* button
;
819 if (ButtonHitTest(pos
.x
, pos
.y
, &button
))
821 if (m_hover_button
&& button
!= m_hover_button
)
823 m_hover_button
->cur_state
= wxAUI_BUTTON_STATE_NORMAL
;
824 m_hover_button
= NULL
;
829 if (button
->cur_state
!= wxAUI_BUTTON_STATE_HOVER
)
831 button
->cur_state
= wxAUI_BUTTON_STATE_HOVER
;
834 m_hover_button
= button
;
842 m_hover_button
->cur_state
= wxAUI_BUTTON_STATE_NORMAL
;
843 m_hover_button
= NULL
;
850 if (!evt
.LeftIsDown() || m_click_pt
== wxDefaultPosition
)
855 wxAuiNotebookEvent
evt(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION
, m_windowId
);
856 evt
.SetSelection(m_click_tab
);
857 evt
.SetOldSelection(m_click_tab
);
858 evt
.SetEventObject(this);
859 GetEventHandler()->ProcessEvent(evt
);
864 int drag_x_threshold
= wxSystemSettings::GetMetric(wxSYS_DRAG_X
);
865 int drag_y_threshold
= wxSystemSettings::GetMetric(wxSYS_DRAG_Y
);
867 if (abs(pos
.x
- m_click_pt
.x
) > drag_x_threshold
||
868 abs(pos
.y
- m_click_pt
.y
) > drag_y_threshold
)
870 wxAuiNotebookEvent
evt(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG
, m_windowId
);
871 evt
.SetSelection(m_click_tab
);
872 evt
.SetOldSelection(m_click_tab
);
873 evt
.SetEventObject(this);
874 GetEventHandler()->ProcessEvent(evt
);
876 m_is_dragging
= true;
880 void wxAuiTabCtrl::OnLeaveWindow(wxMouseEvent
& WXUNUSED(event
))
884 m_hover_button
->cur_state
= wxAUI_BUTTON_STATE_NORMAL
;
885 m_hover_button
= NULL
;
892 // wxTabFrame is an interesting case. It's important that all child pages
893 // of the multi-notebook control are all actually children of that control
894 // (and not grandchildren). wxTabFrame facilitates this. There is one
895 // instance of wxTabFrame for each tab control inside the multi-notebook.
896 // It's important to know that wxTabFrame is not a real window, but it merely
897 // used to capture the dimensions/positioning of the internal tab control and
898 // it's managed page windows
900 class wxTabFrame
: public wxWindow
907 m_rect
= wxRect(0,0,200,200);
908 m_tab_ctrl_height
= 20;
911 void SetTabCtrlHeight(int h
)
913 m_tab_ctrl_height
= h
;
916 void DoSetSize(int x
, int y
,
917 int width
, int height
,
918 int WXUNUSED(sizeFlags
= wxSIZE_AUTO
))
920 m_rect
= wxRect(x
, y
, width
, height
);
924 void DoGetClientSize(int* x
, int* y
) const
930 bool Show( bool WXUNUSED(show
= true) ) { return false; }
937 int tab_height
= wxMin(m_rect
.height
, m_tab_ctrl_height
);
938 m_tab_rect
= wxRect(m_rect
.x
, m_rect
.y
, m_rect
.width
, tab_height
);
939 m_tabs
->SetSize(m_rect
.x
, m_rect
.y
, m_rect
.width
, tab_height
);
940 m_tabs
->SetRect(wxRect(0, 0, m_rect
.width
, tab_height
));
944 wxAuiNotebookPageArray
& pages
= m_tabs
->GetPages();
945 size_t i
, page_count
= pages
.GetCount();
947 for (i
= 0; i
< page_count
; ++i
)
949 wxAuiNotebookPage
& page
= pages
.Item(i
);
950 page
.window
->SetSize(m_rect
.x
, m_rect
.y
+tab_height
, m_rect
.width
, m_rect
.height
-tab_height
);
952 if (page
.window
->IsKindOf(CLASSINFO(wxTabMDIChildFrame
)))
954 wxTabMDIChildFrame
* wnd
= (wxTabMDIChildFrame
*)page
.window
;
955 wnd
->ApplyMDIChildFrameRect();
960 void DoGetSize(int* x
, int* y
) const
963 *x
= m_rect
.GetWidth();
965 *y
= m_rect
.GetHeight();
977 wxAuiTabCtrl
* m_tabs
;
978 int m_tab_ctrl_height
;
985 // -- wxAuiMultiNotebook class implementation --
987 BEGIN_EVENT_TABLE(wxAuiMultiNotebook
, wxControl
)
988 //EVT_ERASE_BACKGROUND(wxAuiMultiNotebook::OnEraseBackground)
989 //EVT_SIZE(wxAuiMultiNotebook::OnSize)
990 //EVT_LEFT_DOWN(wxAuiMultiNotebook::OnLeftDown)
991 EVT_CHILD_FOCUS(wxAuiMultiNotebook::OnChildFocus
)
992 EVT_COMMAND_RANGE(10000, 10100,
993 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING
,
994 wxAuiMultiNotebook::OnTabClicked
)
995 EVT_COMMAND_RANGE(10000, 10100,
996 wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG
,
997 wxAuiMultiNotebook::OnTabBeginDrag
)
998 EVT_COMMAND_RANGE(10000, 10100,
999 wxEVT_COMMAND_AUINOTEBOOK_END_DRAG
,
1000 wxAuiMultiNotebook::OnTabEndDrag
)
1001 EVT_COMMAND_RANGE(10000, 10100,
1002 wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION
,
1003 wxAuiMultiNotebook::OnTabDragMotion
)
1004 EVT_COMMAND_RANGE(10000, 10100,
1005 wxEVT_COMMAND_AUINOTEBOOK_BUTTON
,
1006 wxAuiMultiNotebook::OnTabButton
)
1009 wxAuiMultiNotebook::wxAuiMultiNotebook()
1012 m_tab_id_counter
= 10000;
1014 m_tab_ctrl_height
= 20;
1017 wxAuiMultiNotebook::wxAuiMultiNotebook(wxWindow
*parent
,
1021 long style
) : wxControl(parent
, id
, pos
, size
, style
)
1026 bool wxAuiMultiNotebook::Create(wxWindow
* parent
,
1032 if (!wxControl::Create(parent
, id
, pos
, size
, style
))
1040 // InitNotebook() contains common initialization
1041 // code called by all constructors
1042 void wxAuiMultiNotebook::InitNotebook()
1045 m_tab_id_counter
= 10000;
1047 m_tab_ctrl_height
= 20;
1049 m_normal_font
= *wxNORMAL_FONT
;
1050 m_selected_font
= *wxNORMAL_FONT
;
1051 m_selected_font
.SetWeight(wxBOLD
);
1053 // choose a default for the tab height
1054 wxClientDC
dc(this);
1056 dc
.SetFont(m_selected_font
);
1057 dc
.GetTextExtent(wxT("ABCDEFGHhijklm"), &tx
, &ty
);
1058 m_tab_ctrl_height
= (ty
*150)/100;
1060 m_dummy_wnd
= new wxWindow(this, wxID_ANY
, wxPoint(0,0), wxSize(0,0));
1061 m_dummy_wnd
->SetSize(200, 200);
1062 m_dummy_wnd
->Show(false);
1064 m_mgr
.SetManagedWindow(this);
1066 m_mgr
.AddPane(m_dummy_wnd
,
1067 wxPaneInfo().Name(wxT("dummy")).Bottom().Show(false));
1072 wxAuiMultiNotebook::~wxAuiMultiNotebook()
1077 void wxAuiMultiNotebook::SetArtProvider(wxTabArt
* art
)
1079 m_tabs
.SetArtProvider(art
);
1082 wxTabArt
* wxAuiMultiNotebook::GetArtProvider()
1084 return m_tabs
.GetArtProvider();
1087 bool wxAuiMultiNotebook::AddPage(wxWindow
* page
,
1088 const wxString
& caption
,
1090 const wxBitmap
& bitmap
)
1092 return InsertPage(GetPageCount(), page
, caption
, select
, bitmap
);
1095 bool wxAuiMultiNotebook::InsertPage(size_t page_idx
,
1097 const wxString
& caption
,
1099 const wxBitmap
& bitmap
)
1101 wxAuiNotebookPage info
;
1103 info
.caption
= caption
;
1104 info
.bitmap
= bitmap
;
1105 info
.active
= false;
1107 // if there are currently no tabs, the first added
1108 // tab must be active
1109 if (m_tabs
.GetPageCount() == 0)
1112 m_tabs
.InsertPage(page
, info
, page_idx
);
1114 wxAuiTabCtrl
* active_tabctrl
= GetActiveTabCtrl();
1115 if (page_idx
>= active_tabctrl
->GetPageCount())
1116 active_tabctrl
->AddPage(page
, info
);
1118 active_tabctrl
->InsertPage(page
, info
, page_idx
);
1121 active_tabctrl
->DoShowHide();
1125 int idx
= m_tabs
.GetIdxFromWindow(page
);
1126 wxASSERT_MSG(idx
!= -1, wxT("Invalid Page index returned on wxAuiMultiNotebook::InsertPage()"));
1135 // DeletePage() removes a tab from the multi-notebook,
1136 // and destroys the window as well
1137 bool wxAuiMultiNotebook::DeletePage(size_t page_idx
)
1139 wxWindow
* wnd
= m_tabs
.GetWindowFromIdx(page_idx
);
1140 wxWindow
* new_active
= NULL
;
1142 // find out which onscreen tab ctrl owns this tab
1145 if (!FindTab(wnd
, &ctrl
, &ctrl_idx
))
1148 // find a new page and set it as active
1149 int new_idx
= ctrl_idx
+1;
1150 if (new_idx
>= (int)ctrl
->GetPageCount())
1151 new_idx
= ctrl_idx
-1;
1153 if (new_idx
>= 0 && new_idx
< (int)ctrl
->GetPageCount())
1155 new_active
= ctrl
->GetWindowFromIdx(new_idx
);
1159 // set the active page to the first page that
1160 // isn't the one being deleted
1161 size_t i
, page_count
= m_tabs
.GetPageCount();
1162 for (i
= 0; i
< page_count
; ++i
)
1164 wxWindow
* w
= m_tabs
.GetWindowFromIdx(i
);
1167 new_active
= m_tabs
.GetWindowFromIdx(i
);
1173 // remove the tab from main catalog
1174 if (!m_tabs
.RemovePage(wnd
))
1177 // remove the tab from the onscreen tab ctrl
1178 ctrl
->RemovePage(wnd
);
1180 // actually destroy the window now
1181 if (wnd
->IsKindOf(CLASSINFO(wxTabMDIChildFrame
)))
1183 // delete the child frame with pending delete, as is
1184 // customary with frame windows
1185 if (!wxPendingDelete
.Member(wnd
))
1186 wxPendingDelete
.Append(wnd
);
1193 RemoveEmptyTabFrames();
1195 // set new active pane
1199 SetSelection(m_tabs
.GetIdxFromWindow(new_active
));
1207 // RemovePage() removes a tab from the multi-notebook,
1208 // but does not destroy the window
1209 bool wxAuiMultiNotebook::RemovePage(size_t page_idx
)
1211 // remove the tab from our own catalog
1212 wxWindow
* wnd
= m_tabs
.GetWindowFromIdx(page_idx
);
1213 if (!m_tabs
.RemovePage(wnd
))
1216 // remove the tab from the onscreen tab ctrl
1219 if (FindTab(wnd
, &ctrl
, &ctrl_idx
))
1221 ctrl
->RemovePage(wnd
);
1228 // SetPageText() changes the tab caption of the specified page
1229 bool wxAuiMultiNotebook::SetPageText(size_t page_idx
, const wxString
& text
)
1231 if (page_idx
>= m_tabs
.GetPageCount())
1234 // update our own tab catalog
1235 wxAuiNotebookPage
& page_info
= m_tabs
.GetPage(page_idx
);
1236 page_info
.caption
= text
;
1238 // update what's on screen
1241 if (FindTab(page_info
.window
, &ctrl
, &ctrl_idx
))
1243 wxAuiNotebookPage
& info
= ctrl
->GetPage(ctrl_idx
);
1244 info
.caption
= text
;
1252 // GetSelection() returns the index of the currently active page
1253 int wxAuiMultiNotebook::GetSelection() const
1258 // SetSelection() sets the currently active page
1259 size_t wxAuiMultiNotebook::SetSelection(size_t new_page
)
1261 wxWindow
* wnd
= m_tabs
.GetWindowFromIdx(new_page
);
1265 wxAuiNotebookEvent
evt(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING
, m_windowId
);
1266 evt
.SetSelection(new_page
);
1267 evt
.SetOldSelection(m_curpage
);
1268 evt
.SetEventObject(this);
1269 if (!GetEventHandler()->ProcessEvent(evt
) || evt
.IsAllowed())
1271 // program allows the page change
1272 evt
.SetEventType(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED
);
1273 (void)GetEventHandler()->ProcessEvent(evt
);
1279 if (FindTab(wnd
, &ctrl
, &ctrl_idx
))
1281 m_tabs
.SetActivePage(wnd
);
1283 ctrl
->SetActivePage(ctrl_idx
);
1287 int old_curpage
= m_curpage
;
1288 m_curpage
= new_page
;
1292 wxPaneInfoArray
& all_panes
= m_mgr
.GetAllPanes();
1293 size_t i
, pane_count
= all_panes
.GetCount();
1294 for (i
= 0; i
< pane_count
; ++i
)
1296 wxPaneInfo
& pane
= all_panes
.Item(i
);
1297 if (pane
.name
== wxT("dummy"))
1299 wxAuiTabCtrl
* tabctrl
= ((wxTabFrame
*)pane
.window
)->m_tabs
;
1300 if (tabctrl
!= ctrl
)
1301 tabctrl
->SetSelectedFont(m_normal_font
);
1303 tabctrl
->SetSelectedFont(m_selected_font
);
1316 // GetPageCount() returns the total number of
1317 // pages managed by the multi-notebook
1318 size_t wxAuiMultiNotebook::GetPageCount() const
1320 return m_tabs
.GetPageCount();
1323 // GetPage() returns the wxWindow pointer of the
1325 wxWindow
* wxAuiMultiNotebook::GetPage(size_t page_idx
) const
1327 wxASSERT(page_idx
< m_tabs
.GetPageCount());
1329 return m_tabs
.GetWindowFromIdx(page_idx
);
1332 // DoSizing() performs all sizing operations in each tab control
1333 void wxAuiMultiNotebook::DoSizing()
1335 wxPaneInfoArray
& all_panes
= m_mgr
.GetAllPanes();
1336 size_t i
, pane_count
= all_panes
.GetCount();
1337 for (i
= 0; i
< pane_count
; ++i
)
1339 if (all_panes
.Item(i
).name
== wxT("dummy"))
1342 wxTabFrame
* tabframe
= (wxTabFrame
*)all_panes
.Item(i
).window
;
1343 tabframe
->DoSizing();
1347 // GetActiveTabCtrl() returns the active tab control. It is
1348 // called to determine which control gets new windows being added
1349 wxAuiTabCtrl
* wxAuiMultiNotebook::GetActiveTabCtrl()
1351 if (m_curpage
>= 0 && m_curpage
< (int)m_tabs
.GetPageCount())
1356 // find the tab ctrl with the current page
1357 if (FindTab(m_tabs
.GetPage(m_curpage
).window
,
1364 // no current page, just find the first tab ctrl
1365 wxPaneInfoArray
& all_panes
= m_mgr
.GetAllPanes();
1366 size_t i
, pane_count
= all_panes
.GetCount();
1367 for (i
= 0; i
< pane_count
; ++i
)
1369 if (all_panes
.Item(i
).name
== wxT("dummy"))
1372 wxTabFrame
* tabframe
= (wxTabFrame
*)all_panes
.Item(i
).window
;
1373 return tabframe
->m_tabs
;
1376 // If there is no tabframe at all, create one
1377 wxTabFrame
* tabframe
= new wxTabFrame
;
1378 tabframe
->SetTabCtrlHeight(m_tab_ctrl_height
);
1379 tabframe
->m_tabs
= new wxAuiTabCtrl(this,
1384 m_mgr
.AddPane(tabframe
,
1385 wxPaneInfo().Center().CaptionVisible(false));
1389 return tabframe
->m_tabs
;
1392 // FindTab() finds the tab control that currently contains the window as well
1393 // as the index of the window in the tab control. It returns true if the
1394 // window was found, otherwise false.
1395 bool wxAuiMultiNotebook::FindTab(wxWindow
* page
, wxAuiTabCtrl
** ctrl
, int* idx
)
1397 wxPaneInfoArray
& all_panes
= m_mgr
.GetAllPanes();
1398 size_t i
, pane_count
= all_panes
.GetCount();
1399 for (i
= 0; i
< pane_count
; ++i
)
1401 if (all_panes
.Item(i
).name
== wxT("dummy"))
1404 wxTabFrame
* tabframe
= (wxTabFrame
*)all_panes
.Item(i
).window
;
1406 int page_idx
= tabframe
->m_tabs
->GetIdxFromWindow(page
);
1409 *ctrl
= tabframe
->m_tabs
;
1419 void wxAuiMultiNotebook::OnEraseBackground(wxEraseEvent
&)
1423 void wxAuiMultiNotebook::OnSize(wxSizeEvent
&)
1427 void wxAuiMultiNotebook::OnTabClicked(wxCommandEvent
& command_evt
)
1429 wxAuiNotebookEvent
& evt
= (wxAuiNotebookEvent
&)command_evt
;
1431 wxAuiTabCtrl
* ctrl
= (wxAuiTabCtrl
*)evt
.GetEventObject();
1432 wxASSERT(ctrl
!= NULL
);
1434 wxWindow
* wnd
= ctrl
->GetWindowFromIdx(evt
.GetSelection());
1435 wxASSERT(wnd
!= NULL
);
1437 int idx
= m_tabs
.GetIdxFromWindow(wnd
);
1438 wxASSERT(idx
!= -1);
1443 void wxAuiMultiNotebook::OnTabBeginDrag(wxCommandEvent
&)
1447 void wxAuiMultiNotebook::OnTabDragMotion(wxCommandEvent
& evt
)
1449 wxPoint screen_pt
= ::wxGetMousePosition();
1450 wxPoint client_pt
= ScreenToClient(screen_pt
);
1453 wxAuiTabCtrl
* src_tabs
= (wxAuiTabCtrl
*)evt
.GetEventObject();
1455 wxAuiTabCtrl
* tab_ctrl
= GetTabCtrlFromPoint(client_pt
);
1456 if (tab_ctrl
== src_tabs
)
1458 // inner-tabctrl dragging is not yet implemented
1465 wxRect hint_rect
= tab_ctrl
->GetRect();
1466 ClientToScreen(&hint_rect
.x
, &hint_rect
.y
);
1467 m_mgr
.ShowHint(hint_rect
);
1471 m_mgr
.DrawHintRect(m_dummy_wnd
, client_pt
, zero
);
1477 void wxAuiMultiNotebook::OnTabEndDrag(wxCommandEvent
& command_evt
)
1479 wxAuiNotebookEvent
& evt
= (wxAuiNotebookEvent
&)command_evt
;
1484 // get the mouse position, which will be used to determine the drop point
1485 wxPoint mouse_screen_pt
= ::wxGetMousePosition();
1486 wxPoint mouse_client_pt
= ScreenToClient(mouse_screen_pt
);
1489 // the src tab control is the control that fired this event
1490 wxAuiTabCtrl
* src_tabs
= (wxAuiTabCtrl
*)evt
.GetEventObject();
1491 wxAuiTabCtrl
* dest_tabs
= NULL
;
1494 // If the pointer is in an existing tab frame, do a tab insert
1495 wxWindow
* hit_wnd
= ::wxFindWindowAtPoint(mouse_screen_pt
);
1496 wxTabFrame
* tab_frame
= (wxTabFrame
*)GetTabFrameFromTabCtrl(hit_wnd
);
1499 dest_tabs
= tab_frame
->m_tabs
;
1501 if (dest_tabs
== src_tabs
)
1506 // If there is no tabframe at all, create one
1507 wxTabFrame
* new_tabs
= new wxTabFrame
;
1508 new_tabs
->SetTabCtrlHeight(m_tab_ctrl_height
);
1509 new_tabs
->m_tabs
= new wxAuiTabCtrl(this,
1514 m_mgr
.AddPane(new_tabs
,
1515 wxPaneInfo().Bottom().CaptionVisible(false),
1518 dest_tabs
= new_tabs
->m_tabs
;
1523 // remove the page from the source tabs
1524 wxAuiNotebookPage page_info
= src_tabs
->GetPage(evt
.GetSelection());
1525 page_info
.active
= false;
1526 src_tabs
->RemovePage(page_info
.window
);
1527 if (src_tabs
->GetPageCount() > 0)
1529 src_tabs
->SetActivePage((size_t)0);
1530 src_tabs
->DoShowHide();
1531 src_tabs
->Refresh();
1536 // add the page to the destination tabs
1537 dest_tabs
->AddPage(page_info
.window
, page_info
);
1539 if (src_tabs
->GetPageCount() == 0)
1541 RemoveEmptyTabFrames();
1545 dest_tabs
->DoShowHide();
1546 dest_tabs
->Refresh();
1548 SetSelection(m_tabs
.GetIdxFromWindow(page_info
.window
));
1551 wxAuiTabCtrl
* wxAuiMultiNotebook::GetTabCtrlFromPoint(const wxPoint
& pt
)
1553 // if we've just removed the last tab from the source
1554 // tab set, the remove the tab control completely
1555 wxPaneInfoArray
& all_panes
= m_mgr
.GetAllPanes();
1556 size_t i
, pane_count
= all_panes
.GetCount();
1557 for (i
= 0; i
< pane_count
; ++i
)
1559 if (all_panes
.Item(i
).name
== wxT("dummy"))
1562 wxTabFrame
* tabframe
= (wxTabFrame
*)all_panes
.Item(i
).window
;
1563 if (tabframe
->m_tab_rect
.Contains(pt
))
1564 return tabframe
->m_tabs
;
1570 wxWindow
* wxAuiMultiNotebook::GetTabFrameFromTabCtrl(wxWindow
* tab_ctrl
)
1572 // if we've just removed the last tab from the source
1573 // tab set, the remove the tab control completely
1574 wxPaneInfoArray
& all_panes
= m_mgr
.GetAllPanes();
1575 size_t i
, pane_count
= all_panes
.GetCount();
1576 for (i
= 0; i
< pane_count
; ++i
)
1578 if (all_panes
.Item(i
).name
== wxT("dummy"))
1581 wxTabFrame
* tabframe
= (wxTabFrame
*)all_panes
.Item(i
).window
;
1582 if (tabframe
->m_tabs
== tab_ctrl
)
1591 void wxAuiMultiNotebook::RemoveEmptyTabFrames()
1593 // if we've just removed the last tab from the source
1594 // tab set, the remove the tab control completely
1595 wxPaneInfoArray all_panes
= m_mgr
.GetAllPanes();
1596 size_t i
, pane_count
= all_panes
.GetCount();
1597 for (i
= 0; i
< pane_count
; ++i
)
1599 if (all_panes
.Item(i
).name
== wxT("dummy"))
1602 wxTabFrame
* tab_frame
= (wxTabFrame
*)all_panes
.Item(i
).window
;
1603 if (tab_frame
->m_tabs
->GetPageCount() == 0)
1605 m_mgr
.DetachPane(tab_frame
);
1607 // use pending delete because sometimes during
1608 // window closing, refreshs are pending
1609 if (!wxPendingDelete
.Member(tab_frame
->m_tabs
))
1610 wxPendingDelete
.Append(tab_frame
->m_tabs
);
1611 //tab_frame->m_tabs->Destroy();
1618 // check to see if there is still a center pane;
1619 // if there isn't, make a frame the center pane
1620 wxPaneInfoArray panes
= m_mgr
.GetAllPanes();
1621 pane_count
= panes
.GetCount();
1622 wxWindow
* first_good
= NULL
;
1623 bool center_found
= false;
1624 for (i
= 0; i
< pane_count
; ++i
)
1626 if (panes
.Item(i
).name
== wxT("dummy"))
1628 if (panes
.Item(i
).dock_direction
== wxAUI_DOCK_CENTRE
)
1629 center_found
= true;
1631 first_good
= panes
.Item(i
).window
;
1634 if (!center_found
&& first_good
)
1636 m_mgr
.GetPane(first_good
).Centre();
1642 void wxAuiMultiNotebook::OnChildFocus(wxChildFocusEvent
& evt
)
1644 int idx
= m_tabs
.GetIdxFromWindow(evt
.GetWindow());
1645 if (idx
!= -1 && idx
!= m_curpage
)
1652 void wxAuiMultiNotebook::OnTabButton(wxCommandEvent
& command_evt
)
1654 wxAuiNotebookEvent
& evt
= (wxAuiNotebookEvent
&)command_evt
;
1655 wxAuiTabCtrl
* tabs
= (wxAuiTabCtrl
*)evt
.GetEventObject();
1657 int button_id
= evt
.GetInt();
1659 if (button_id
== wxAuiButtonClose
)
1661 int selection
= tabs
->GetActivePage();
1663 if (selection
!= -1)
1665 wxWindow
* close_wnd
= tabs
->GetWindowFromIdx(selection
);
1667 if (close_wnd
->IsKindOf(CLASSINFO(wxTabMDIChildFrame
)))
1673 int main_idx
= m_tabs
.GetIdxFromWindow(close_wnd
);
1674 DeletePage(main_idx
);