]> git.saurik.com Git - wxWidgets.git/blob - src/aui/auibook.cpp
bc7f8fb218abde90295cecb68d420d74d8a10312
[wxWidgets.git] / src / aui / auibook.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/aui/auibook.cpp
3 // Purpose: wxaui: wx advanced user interface - notebook
4 // Author: Benjamin I. Williams
5 // Modified by:
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 #ifdef __BORLANDC__
18 #pragma hdrstop
19 #endif
20
21 #if wxUSE_AUI
22
23 #include "wx/aui/auibook.h"
24
25 #ifndef WX_PRECOMP
26 #include "wx/settings.h"
27 #include "wx/image.h"
28 #endif
29
30 #include "wx/aui/tabmdi.h"
31 #include "wx/dcbuffer.h"
32 #include "wx/menu.h"
33
34 #include "wx/arrimpl.cpp"
35 WX_DEFINE_OBJARRAY(wxAuiNotebookPageArray)
36 WX_DEFINE_OBJARRAY(wxAuiTabContainerButtonArray)
37
38 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING)
39 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED)
40 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_BUTTON)
41 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG)
42 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG)
43 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION)
44 DEFINE_EVENT_TYPE(wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND)
45
46
47 IMPLEMENT_CLASS(wxAuiNotebook, wxControl)
48 IMPLEMENT_CLASS(wxAuiTabCtrl, wxControl)
49 IMPLEMENT_DYNAMIC_CLASS(wxAuiNotebookEvent, wxEvent)
50
51
52
53
54
55 // This functions are here for this proof of concept
56 // and will be factored out later. See dockart.cpp
57 static wxColor StepColour(const wxColor& c, int percent)
58 {
59 int r = c.Red(), g = c.Green(), b = c.Blue();
60 return wxColour((unsigned char)wxMin((r*percent)/100,255),
61 (unsigned char)wxMin((g*percent)/100,255),
62 (unsigned char)wxMin((b*percent)/100,255));
63 }
64
65 // This functions are here for this proof of concept
66 // and will be factored out later. See dockart.cpp
67 static wxBitmap BitmapFromBits(const unsigned char bits[], int w, int h,
68 const wxColour& color)
69 {
70 wxImage img = wxBitmap((const char*)bits, w, h).ConvertToImage();
71 img.Replace(0,0,0,123,123,123);
72 img.Replace(255,255,255,color.Red(),color.Green(),color.Blue());
73 img.SetMaskColour(123,123,123);
74 return wxBitmap(img);
75 }
76
77 static void DrawButtonS(wxDC& dc,
78 const wxRect& _rect,
79 const wxBitmap& bmp,
80 const wxColour& bkcolour,
81 int button_state)
82 {
83 wxRect rect = _rect;
84
85 if (button_state == wxAUI_BUTTON_STATE_PRESSED)
86 {
87 rect.x++;
88 rect.y++;
89 }
90
91 if (button_state == wxAUI_BUTTON_STATE_HOVER ||
92 button_state == wxAUI_BUTTON_STATE_PRESSED)
93 {
94 dc.SetBrush(wxBrush(StepColour(bkcolour, 120)));
95 dc.SetPen(wxPen(StepColour(bkcolour, 70)));
96
97 // draw the background behind the button
98 dc.DrawRectangle(rect.x, rect.y, 15, 15);
99 }
100
101 // draw the button itself
102 dc.DrawBitmap(bmp, rect.x, rect.y, true);
103 }
104
105
106
107
108
109 // -- wxAuiDefaultTabArt class implementation --
110
111 wxAuiDefaultTabArt::wxAuiDefaultTabArt()
112 {
113 m_normal_font = *wxNORMAL_FONT;
114 m_selected_font = *wxNORMAL_FONT;
115 m_selected_font.SetWeight(wxBOLD);
116 m_measuring_font = m_selected_font;
117
118 wxColour base_colour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
119
120 wxColour background_colour = StepColour(base_colour, 95);
121 wxColour normaltab_colour = base_colour;
122 wxColour selectedtab_colour = *wxWHITE;
123
124 m_bkbrush = wxBrush(background_colour);
125 m_normal_bkbrush = wxBrush(normaltab_colour);
126 m_normal_bkpen = wxPen(normaltab_colour);
127 m_selected_bkbrush = wxBrush(selectedtab_colour);
128 m_selected_bkpen = wxPen(selectedtab_colour);
129
130
131 #if defined( __WXMAC__ )
132 static unsigned char close_bits[]={
133 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFE, 0x03, 0xF8, 0x01, 0xF0, 0x19, 0xF3,
134 0xB8, 0xE3, 0xF0, 0xE1, 0xE0, 0xE0, 0xF0, 0xE1, 0xB8, 0xE3, 0x19, 0xF3,
135 0x01, 0xF0, 0x03, 0xF8, 0x0F, 0xFE, 0xFF, 0xFF };
136 #elif defined( __WXGTK__)
137 static unsigned char close_bits[]={
138 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
139 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
140 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
141 #else
142 static unsigned char close_bits[]={
143 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xfb,0xcf,0xf9,
144 0x9f,0xfc,0x3f,0xfe,0x3f,0xfe,0x9f,0xfc,0xcf,0xf9,0xef,0xfb,
145 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
146 #endif
147
148 static unsigned char left_bits[] = {
149 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfe, 0x3f, 0xfe,
150 0x1f, 0xfe, 0x0f, 0xfe, 0x1f, 0xfe, 0x3f, 0xfe, 0x7f, 0xfe, 0xff, 0xfe,
151 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
152
153 static unsigned char right_bits[] = {
154 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0x9f, 0xff, 0x1f, 0xff,
155 0x1f, 0xfe, 0x1f, 0xfc, 0x1f, 0xfe, 0x1f, 0xff, 0x9f, 0xff, 0xdf, 0xff,
156 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
157
158 static unsigned char list_bits[] = {
159 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
160 0x0f, 0xf8, 0xff, 0xff, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0xfe, 0x7f, 0xff,
161 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
162
163
164 m_active_close_bmp = BitmapFromBits(close_bits, 16, 16, *wxBLACK);
165 m_disabled_close_bmp = BitmapFromBits(close_bits, 16, 16, wxColour(128,128,128));
166
167 m_active_left_bmp = BitmapFromBits(left_bits, 16, 16, *wxBLACK);
168 m_disabled_left_bmp = BitmapFromBits(left_bits, 16, 16, wxColour(128,128,128));
169
170 m_active_right_bmp = BitmapFromBits(right_bits, 16, 16, *wxBLACK);
171 m_disabled_right_bmp = BitmapFromBits(right_bits, 16, 16, wxColour(128,128,128));
172
173 m_active_windowlist_bmp = BitmapFromBits(list_bits, 16, 16, *wxBLACK);
174 m_disabled_windowlist_bmp = BitmapFromBits(list_bits, 16, 16, wxColour(128,128,128));
175
176 }
177
178 wxAuiDefaultTabArt::~wxAuiDefaultTabArt()
179 {
180 }
181
182 void wxAuiDefaultTabArt::DrawBackground(wxDC& dc,
183 wxWindow* WXUNUSED(wnd),
184 const wxRect& rect)
185 {
186 // draw background
187 dc.SetBrush(m_bkbrush);
188 dc.SetPen(*wxTRANSPARENT_PEN);
189 dc.DrawRectangle(-1, -1, rect.GetWidth()+2, rect.GetHeight()+2);
190
191 // draw base line
192 dc.SetPen(*wxGREY_PEN);
193 dc.DrawLine(0, rect.GetHeight()-1, rect.GetWidth(), rect.GetHeight()-1);
194 }
195
196
197 // DrawTab() draws an individual tab.
198 //
199 // dc - output dc
200 // in_rect - rectangle the tab should be confined to
201 // caption - tab's caption
202 // active - whether or not the tab is active
203 // out_rect - actual output rectangle
204 // x_extent - the advance x; where the next tab should start
205
206 void wxAuiDefaultTabArt::DrawTab(wxDC& dc,
207 wxWindow* wnd,
208 const wxRect& in_rect,
209 const wxString& caption_text,
210 bool active,
211 int close_button_state,
212 wxRect* out_tab_rect,
213 wxRect* out_button_rect,
214 int* x_extent)
215 {
216 wxCoord normal_textx, normal_texty;
217 wxCoord selected_textx, selected_texty;
218 wxCoord textx, texty;
219
220 // if the caption is empty, measure some temporary text
221 wxString caption = caption_text;
222 if (caption_text.empty())
223 caption = wxT("Xj");
224
225 dc.SetFont(m_selected_font);
226 dc.GetTextExtent(caption, &selected_textx, &selected_texty);
227
228 dc.SetFont(m_normal_font);
229 dc.GetTextExtent(caption, &normal_textx, &normal_texty);
230
231 // figure out the size of the tab
232 wxSize tab_size = GetTabSize(dc, wnd, caption, active, close_button_state, x_extent);
233
234 wxCoord tab_height = tab_size.y;
235 wxCoord tab_width = tab_size.x;
236 wxCoord tab_x = in_rect.x;
237 wxCoord tab_y = in_rect.y + in_rect.height - tab_height;
238
239 // select pen, brush and font for the tab to be drawn
240
241 if (active)
242 {
243 dc.SetPen(m_selected_bkpen);
244 dc.SetBrush(m_selected_bkbrush);
245 dc.SetFont(m_selected_font);
246 textx = selected_textx;
247 texty = selected_texty;
248 }
249 else
250 {
251 dc.SetPen(m_normal_bkpen);
252 dc.SetBrush(m_normal_bkbrush);
253 dc.SetFont(m_normal_font);
254 textx = normal_textx;
255 texty = normal_texty;
256 }
257
258
259 // -- draw line --
260
261 wxPoint points[7];
262 points[0].x = tab_x;
263 points[0].y = tab_y + tab_height - 1;
264 points[1].x = tab_x + tab_height - 3;
265 points[1].y = tab_y + 2;
266 points[2].x = tab_x + tab_height + 3;
267 points[2].y = tab_y;
268 points[3].x = tab_x + tab_width - 2;
269 points[3].y = tab_y;
270 points[4].x = tab_x + tab_width;
271 points[4].y = tab_y + 2;
272 points[5].x = tab_x + tab_width;
273 points[5].y = tab_y + tab_height - 1;
274 points[6] = points[0];
275
276
277 dc.DrawPolygon(6, points);
278
279 dc.SetPen(*wxGREY_PEN);
280
281 //dc.DrawLines(active ? 6 : 7, points);
282 dc.DrawLines(7, points);
283
284
285 int text_offset;
286
287 int close_button_width = 0;
288 if (close_button_state != wxAUI_BUTTON_STATE_HIDDEN)
289 {
290 close_button_width = m_active_close_bmp.GetWidth();
291 text_offset = tab_x + (tab_height/2) + ((tab_width-close_button_width)/2) - (textx/2);
292 }
293 else
294 {
295 text_offset = tab_x + (tab_height/3) + (tab_width/2) - (textx/2);
296 }
297
298
299 // draw tab text
300 dc.DrawText(caption,
301 text_offset,
302 (tab_y + tab_height)/2 - (texty/2) + 1);
303
304
305 // draw close button if necessary
306 if (close_button_state != wxAUI_BUTTON_STATE_HIDDEN)
307 {
308 wxBitmap bmp;
309 if (active)
310 bmp = m_active_close_bmp;
311 else
312 bmp = m_disabled_close_bmp;
313
314 wxRect rect(tab_x + tab_width - close_button_width - 1,
315 tab_y + (tab_height/2) - (bmp.GetHeight()/2) + 1,
316 close_button_width,
317 tab_height - 1);
318 DrawButtonS(dc, rect, bmp, *wxWHITE, close_button_state);
319
320 *out_button_rect = rect;
321 }
322
323
324 *out_tab_rect = wxRect(tab_x, tab_y, tab_width, tab_height);
325 }
326
327
328 wxSize wxAuiDefaultTabArt::GetTabSize(wxDC& dc,
329 wxWindow* WXUNUSED(wnd),
330 const wxString& caption,
331 bool WXUNUSED(active),
332 int close_button_state,
333 int* x_extent)
334 {
335 wxCoord measured_textx, measured_texty;
336
337 dc.SetFont(m_measuring_font);
338 dc.GetTextExtent(caption, &measured_textx, &measured_texty);
339
340 wxCoord tab_height = measured_texty + 4;
341 wxCoord tab_width = measured_textx + tab_height + 5;
342
343 if (close_button_state != wxAUI_BUTTON_STATE_HIDDEN)
344 tab_width += m_active_close_bmp.GetWidth();
345
346 *x_extent = tab_width - (tab_height/2) - 1;
347
348 return wxSize(tab_width, tab_height);
349 }
350
351
352 void wxAuiDefaultTabArt::DrawButton(
353 wxDC& dc,
354 wxWindow* WXUNUSED(wnd),
355 const wxRect& in_rect,
356 int bitmap_id,
357 int button_state,
358 int orientation,
359 const wxBitmap& bitmap_override,
360 wxRect* out_rect)
361 {
362 wxBitmap bmp;
363 wxRect rect;
364
365 if (bitmap_override.IsOk())
366 {
367 bmp = bitmap_override;
368 }
369 else
370 {
371 switch (bitmap_id)
372 {
373 case wxAUI_BUTTON_CLOSE:
374 if (button_state & wxAUI_BUTTON_STATE_DISABLED)
375 bmp = m_disabled_close_bmp;
376 else
377 bmp = m_active_close_bmp;
378 break;
379 case wxAUI_BUTTON_LEFT:
380 if (button_state & wxAUI_BUTTON_STATE_DISABLED)
381 bmp = m_disabled_left_bmp;
382 else
383 bmp = m_active_left_bmp;
384 break;
385 case wxAUI_BUTTON_RIGHT:
386 if (button_state & wxAUI_BUTTON_STATE_DISABLED)
387 bmp = m_disabled_right_bmp;
388 else
389 bmp = m_active_right_bmp;
390 break;
391 case wxAUI_BUTTON_WINDOWLIST:
392 if (button_state & wxAUI_BUTTON_STATE_DISABLED)
393 bmp = m_disabled_windowlist_bmp;
394 else
395 bmp = m_active_windowlist_bmp;
396 break;
397 }
398 }
399
400 if (!bmp.IsOk())
401 return;
402
403 rect = in_rect;
404
405 if (orientation == wxLEFT)
406 {
407 rect.SetX(in_rect.x);
408 rect.SetY(((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2));
409 rect.SetWidth(bmp.GetWidth());
410 rect.SetHeight(bmp.GetHeight());
411 }
412 else
413 {
414 rect = wxRect(in_rect.x + in_rect.width - bmp.GetWidth(),
415 ((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2),
416 bmp.GetWidth(), bmp.GetHeight());
417 }
418
419
420 DrawButtonS(dc, rect, bmp, *wxWHITE, button_state);
421
422 *out_rect = rect;
423 }
424
425
426
427
428 // -- GUI helper classes and functions --
429
430 class wxAuiCommandCapture : public wxEvtHandler
431 {
432 public:
433
434 wxAuiCommandCapture() { m_last_id = 0; }
435 int GetCommandId() const { return m_last_id; }
436
437 bool ProcessEvent(wxEvent& evt)
438 {
439 if (evt.GetEventType() == wxEVT_COMMAND_MENU_SELECTED)
440 {
441 m_last_id = evt.GetId();
442 return true;
443 }
444
445 if (GetNextHandler())
446 return GetNextHandler()->ProcessEvent(evt);
447
448 return false;
449 }
450
451 private:
452 int m_last_id;
453 };
454
455
456
457 int wxAuiDefaultTabArt::ShowWindowList(wxWindow* wnd,
458 const wxArrayString& items,
459 int active_idx)
460 {
461 wxMenu menuPopup;
462
463 size_t i, count = items.GetCount();
464 for (i = 0; i < count; ++i)
465 {
466 menuPopup.AppendCheckItem(1000+i, items.Item(i));
467 }
468
469 if (active_idx != -1)
470 {
471 menuPopup.Check(1000+active_idx, true);
472 }
473
474 // find out where to put the popup menu of window
475 // items. Subtract 100 for now to center the menu
476 // a bit, until a better mechanism can be implemented
477 wxPoint pt = ::wxGetMousePosition();
478 pt = wnd->ScreenToClient(pt);
479 if (pt.x < 100)
480 pt.x = 0;
481 else
482 pt.x -= 100;
483
484 // find out the screen coordinate at the bottom of the tab ctrl
485 wxRect cli_rect = wnd->GetClientRect();
486 pt.y = cli_rect.y + cli_rect.height;
487
488 wxAuiCommandCapture* cc = new wxAuiCommandCapture;
489 wnd->PushEventHandler(cc);
490 wnd->PopupMenu(&menuPopup, pt);
491 int command = cc->GetCommandId();
492 wnd->PopEventHandler(true);
493
494 if (command >= 1000)
495 return command-1000;
496
497 return -1;
498 }
499
500 int wxAuiDefaultTabArt::GetBestTabCtrlSize(wxWindow* wnd)
501 {
502 wxClientDC dc(wnd);
503 dc.SetFont(m_measuring_font);
504 int x_ext = 0;
505 wxSize s = GetTabSize(dc,
506 wnd,
507 wxT("ABCDEFGHIj"),
508 true,
509 wxAUI_BUTTON_STATE_HIDDEN,
510 &x_ext);
511 return s.y+3;
512 }
513
514 void wxAuiDefaultTabArt::SetNormalFont(const wxFont& font)
515 {
516 m_normal_font = font;
517 }
518
519 void wxAuiDefaultTabArt::SetSelectedFont(const wxFont& font)
520 {
521 m_selected_font = font;
522 }
523
524 void wxAuiDefaultTabArt::SetMeasuringFont(const wxFont& font)
525 {
526 m_measuring_font = font;
527 }
528
529
530
531
532
533
534 // -- wxAuiTabContainer class implementation --
535
536
537 // wxAuiTabContainer is a class which contains information about each
538 // tab. It also can render an entire tab control to a specified DC.
539 // It's not a window class itself, because this code will be used by
540 // the wxFrameMananger, where it is disadvantageous to have separate
541 // windows for each tab control in the case of "docked tabs"
542
543 // A derived class, wxAuiTabCtrl, is an actual wxWindow-derived window
544 // which can be used as a tab control in the normal sense.
545
546
547 wxAuiTabContainer::wxAuiTabContainer()
548 {
549 m_tab_offset = 0;
550 m_flags = 0;
551 m_art = new wxAuiDefaultTabArt;
552
553 AddButton(wxAUI_BUTTON_LEFT, wxLEFT);
554 AddButton(wxAUI_BUTTON_RIGHT, wxRIGHT);
555 AddButton(wxAUI_BUTTON_WINDOWLIST, wxRIGHT);
556 AddButton(wxAUI_BUTTON_CLOSE, wxRIGHT);
557 }
558
559 wxAuiTabContainer::~wxAuiTabContainer()
560 {
561 delete m_art;
562 }
563
564 void wxAuiTabContainer::SetArtProvider(wxAuiTabArt* art)
565 {
566 delete m_art;
567 m_art = art;
568 }
569
570 wxAuiTabArt* wxAuiTabContainer::GetArtProvider()
571 {
572 return m_art;
573 }
574
575 void wxAuiTabContainer::SetFlags(unsigned int flags)
576 {
577 m_flags = flags;
578
579 // check for new close button settings
580 RemoveButton(wxAUI_BUTTON_LEFT);
581 RemoveButton(wxAUI_BUTTON_RIGHT);
582 RemoveButton(wxAUI_BUTTON_WINDOWLIST);
583 RemoveButton(wxAUI_BUTTON_CLOSE);
584
585
586 if (flags & wxAUI_NB_SCROLL_BUTTONS)
587 {
588 AddButton(wxAUI_BUTTON_LEFT, wxLEFT);
589 AddButton(wxAUI_BUTTON_RIGHT, wxRIGHT);
590 }
591
592 if (flags & wxAUI_NB_WINDOWLIST_BUTTON)
593 {
594 AddButton(wxAUI_BUTTON_WINDOWLIST, wxRIGHT);
595 }
596
597 if (flags & wxAUI_NB_CLOSE_BUTTON)
598 {
599 AddButton(wxAUI_BUTTON_CLOSE, wxRIGHT);
600 }
601 }
602
603 unsigned int wxAuiTabContainer::GetFlags() const
604 {
605 return m_flags;
606 }
607
608
609 void wxAuiTabContainer::SetNormalFont(const wxFont& font)
610 {
611 m_art->SetNormalFont(font);
612 }
613
614 void wxAuiTabContainer::SetSelectedFont(const wxFont& font)
615 {
616 m_art->SetSelectedFont(font);
617 }
618
619 void wxAuiTabContainer::SetMeasuringFont(const wxFont& font)
620 {
621 m_art->SetMeasuringFont(font);
622 }
623
624 void wxAuiTabContainer::SetRect(const wxRect& rect)
625 {
626 m_rect = rect;
627 }
628
629 bool wxAuiTabContainer::AddPage(wxWindow* page,
630 const wxAuiNotebookPage& info)
631 {
632 wxAuiNotebookPage page_info;
633 page_info = info;
634 page_info.window = page;
635
636 m_pages.Add(page_info);
637
638 return true;
639 }
640
641 bool wxAuiTabContainer::InsertPage(wxWindow* page,
642 const wxAuiNotebookPage& info,
643 size_t idx)
644 {
645 wxAuiNotebookPage page_info;
646 page_info = info;
647 page_info.window = page;
648
649 if (idx >= m_pages.GetCount())
650 m_pages.Add(page_info);
651 else
652 m_pages.Insert(page_info, idx);
653
654 return true;
655 }
656
657 bool wxAuiTabContainer::MovePage(wxWindow* page,
658 size_t new_idx)
659 {
660 int idx = GetIdxFromWindow(page);
661 if (idx == -1)
662 return false;
663
664 // get page entry, make a copy of it
665 wxAuiNotebookPage p = GetPage(idx);
666
667 // remove old page entry
668 RemovePage(page);
669
670 // insert page where it should be
671 InsertPage(page, p, new_idx);
672
673 return true;
674 }
675
676 bool wxAuiTabContainer::RemovePage(wxWindow* wnd)
677 {
678 size_t i, page_count = m_pages.GetCount();
679 for (i = 0; i < page_count; ++i)
680 {
681 wxAuiNotebookPage& page = m_pages.Item(i);
682 if (page.window == wnd)
683 {
684 m_pages.RemoveAt(i);
685 return true;
686 }
687 }
688
689 return false;
690 }
691
692 bool wxAuiTabContainer::SetActivePage(wxWindow* wnd)
693 {
694 bool found = false;
695
696 size_t i, page_count = m_pages.GetCount();
697 for (i = 0; i < page_count; ++i)
698 {
699 wxAuiNotebookPage& page = m_pages.Item(i);
700 if (page.window == wnd)
701 {
702 page.active = true;
703 found = true;
704 }
705 else
706 {
707 page.active = false;
708 }
709 }
710
711 return found;
712 }
713
714 void wxAuiTabContainer::SetNoneActive()
715 {
716 size_t i, page_count = m_pages.GetCount();
717 for (i = 0; i < page_count; ++i)
718 {
719 wxAuiNotebookPage& page = m_pages.Item(i);
720 page.active = false;
721 }
722 }
723
724 bool wxAuiTabContainer::SetActivePage(size_t page)
725 {
726 if (page >= m_pages.GetCount())
727 return false;
728
729 return SetActivePage(m_pages.Item(page).window);
730 }
731
732 int wxAuiTabContainer::GetActivePage() const
733 {
734 size_t i, page_count = m_pages.GetCount();
735 for (i = 0; i < page_count; ++i)
736 {
737 wxAuiNotebookPage& page = m_pages.Item(i);
738 if (page.active)
739 return i;
740 }
741
742 return -1;
743 }
744
745 wxWindow* wxAuiTabContainer::GetWindowFromIdx(size_t idx) const
746 {
747 if (idx >= m_pages.GetCount())
748 return NULL;
749
750 return m_pages[idx].window;
751 }
752
753 int wxAuiTabContainer::GetIdxFromWindow(wxWindow* wnd) const
754 {
755 size_t i, page_count = m_pages.GetCount();
756 for (i = 0; i < page_count; ++i)
757 {
758 wxAuiNotebookPage& page = m_pages.Item(i);
759 if (page.window == wnd)
760 return i;
761 }
762 return -1;
763 }
764
765 wxAuiNotebookPage& wxAuiTabContainer::GetPage(size_t idx)
766 {
767 wxASSERT_MSG(idx < m_pages.GetCount(), wxT("Invalid Page index"));
768
769 return m_pages[idx];
770 }
771
772 wxAuiNotebookPageArray& wxAuiTabContainer::GetPages()
773 {
774 return m_pages;
775 }
776
777 size_t wxAuiTabContainer::GetPageCount() const
778 {
779 return m_pages.GetCount();
780 }
781
782 void wxAuiTabContainer::AddButton(int id,
783 int location,
784 const wxBitmap& normal_bitmap,
785 const wxBitmap& disabled_bitmap)
786 {
787 wxAuiTabContainerButton button;
788 button.id = id;
789 button.bitmap = normal_bitmap;
790 button.dis_bitmap = disabled_bitmap;
791 button.location = location;
792 button.cur_state = wxAUI_BUTTON_STATE_NORMAL;
793
794 m_buttons.Add(button);
795 }
796
797 void wxAuiTabContainer::RemoveButton(int id)
798 {
799 size_t i, button_count = m_buttons.GetCount();
800
801 for (i = 0; i < button_count; ++i)
802 {
803 if (m_buttons.Item(i).id == id)
804 {
805 m_buttons.RemoveAt(i);
806 return;
807 }
808 }
809 }
810
811
812
813 size_t wxAuiTabContainer::GetTabOffset() const
814 {
815 return m_tab_offset;
816 }
817
818 void wxAuiTabContainer::SetTabOffset(size_t offset)
819 {
820 m_tab_offset = offset;
821 }
822
823 // Render() renders the tab catalog to the specified DC
824 // It is a virtual function and can be overridden to
825 // provide custom drawing capabilities
826 void wxAuiTabContainer::Render(wxDC* raw_dc, wxWindow* wnd)
827 {
828 wxMemoryDC dc;
829 wxBitmap bmp;
830 size_t i;
831 size_t page_count = m_pages.GetCount();
832 size_t button_count = m_buttons.GetCount();
833
834 // create off-screen bitmap
835 bmp.Create(m_rect.GetWidth(), m_rect.GetHeight());
836 dc.SelectObject(bmp);
837
838
839 // find out if size of tabs is larger than can be
840 // afforded on screen
841 int total_width = 0;
842 int visible_width = 0;
843 for (i = 0; i < page_count; ++i)
844 {
845 wxAuiNotebookPage& page = m_pages.Item(i);
846
847 // determine if a close button is on this tab
848 bool close_button = false;
849 if ((m_flags & wxAUI_NB_CLOSE_ON_ALL_TABS) != 0 ||
850 ((m_flags & wxAUI_NB_CLOSE_ON_ACTIVE_TAB) != 0 && page.active))
851 {
852 close_button = true;
853 }
854
855
856 int x_extent = 0;
857 wxSize size = m_art->GetTabSize(dc,
858 wnd,
859 page.caption,
860 page.active,
861 close_button ?
862 wxAUI_BUTTON_STATE_NORMAL :
863 wxAUI_BUTTON_STATE_HIDDEN,
864 &x_extent);
865
866 if (i+1 < page_count)
867 total_width += x_extent;
868 else
869 total_width += size.x;
870
871 if (i >= m_tab_offset)
872 {
873 if (i+1 < page_count)
874 visible_width += x_extent;
875 else
876 visible_width += size.x;
877 }
878 }
879
880 if (total_width > m_rect.GetWidth() - 20 || m_tab_offset != 0)
881 {
882 // show left/right buttons
883 for (i = 0; i < button_count; ++i)
884 {
885 wxAuiTabContainerButton& button = m_buttons.Item(i);
886 if (button.id == wxAUI_BUTTON_LEFT ||
887 button.id == wxAUI_BUTTON_RIGHT)
888 {
889 button.cur_state &= ~wxAUI_BUTTON_STATE_HIDDEN;
890 }
891 }
892 }
893 else
894 {
895 // hide left/right buttons
896 for (i = 0; i < button_count; ++i)
897 {
898 wxAuiTabContainerButton& button = m_buttons.Item(i);
899 if (button.id == wxAUI_BUTTON_LEFT ||
900 button.id == wxAUI_BUTTON_RIGHT)
901 {
902 button.cur_state |= wxAUI_BUTTON_STATE_HIDDEN;
903 }
904 }
905 }
906
907 // determine whether left button should be enabled
908 for (i = 0; i < button_count; ++i)
909 {
910 wxAuiTabContainerButton& button = m_buttons.Item(i);
911 if (button.id == wxAUI_BUTTON_LEFT)
912 {
913 if (m_tab_offset == 0)
914 button.cur_state |= wxAUI_BUTTON_STATE_DISABLED;
915 else
916 button.cur_state &= ~wxAUI_BUTTON_STATE_DISABLED;
917 }
918 if (button.id == wxAUI_BUTTON_RIGHT)
919 {
920 if (visible_width < m_rect.GetWidth() - ((int)button_count*16))
921 button.cur_state |= wxAUI_BUTTON_STATE_DISABLED;
922 else
923 button.cur_state &= ~wxAUI_BUTTON_STATE_DISABLED;
924 }
925 }
926
927
928
929 // draw background
930 m_art->DrawBackground(dc, wnd, m_rect);
931
932 // draw buttons
933 int left_buttons_width = 0;
934 int right_buttons_width = 0;
935
936 int offset = 0;
937
938 // draw the buttons on the right side
939 offset = m_rect.x + m_rect.width;
940 for (i = 0; i < button_count; ++i)
941 {
942 wxAuiTabContainerButton& button = m_buttons.Item(button_count - i - 1);
943
944 if (button.location != wxRIGHT)
945 continue;
946 if (button.cur_state & wxAUI_BUTTON_STATE_HIDDEN)
947 continue;
948
949 wxRect button_rect = m_rect;
950 button_rect.SetY(1);
951 button_rect.SetWidth(offset);
952
953 m_art->DrawButton(dc,
954 wnd,
955 button_rect,
956 button.id,
957 button.cur_state,
958 wxRIGHT,
959 wxNullBitmap,
960 &button.rect);
961
962 offset -= button.rect.GetWidth();
963 right_buttons_width += button.rect.GetWidth();
964 }
965
966
967
968 offset = 0;
969
970 // draw the buttons on the left side
971
972 for (i = 0; i < button_count; ++i)
973 {
974 wxAuiTabContainerButton& button = m_buttons.Item(button_count - i - 1);
975
976 if (button.location != wxLEFT)
977 continue;
978 if (button.cur_state & wxAUI_BUTTON_STATE_HIDDEN)
979 continue;
980
981 wxRect button_rect(offset, 1, 1000, m_rect.height);
982
983 m_art->DrawButton(dc,
984 wnd,
985 button_rect,
986 button.id,
987 button.cur_state,
988 wxLEFT,
989 wxNullBitmap,
990 &button.rect);
991
992 offset += button.rect.GetWidth();
993 left_buttons_width += button.rect.GetWidth();
994 }
995
996 offset = left_buttons_width;
997
998 // set a clipping region to the tabs don't draw over the buttons
999 dc.SetClippingRegion(left_buttons_width, 0,
1000 m_rect.GetWidth() - right_buttons_width - left_buttons_width - 2,
1001 m_rect.GetHeight());
1002
1003
1004
1005 // prepare the tab-close-button array
1006 while (m_tab_close_buttons.GetCount() < page_count)
1007 {
1008 wxAuiTabContainerButton tempbtn;
1009 tempbtn.id = wxAUI_BUTTON_CLOSE;
1010 tempbtn.location = wxCENTER;
1011 tempbtn.cur_state = wxAUI_BUTTON_STATE_HIDDEN;
1012 m_tab_close_buttons.Add(tempbtn);
1013 }
1014
1015 for (i = 0; i < m_tab_offset; ++i)
1016 {
1017 // buttons before the tab offset must be set to hidden
1018 m_tab_close_buttons.Item(i).cur_state = wxAUI_BUTTON_STATE_HIDDEN;
1019 }
1020
1021
1022 // draw the tabs
1023
1024 size_t active = 999;
1025 int active_offset = 0;
1026
1027 int x_extent = 0;
1028 wxRect rect = m_rect;
1029 rect.y = 0;
1030 rect.width = 1000;
1031 rect.height = m_rect.height;
1032
1033 for (i = m_tab_offset; i < page_count; ++i)
1034 {
1035 wxAuiNotebookPage& page = m_pages.Item(i);
1036 wxAuiTabContainerButton& tab_button = m_tab_close_buttons.Item(i);
1037
1038 // determine if a close button is on this tab
1039 bool close_button = false;
1040 if ((m_flags & wxAUI_NB_CLOSE_ON_ALL_TABS) != 0 ||
1041 ((m_flags & wxAUI_NB_CLOSE_ON_ACTIVE_TAB) != 0 && page.active))
1042 {
1043 close_button = true;
1044 if (tab_button.cur_state == wxAUI_BUTTON_STATE_HIDDEN)
1045 {
1046 tab_button.id = wxAUI_BUTTON_CLOSE;
1047 tab_button.cur_state = wxAUI_BUTTON_STATE_NORMAL;
1048 tab_button.location = wxCENTER;
1049 }
1050 }
1051 else
1052 {
1053 tab_button.cur_state = wxAUI_BUTTON_STATE_HIDDEN;
1054 }
1055
1056 rect.x = offset;
1057
1058 m_art->DrawTab(dc,
1059 wnd,
1060 rect,
1061 page.caption,
1062 page.active,
1063 tab_button.cur_state,
1064 &page.rect,
1065 &tab_button.rect,
1066 &x_extent);
1067
1068 if (page.active)
1069 {
1070 active = i;
1071 active_offset = offset;
1072 }
1073
1074 offset += x_extent;
1075 }
1076
1077 // draw the active tab again so it stands in the foreground
1078 if (active >= m_tab_offset && active < m_pages.GetCount())
1079 {
1080 wxAuiNotebookPage& page = m_pages.Item(active);
1081
1082 wxAuiTabContainerButton& tab_button = m_tab_close_buttons.Item(active);
1083
1084 // determine if a close button is on this tab
1085 bool close_button = false;
1086 if ((m_flags & wxAUI_NB_CLOSE_ON_ALL_TABS) != 0 ||
1087 ((m_flags & wxAUI_NB_CLOSE_ON_ACTIVE_TAB) != 0 && page.active))
1088 {
1089 close_button = true;
1090 }
1091
1092 rect.x = active_offset;
1093 m_art->DrawTab(dc,
1094 wnd,
1095 rect,
1096 page.caption,
1097 page.active,
1098 tab_button.cur_state,
1099 &page.rect,
1100 &tab_button.rect,
1101 &x_extent);
1102 }
1103
1104 dc.DestroyClippingRegion();
1105
1106 raw_dc->Blit(m_rect.x, m_rect.y,
1107 m_rect.GetWidth(), m_rect.GetHeight(),
1108 &dc, 0, 0);
1109 }
1110
1111
1112 // TabHitTest() tests if a tab was hit, passing the window pointer
1113 // back if that condition was fulfilled. The function returns
1114 // true if a tab was hit, otherwise false
1115 bool wxAuiTabContainer::TabHitTest(int x, int y, wxWindow** hit) const
1116 {
1117 if (!m_rect.Contains(x,y))
1118 return false;
1119
1120 wxAuiTabContainerButton* btn = NULL;
1121 if (ButtonHitTest(x, y, &btn))
1122 {
1123 if (m_buttons.Index(*btn) != wxNOT_FOUND)
1124 return false;
1125 }
1126
1127 size_t i, page_count = m_pages.GetCount();
1128
1129 for (i = m_tab_offset; i < page_count; ++i)
1130 {
1131 wxAuiNotebookPage& page = m_pages.Item(i);
1132 if (page.rect.Contains(x,y))
1133 {
1134 if (hit)
1135 *hit = page.window;
1136 return true;
1137 }
1138 }
1139
1140 return false;
1141 }
1142
1143 // ButtonHitTest() tests if a button was hit. The function returns
1144 // true if a button was hit, otherwise false
1145 bool wxAuiTabContainer::ButtonHitTest(int x, int y,
1146 wxAuiTabContainerButton** hit) const
1147 {
1148 if (!m_rect.Contains(x,y))
1149 return false;
1150
1151 size_t i, button_count;
1152
1153
1154 button_count = m_buttons.GetCount();
1155 for (i = 0; i < button_count; ++i)
1156 {
1157 wxAuiTabContainerButton& button = m_buttons.Item(i);
1158 if (button.rect.Contains(x,y))
1159 {
1160 if (hit)
1161 *hit = &button;
1162 return true;
1163 }
1164 }
1165
1166 button_count = m_tab_close_buttons.GetCount();
1167 for (i = 0; i < button_count; ++i)
1168 {
1169 wxAuiTabContainerButton& button = m_tab_close_buttons.Item(i);
1170 if (button.rect.Contains(x,y))
1171 {
1172 if (hit)
1173 *hit = &button;
1174 return true;
1175 }
1176 }
1177
1178 return false;
1179 }
1180
1181
1182
1183 // the utility function ShowWnd() is the same as show,
1184 // except it handles wxAuiMDIChildFrame windows as well,
1185 // as the Show() method on this class is "unplugged"
1186 static void ShowWnd(wxWindow* wnd, bool show)
1187 {
1188 if (wnd->IsKindOf(CLASSINFO(wxAuiMDIChildFrame)))
1189 {
1190 wxAuiMDIChildFrame* cf = (wxAuiMDIChildFrame*)wnd;
1191 cf->DoShow(show);
1192 }
1193 else
1194 {
1195 wnd->Show(show);
1196 }
1197 }
1198
1199
1200 // DoShowHide() this function shows the active window, then
1201 // hides all of the other windows (in that order)
1202 void wxAuiTabContainer::DoShowHide()
1203 {
1204 wxAuiNotebookPageArray& pages = GetPages();
1205 size_t i, page_count = pages.GetCount();
1206
1207 // show new active page first
1208 for (i = 0; i < page_count; ++i)
1209 {
1210 wxAuiNotebookPage& page = pages.Item(i);
1211 if (page.active)
1212 {
1213 ShowWnd(page.window, true);
1214 break;
1215 }
1216 }
1217
1218 // hide all other pages
1219 for (i = 0; i < page_count; ++i)
1220 {
1221 wxAuiNotebookPage& page = pages.Item(i);
1222 ShowWnd(page.window, page.active);
1223 }
1224 }
1225
1226
1227
1228
1229
1230
1231 // -- wxAuiTabCtrl class implementation --
1232
1233
1234
1235 BEGIN_EVENT_TABLE(wxAuiTabCtrl, wxControl)
1236 EVT_PAINT(wxAuiTabCtrl::OnPaint)
1237 EVT_ERASE_BACKGROUND(wxAuiTabCtrl::OnEraseBackground)
1238 EVT_SIZE(wxAuiTabCtrl::OnSize)
1239 EVT_LEFT_DOWN(wxAuiTabCtrl::OnLeftDown)
1240 EVT_LEFT_UP(wxAuiTabCtrl::OnLeftUp)
1241 EVT_MOTION(wxAuiTabCtrl::OnMotion)
1242 EVT_LEAVE_WINDOW(wxAuiTabCtrl::OnLeaveWindow)
1243 EVT_AUINOTEBOOK_BUTTON(-1, wxAuiTabCtrl::OnButton)
1244 END_EVENT_TABLE()
1245
1246
1247 wxAuiTabCtrl::wxAuiTabCtrl(wxWindow* parent,
1248 wxWindowID id,
1249 const wxPoint& pos,
1250 const wxSize& size,
1251 long style) : wxControl(parent, id, pos, size, style)
1252 {
1253 m_click_pt = wxDefaultPosition;
1254 m_is_dragging = false;
1255 m_hover_button = NULL;
1256 }
1257
1258 wxAuiTabCtrl::~wxAuiTabCtrl()
1259 {
1260 }
1261
1262 void wxAuiTabCtrl::OnPaint(wxPaintEvent&)
1263 {
1264 wxPaintDC dc(this);
1265
1266 dc.SetFont(GetFont());
1267
1268 if (GetPageCount() > 0)
1269 Render(&dc, this);
1270 }
1271
1272 void wxAuiTabCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
1273 {
1274 }
1275
1276 void wxAuiTabCtrl::OnSize(wxSizeEvent& evt)
1277 {
1278 wxSize s = evt.GetSize();
1279 wxRect r(0, 0, s.GetWidth(), s.GetHeight());
1280 SetRect(r);
1281 }
1282
1283 void wxAuiTabCtrl::OnLeftDown(wxMouseEvent& evt)
1284 {
1285 CaptureMouse();
1286 m_click_pt = wxDefaultPosition;
1287 m_is_dragging = false;
1288 m_click_tab = NULL;
1289
1290 wxWindow* wnd;
1291 if (TabHitTest(evt.m_x, evt.m_y, &wnd))
1292 {
1293 wxAuiNotebookEvent e(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, m_windowId);
1294 e.SetSelection(GetIdxFromWindow(wnd));
1295 e.SetOldSelection(GetActivePage());
1296 e.SetEventObject(this);
1297 GetEventHandler()->ProcessEvent(e);
1298
1299 m_click_pt.x = evt.m_x;
1300 m_click_pt.y = evt.m_y;
1301 m_click_tab = wnd;
1302 }
1303
1304 if (m_hover_button)
1305 {
1306 m_hover_button->cur_state = wxAUI_BUTTON_STATE_PRESSED;
1307 Refresh();
1308 Update();
1309 }
1310 }
1311
1312 void wxAuiTabCtrl::OnLeftUp(wxMouseEvent&)
1313 {
1314 if (GetCapture() == this)
1315 ReleaseMouse();
1316
1317 if (m_is_dragging)
1318 {
1319 wxAuiNotebookEvent evt(wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, m_windowId);
1320 evt.SetSelection(GetIdxFromWindow(m_click_tab));
1321 evt.SetOldSelection(evt.GetSelection());
1322 evt.SetEventObject(this);
1323 GetEventHandler()->ProcessEvent(evt);
1324 return;
1325 }
1326
1327 if (m_hover_button)
1328 {
1329 m_hover_button->cur_state = wxAUI_BUTTON_STATE_HOVER;
1330 Refresh();
1331 Update();
1332
1333 if (!(m_hover_button->cur_state & wxAUI_BUTTON_STATE_DISABLED))
1334 {
1335 wxAuiNotebookEvent evt(wxEVT_COMMAND_AUINOTEBOOK_BUTTON, m_windowId);
1336 evt.SetInt(m_hover_button->id);
1337 evt.SetEventObject(this);
1338 GetEventHandler()->ProcessEvent(evt);
1339 }
1340 }
1341
1342 m_click_pt = wxDefaultPosition;
1343 m_is_dragging = false;
1344 m_click_tab = NULL;
1345 }
1346
1347 void wxAuiTabCtrl::OnMotion(wxMouseEvent& evt)
1348 {
1349 wxPoint pos = evt.GetPosition();
1350
1351 // check if the mouse is hovering above a button
1352 wxAuiTabContainerButton* button;
1353 if (ButtonHitTest(pos.x, pos.y, &button))
1354 {
1355 if (m_hover_button && button != m_hover_button)
1356 {
1357 m_hover_button->cur_state = wxAUI_BUTTON_STATE_NORMAL;
1358 m_hover_button = NULL;
1359 Refresh();
1360 Update();
1361 }
1362
1363 if (button->cur_state != wxAUI_BUTTON_STATE_HOVER)
1364 {
1365 button->cur_state = wxAUI_BUTTON_STATE_HOVER;
1366 Refresh();
1367 Update();
1368 m_hover_button = button;
1369 return;
1370 }
1371 }
1372 else
1373 {
1374 if (m_hover_button)
1375 {
1376 m_hover_button->cur_state = wxAUI_BUTTON_STATE_NORMAL;
1377 m_hover_button = NULL;
1378 Refresh();
1379 Update();
1380 }
1381 }
1382
1383
1384 if (!evt.LeftIsDown() || m_click_pt == wxDefaultPosition)
1385 return;
1386
1387 if (m_is_dragging)
1388 {
1389 wxAuiNotebookEvent evt(wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, m_windowId);
1390 evt.SetSelection(GetIdxFromWindow(m_click_tab));
1391 evt.SetOldSelection(evt.GetSelection());
1392 evt.SetEventObject(this);
1393 GetEventHandler()->ProcessEvent(evt);
1394 return;
1395 }
1396
1397
1398 int drag_x_threshold = wxSystemSettings::GetMetric(wxSYS_DRAG_X);
1399 int drag_y_threshold = wxSystemSettings::GetMetric(wxSYS_DRAG_Y);
1400
1401 if (abs(pos.x - m_click_pt.x) > drag_x_threshold ||
1402 abs(pos.y - m_click_pt.y) > drag_y_threshold)
1403 {
1404 wxAuiNotebookEvent evt(wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, m_windowId);
1405 evt.SetSelection(GetIdxFromWindow(m_click_tab));
1406 evt.SetOldSelection(evt.GetSelection());
1407 evt.SetEventObject(this);
1408 GetEventHandler()->ProcessEvent(evt);
1409
1410 m_is_dragging = true;
1411 }
1412 }
1413
1414 void wxAuiTabCtrl::OnLeaveWindow(wxMouseEvent& WXUNUSED(event))
1415 {
1416 if (m_hover_button)
1417 {
1418 m_hover_button->cur_state = wxAUI_BUTTON_STATE_NORMAL;
1419 m_hover_button = NULL;
1420 Refresh();
1421 Update();
1422 }
1423 }
1424
1425 void wxAuiTabCtrl::OnButton(wxAuiNotebookEvent& event)
1426 {
1427 int button = event.GetInt();
1428
1429 if (button == wxAUI_BUTTON_LEFT || button == wxAUI_BUTTON_RIGHT)
1430 {
1431 if (button == wxAUI_BUTTON_LEFT)
1432 {
1433 if (GetTabOffset() > 0)
1434 {
1435 SetTabOffset(GetTabOffset()-1);
1436 Refresh();
1437 Update();
1438 }
1439 }
1440 else
1441 {
1442 SetTabOffset(GetTabOffset()+1);
1443 Refresh();
1444 Update();
1445 }
1446 }
1447 else if (button == wxAUI_BUTTON_WINDOWLIST)
1448 {
1449 wxArrayString as;
1450
1451 size_t i, page_count = m_pages.GetCount();
1452 for (i = 0; i < page_count; ++i)
1453 {
1454 wxAuiNotebookPage& page = m_pages.Item(i);
1455 as.Add(page.caption);
1456 }
1457
1458 int idx = GetArtProvider()->ShowWindowList(this, as, GetActivePage());
1459
1460 if (idx != -1)
1461 {
1462 wxAuiNotebookEvent e(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, m_windowId);
1463 e.SetSelection(idx);
1464 e.SetOldSelection(GetActivePage());
1465 e.SetEventObject(this);
1466 GetEventHandler()->ProcessEvent(e);
1467 }
1468 }
1469 else
1470 {
1471 event.Skip();
1472 }
1473 }
1474
1475 // wxTabFrame is an interesting case. It's important that all child pages
1476 // of the multi-notebook control are all actually children of that control
1477 // (and not grandchildren). wxTabFrame facilitates this. There is one
1478 // instance of wxTabFrame for each tab control inside the multi-notebook.
1479 // It's important to know that wxTabFrame is not a real window, but it merely
1480 // used to capture the dimensions/positioning of the internal tab control and
1481 // it's managed page windows
1482
1483 class wxTabFrame : public wxWindow
1484 {
1485 public:
1486
1487 wxTabFrame()
1488 {
1489 m_tabs = NULL;
1490 m_rect = wxRect(0,0,200,200);
1491 m_tab_ctrl_height = 20;
1492 }
1493
1494 void SetTabCtrlHeight(int h)
1495 {
1496 m_tab_ctrl_height = h;
1497 }
1498
1499 void DoSetSize(int x, int y,
1500 int width, int height,
1501 int WXUNUSED(sizeFlags = wxSIZE_AUTO))
1502 {
1503 m_rect = wxRect(x, y, width, height);
1504 DoSizing();
1505 }
1506
1507 void DoGetClientSize(int* x, int* y) const
1508 {
1509 *x = m_rect.width;
1510 *y = m_rect.height;
1511 }
1512
1513 bool Show( bool WXUNUSED(show = true) ) { return false; }
1514
1515 void DoSizing()
1516 {
1517 if (!m_tabs)
1518 return;
1519
1520 int tab_height = wxMin(m_rect.height, m_tab_ctrl_height);
1521 m_tab_rect = wxRect(m_rect.x, m_rect.y, m_rect.width, tab_height);
1522 m_tabs->SetSize(m_rect.x, m_rect.y, m_rect.width, tab_height);
1523 m_tabs->SetRect(wxRect(0, 0, m_rect.width, tab_height));
1524 m_tabs->Refresh();
1525 m_tabs->Update();
1526
1527 wxAuiNotebookPageArray& pages = m_tabs->GetPages();
1528 size_t i, page_count = pages.GetCount();
1529
1530 for (i = 0; i < page_count; ++i)
1531 {
1532 wxAuiNotebookPage& page = pages.Item(i);
1533 page.window->SetSize(m_rect.x, m_rect.y+tab_height,
1534 m_rect.width, m_rect.height-tab_height);
1535
1536 if (page.window->IsKindOf(CLASSINFO(wxAuiMDIChildFrame)))
1537 {
1538 wxAuiMDIChildFrame* wnd = (wxAuiMDIChildFrame*)page.window;
1539 wnd->ApplyMDIChildFrameRect();
1540 }
1541 }
1542 }
1543
1544 void DoGetSize(int* x, int* y) const
1545 {
1546 if (x)
1547 *x = m_rect.GetWidth();
1548 if (y)
1549 *y = m_rect.GetHeight();
1550 }
1551
1552 void Update()
1553 {
1554 // does nothing
1555 }
1556
1557 public:
1558
1559 wxRect m_rect;
1560 wxRect m_tab_rect;
1561 wxAuiTabCtrl* m_tabs;
1562 int m_tab_ctrl_height;
1563 };
1564
1565
1566
1567
1568
1569 // -- wxAuiNotebook class implementation --
1570
1571 BEGIN_EVENT_TABLE(wxAuiNotebook, wxControl)
1572 //EVT_ERASE_BACKGROUND(wxAuiNotebook::OnEraseBackground)
1573 //EVT_SIZE(wxAuiNotebook::OnSize)
1574 //EVT_LEFT_DOWN(wxAuiNotebook::OnLeftDown)
1575 EVT_CHILD_FOCUS(wxAuiNotebook::OnChildFocus)
1576 EVT_COMMAND_RANGE(10000, 10100,
1577 wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING,
1578 wxAuiNotebook::OnTabClicked)
1579 EVT_COMMAND_RANGE(10000, 10100,
1580 wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG,
1581 wxAuiNotebook::OnTabBeginDrag)
1582 EVT_COMMAND_RANGE(10000, 10100,
1583 wxEVT_COMMAND_AUINOTEBOOK_END_DRAG,
1584 wxAuiNotebook::OnTabEndDrag)
1585 EVT_COMMAND_RANGE(10000, 10100,
1586 wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION,
1587 wxAuiNotebook::OnTabDragMotion)
1588 EVT_COMMAND_RANGE(10000, 10100,
1589 wxEVT_COMMAND_AUINOTEBOOK_BUTTON,
1590 wxAuiNotebook::OnTabButton)
1591 END_EVENT_TABLE()
1592
1593 wxAuiNotebook::wxAuiNotebook()
1594 {
1595 m_curpage = -1;
1596 m_tab_id_counter = 10000;
1597 m_dummy_wnd = NULL;
1598 m_tab_ctrl_height = 20;
1599 }
1600
1601 wxAuiNotebook::wxAuiNotebook(wxWindow *parent,
1602 wxWindowID id,
1603 const wxPoint& pos,
1604 const wxSize& size,
1605 long style) : wxControl(parent, id, pos, size, style)
1606 {
1607 InitNotebook(style);
1608 }
1609
1610 bool wxAuiNotebook::Create(wxWindow* parent,
1611 wxWindowID id,
1612 const wxPoint& pos,
1613 const wxSize& size,
1614 long style)
1615 {
1616 if (!wxControl::Create(parent, id, pos, size, style))
1617 return false;
1618
1619 InitNotebook(style);
1620
1621 return true;
1622 }
1623
1624 // InitNotebook() contains common initialization
1625 // code called by all constructors
1626 void wxAuiNotebook::InitNotebook(long style)
1627 {
1628 m_curpage = -1;
1629 m_tab_id_counter = 10000;
1630 m_dummy_wnd = NULL;
1631 m_tab_ctrl_height = 20;
1632 m_flags = (unsigned int)style;
1633
1634 m_normal_font = *wxNORMAL_FONT;
1635 m_selected_font = *wxNORMAL_FONT;
1636 m_selected_font.SetWeight(wxBOLD);
1637
1638 // choose a default for the tab height
1639 m_tab_ctrl_height = m_tabs.GetArtProvider()->GetBestTabCtrlSize(this);
1640
1641 m_dummy_wnd = new wxWindow(this, wxID_ANY, wxPoint(0,0), wxSize(0,0));
1642 m_dummy_wnd->SetSize(200, 200);
1643 m_dummy_wnd->Show(false);
1644
1645 m_mgr.SetManagedWindow(this);
1646
1647 m_mgr.AddPane(m_dummy_wnd,
1648 wxAuiPaneInfo().Name(wxT("dummy")).Bottom().Show(false));
1649
1650 m_mgr.Update();
1651 }
1652
1653 wxAuiNotebook::~wxAuiNotebook()
1654 {
1655 m_mgr.UnInit();
1656 }
1657
1658 void wxAuiNotebook::SetArtProvider(wxAuiTabArt* art)
1659 {
1660 m_tabs.SetArtProvider(art);
1661 }
1662
1663 wxAuiTabArt* wxAuiNotebook::GetArtProvider()
1664 {
1665 return m_tabs.GetArtProvider();
1666 }
1667
1668 void wxAuiNotebook::SetWindowStyleFlag(long style)
1669 {
1670 wxControl::SetWindowStyleFlag(style);
1671
1672 m_flags = (unsigned int)style;
1673
1674 // if the control is already initialized
1675 if (m_mgr.GetManagedWindow() == (wxWindow*)this)
1676 {
1677 // let all of the tab children know about the new style
1678
1679 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
1680 size_t i, pane_count = all_panes.GetCount();
1681 for (i = 0; i < pane_count; ++i)
1682 {
1683 wxAuiPaneInfo& pane = all_panes.Item(i);
1684 if (pane.name == wxT("dummy"))
1685 continue;
1686 wxAuiTabCtrl* tabctrl = ((wxTabFrame*)pane.window)->m_tabs;
1687 tabctrl->SetFlags(m_flags);
1688 tabctrl->Refresh();
1689 tabctrl->Update();
1690 }
1691 }
1692 }
1693
1694
1695 bool wxAuiNotebook::AddPage(wxWindow* page,
1696 const wxString& caption,
1697 bool select,
1698 const wxBitmap& bitmap)
1699 {
1700 return InsertPage(GetPageCount(), page, caption, select, bitmap);
1701 }
1702
1703 bool wxAuiNotebook::InsertPage(size_t page_idx,
1704 wxWindow* page,
1705 const wxString& caption,
1706 bool select,
1707 const wxBitmap& bitmap)
1708 {
1709 wxAuiNotebookPage info;
1710 info.window = page;
1711 info.caption = caption;
1712 info.bitmap = bitmap;
1713 info.active = false;
1714
1715 // if there are currently no tabs, the first added
1716 // tab must be active
1717 if (m_tabs.GetPageCount() == 0)
1718 info.active = true;
1719
1720 m_tabs.InsertPage(page, info, page_idx);
1721
1722 wxAuiTabCtrl* active_tabctrl = GetActiveTabCtrl();
1723 if (page_idx >= active_tabctrl->GetPageCount())
1724 active_tabctrl->AddPage(page, info);
1725 else
1726 active_tabctrl->InsertPage(page, info, page_idx);
1727
1728 DoSizing();
1729 active_tabctrl->DoShowHide();
1730
1731 if (select)
1732 {
1733 int idx = m_tabs.GetIdxFromWindow(page);
1734 wxASSERT_MSG(idx != -1, wxT("Invalid Page index returned on wxAuiNotebook::InsertPage()"));
1735
1736 SetSelection(idx);
1737 }
1738
1739 return true;
1740 }
1741
1742
1743 // DeletePage() removes a tab from the multi-notebook,
1744 // and destroys the window as well
1745 bool wxAuiNotebook::DeletePage(size_t page_idx)
1746 {
1747 wxWindow* wnd = m_tabs.GetWindowFromIdx(page_idx);
1748
1749 if (!RemovePage(page_idx))
1750 return false;
1751
1752
1753 // actually destroy the window now
1754 if (wnd->IsKindOf(CLASSINFO(wxAuiMDIChildFrame)))
1755 {
1756 // delete the child frame with pending delete, as is
1757 // customary with frame windows
1758 if (!wxPendingDelete.Member(wnd))
1759 wxPendingDelete.Append(wnd);
1760 }
1761 else
1762 {
1763 wnd->Destroy();
1764 }
1765
1766 return true;
1767
1768 /*
1769
1770 wxWindow* wnd = m_tabs.GetWindowFromIdx(page_idx);
1771 wxWindow* new_active = NULL;
1772
1773 // find out which onscreen tab ctrl owns this tab
1774 wxAuiTabCtrl* ctrl;
1775 int ctrl_idx;
1776 if (!FindTab(wnd, &ctrl, &ctrl_idx))
1777 return false;
1778
1779 // find a new page and set it as active
1780 int new_idx = ctrl_idx+1;
1781 if (new_idx >= (int)ctrl->GetPageCount())
1782 new_idx = ctrl_idx-1;
1783
1784 if (new_idx >= 0 && new_idx < (int)ctrl->GetPageCount())
1785 {
1786 new_active = ctrl->GetWindowFromIdx(new_idx);
1787 }
1788 else
1789 {
1790 // set the active page to the first page that
1791 // isn't the one being deleted
1792 size_t i, page_count = m_tabs.GetPageCount();
1793 for (i = 0; i < page_count; ++i)
1794 {
1795 wxWindow* w = m_tabs.GetWindowFromIdx(i);
1796 if (wnd != w)
1797 {
1798 new_active = m_tabs.GetWindowFromIdx(i);
1799 break;
1800 }
1801 }
1802 }
1803
1804 // remove the tab from main catalog
1805 if (!m_tabs.RemovePage(wnd))
1806 return false;
1807
1808 // remove the tab from the onscreen tab ctrl
1809 ctrl->RemovePage(wnd);
1810
1811 // actually destroy the window now
1812 if (wnd->IsKindOf(CLASSINFO(wxAuiMDIChildFrame)))
1813 {
1814 // delete the child frame with pending delete, as is
1815 // customary with frame windows
1816 if (!wxPendingDelete.Member(wnd))
1817 wxPendingDelete.Append(wnd);
1818 }
1819 else
1820 {
1821 wnd->Destroy();
1822 }
1823
1824 RemoveEmptyTabFrames();
1825
1826 // set new active pane
1827 if (new_active)
1828 {
1829 m_curpage = -1;
1830 SetSelection(m_tabs.GetIdxFromWindow(new_active));
1831 }
1832
1833 return true;
1834 */
1835 }
1836
1837
1838
1839 // RemovePage() removes a tab from the multi-notebook,
1840 // but does not destroy the window
1841 bool wxAuiNotebook::RemovePage(size_t page_idx)
1842 {
1843 wxWindow* wnd = m_tabs.GetWindowFromIdx(page_idx);
1844 wxWindow* new_active = NULL;
1845
1846 // find out which onscreen tab ctrl owns this tab
1847 wxAuiTabCtrl* ctrl;
1848 int ctrl_idx;
1849 if (!FindTab(wnd, &ctrl, &ctrl_idx))
1850 return false;
1851
1852 // find a new page and set it as active
1853 int new_idx = ctrl_idx+1;
1854 if (new_idx >= (int)ctrl->GetPageCount())
1855 new_idx = ctrl_idx-1;
1856
1857 if (new_idx >= 0 && new_idx < (int)ctrl->GetPageCount())
1858 {
1859 new_active = ctrl->GetWindowFromIdx(new_idx);
1860 }
1861 else
1862 {
1863 // set the active page to the first page that
1864 // isn't the one being deleted
1865 size_t i, page_count = m_tabs.GetPageCount();
1866 for (i = 0; i < page_count; ++i)
1867 {
1868 wxWindow* w = m_tabs.GetWindowFromIdx(i);
1869 if (wnd != w)
1870 {
1871 new_active = m_tabs.GetWindowFromIdx(i);
1872 break;
1873 }
1874 }
1875 }
1876
1877 // remove the tab from main catalog
1878 if (!m_tabs.RemovePage(wnd))
1879 return false;
1880
1881 // remove the tab from the onscreen tab ctrl
1882 ctrl->RemovePage(wnd);
1883
1884
1885 RemoveEmptyTabFrames();
1886
1887 // set new active pane
1888 if (new_active)
1889 {
1890 m_curpage = -1;
1891 SetSelection(m_tabs.GetIdxFromWindow(new_active));
1892 }
1893
1894 return true;
1895 }
1896
1897 // SetPageText() changes the tab caption of the specified page
1898 bool wxAuiNotebook::SetPageText(size_t page_idx, const wxString& text)
1899 {
1900 if (page_idx >= m_tabs.GetPageCount())
1901 return false;
1902
1903 // update our own tab catalog
1904 wxAuiNotebookPage& page_info = m_tabs.GetPage(page_idx);
1905 page_info.caption = text;
1906
1907 // update what's on screen
1908 wxAuiTabCtrl* ctrl;
1909 int ctrl_idx;
1910 if (FindTab(page_info.window, &ctrl, &ctrl_idx))
1911 {
1912 wxAuiNotebookPage& info = ctrl->GetPage(ctrl_idx);
1913 info.caption = text;
1914 ctrl->Refresh();
1915 ctrl->Update();
1916 }
1917
1918 return true;
1919 }
1920
1921 // GetSelection() returns the index of the currently active page
1922 int wxAuiNotebook::GetSelection() const
1923 {
1924 return m_curpage;
1925 }
1926
1927 // SetSelection() sets the currently active page
1928 size_t wxAuiNotebook::SetSelection(size_t new_page)
1929 {
1930 wxWindow* wnd = m_tabs.GetWindowFromIdx(new_page);
1931 if (!wnd)
1932 return m_curpage;
1933
1934 wxAuiNotebookEvent evt(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, m_windowId);
1935 evt.SetSelection(new_page);
1936 evt.SetOldSelection(m_curpage);
1937 evt.SetEventObject(this);
1938 if (!GetEventHandler()->ProcessEvent(evt) || evt.IsAllowed())
1939 {
1940 // program allows the page change
1941 evt.SetEventType(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED);
1942 (void)GetEventHandler()->ProcessEvent(evt);
1943
1944
1945
1946 wxAuiTabCtrl* ctrl;
1947 int ctrl_idx;
1948 if (FindTab(wnd, &ctrl, &ctrl_idx))
1949 {
1950 m_tabs.SetActivePage(wnd);
1951
1952 ctrl->SetActivePage(ctrl_idx);
1953 DoSizing();
1954 ctrl->DoShowHide();
1955
1956 int old_curpage = m_curpage;
1957 m_curpage = new_page;
1958
1959
1960 // set fonts
1961 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
1962 size_t i, pane_count = all_panes.GetCount();
1963 for (i = 0; i < pane_count; ++i)
1964 {
1965 wxAuiPaneInfo& pane = all_panes.Item(i);
1966 if (pane.name == wxT("dummy"))
1967 continue;
1968 wxAuiTabCtrl* tabctrl = ((wxTabFrame*)pane.window)->m_tabs;
1969 if (tabctrl != ctrl)
1970 tabctrl->SetSelectedFont(m_normal_font);
1971 else
1972 tabctrl->SetSelectedFont(m_selected_font);
1973 tabctrl->Refresh();
1974 }
1975
1976 wnd->SetFocus();
1977
1978 return old_curpage;
1979 }
1980 }
1981
1982 return m_curpage;
1983 }
1984
1985 // GetPageCount() returns the total number of
1986 // pages managed by the multi-notebook
1987 size_t wxAuiNotebook::GetPageCount() const
1988 {
1989 return m_tabs.GetPageCount();
1990 }
1991
1992 // GetPage() returns the wxWindow pointer of the
1993 // specified page
1994 wxWindow* wxAuiNotebook::GetPage(size_t page_idx) const
1995 {
1996 wxASSERT(page_idx < m_tabs.GetPageCount());
1997
1998 return m_tabs.GetWindowFromIdx(page_idx);
1999 }
2000
2001 // DoSizing() performs all sizing operations in each tab control
2002 void wxAuiNotebook::DoSizing()
2003 {
2004 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2005 size_t i, pane_count = all_panes.GetCount();
2006 for (i = 0; i < pane_count; ++i)
2007 {
2008 if (all_panes.Item(i).name == wxT("dummy"))
2009 continue;
2010
2011 wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2012 tabframe->DoSizing();
2013 }
2014 }
2015
2016 // GetActiveTabCtrl() returns the active tab control. It is
2017 // called to determine which control gets new windows being added
2018 wxAuiTabCtrl* wxAuiNotebook::GetActiveTabCtrl()
2019 {
2020 if (m_curpage >= 0 && m_curpage < (int)m_tabs.GetPageCount())
2021 {
2022 wxAuiTabCtrl* ctrl;
2023 int idx;
2024
2025 // find the tab ctrl with the current page
2026 if (FindTab(m_tabs.GetPage(m_curpage).window,
2027 &ctrl, &idx))
2028 {
2029 return ctrl;
2030 }
2031 }
2032
2033 // no current page, just find the first tab ctrl
2034 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2035 size_t i, pane_count = all_panes.GetCount();
2036 for (i = 0; i < pane_count; ++i)
2037 {
2038 if (all_panes.Item(i).name == wxT("dummy"))
2039 continue;
2040
2041 wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2042 return tabframe->m_tabs;
2043 }
2044
2045 // If there is no tabframe at all, create one
2046 wxTabFrame* tabframe = new wxTabFrame;
2047 tabframe->SetTabCtrlHeight(m_tab_ctrl_height);
2048 tabframe->m_tabs = new wxAuiTabCtrl(this,
2049 m_tab_id_counter++,
2050 wxDefaultPosition,
2051 wxDefaultSize,
2052 wxNO_BORDER);
2053 tabframe->m_tabs->SetFlags(m_flags);
2054 m_mgr.AddPane(tabframe,
2055 wxAuiPaneInfo().Center().CaptionVisible(false));
2056
2057 m_mgr.Update();
2058
2059 return tabframe->m_tabs;
2060 }
2061
2062 // FindTab() finds the tab control that currently contains the window as well
2063 // as the index of the window in the tab control. It returns true if the
2064 // window was found, otherwise false.
2065 bool wxAuiNotebook::FindTab(wxWindow* page, wxAuiTabCtrl** ctrl, int* idx)
2066 {
2067 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2068 size_t i, pane_count = all_panes.GetCount();
2069 for (i = 0; i < pane_count; ++i)
2070 {
2071 if (all_panes.Item(i).name == wxT("dummy"))
2072 continue;
2073
2074 wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2075
2076 int page_idx = tabframe->m_tabs->GetIdxFromWindow(page);
2077 if (page_idx != -1)
2078 {
2079 *ctrl = tabframe->m_tabs;
2080 *idx = page_idx;
2081 return true;
2082 }
2083 }
2084
2085 return false;
2086 }
2087
2088
2089 void wxAuiNotebook::OnEraseBackground(wxEraseEvent&)
2090 {
2091 }
2092
2093 void wxAuiNotebook::OnSize(wxSizeEvent&)
2094 {
2095 }
2096
2097 void wxAuiNotebook::OnTabClicked(wxCommandEvent& command_evt)
2098 {
2099 wxAuiNotebookEvent& evt = (wxAuiNotebookEvent&)command_evt;
2100
2101 wxAuiTabCtrl* ctrl = (wxAuiTabCtrl*)evt.GetEventObject();
2102 wxASSERT(ctrl != NULL);
2103
2104 wxWindow* wnd = ctrl->GetWindowFromIdx(evt.GetSelection());
2105 wxASSERT(wnd != NULL);
2106
2107 int idx = m_tabs.GetIdxFromWindow(wnd);
2108 wxASSERT(idx != -1);
2109
2110 SetSelection(idx);
2111 }
2112
2113 void wxAuiNotebook::OnTabBeginDrag(wxCommandEvent&)
2114 {
2115 m_last_drag_x = 0;
2116 }
2117
2118 void wxAuiNotebook::OnTabDragMotion(wxCommandEvent& evt)
2119 {
2120 wxPoint screen_pt = ::wxGetMousePosition();
2121 wxPoint client_pt = ScreenToClient(screen_pt);
2122 wxPoint zero(0,0);
2123
2124 wxAuiTabCtrl* src_tabs = (wxAuiTabCtrl*)evt.GetEventObject();
2125 wxAuiTabCtrl* dest_tabs = GetTabCtrlFromPoint(client_pt);
2126
2127 if (dest_tabs == src_tabs)
2128 {
2129 if (src_tabs)
2130 {
2131 src_tabs->SetCursor(wxCursor(wxCURSOR_ARROW));
2132 }
2133
2134 // always hide the hint for inner-tabctrl drag
2135 m_mgr.HideHint();
2136
2137 // if tab moving is not allowed, leave
2138 if (!(m_flags & wxAUI_NB_TAB_MOVE))
2139 {
2140 return;
2141 }
2142
2143 wxPoint pt = dest_tabs->ScreenToClient(screen_pt);
2144 wxWindow* dest_location_tab;
2145
2146 // this is an inner-tab drag/reposition
2147 if (dest_tabs->TabHitTest(pt.x, pt.y, &dest_location_tab))
2148 {
2149 int src_idx = evt.GetSelection();
2150 int dest_idx = dest_tabs->GetIdxFromWindow(dest_location_tab);
2151
2152 // prevent jumpy drag
2153 if ((src_idx == dest_idx) || dest_idx == -1 ||
2154 (src_idx > dest_idx && m_last_drag_x <= pt.x) ||
2155 (src_idx < dest_idx && m_last_drag_x >= pt.x))
2156 {
2157 m_last_drag_x = pt.x;
2158 return;
2159 }
2160
2161
2162 wxWindow* src_tab = dest_tabs->GetWindowFromIdx(src_idx);
2163 dest_tabs->MovePage(src_tab, dest_idx);
2164 dest_tabs->SetActivePage((size_t)dest_idx);
2165 dest_tabs->DoShowHide();
2166 dest_tabs->Refresh();
2167 m_last_drag_x = pt.x;
2168
2169 }
2170
2171 return;
2172 }
2173
2174
2175 // if external drag is allowed, check if the tab is being dragged
2176 // over a different wxAuiNotebook control
2177 if (m_flags & wxAUI_NB_TAB_EXTERNAL_MOVE)
2178 {
2179 wxWindow* tab_ctrl = ::wxFindWindowAtPoint(screen_pt);
2180
2181 // if we are over a hint window, leave
2182 if (tab_ctrl->IsKindOf(CLASSINFO(wxFrame)))
2183 return;
2184
2185 while (tab_ctrl)
2186 {
2187 if (tab_ctrl->IsKindOf(CLASSINFO(wxAuiTabCtrl)))
2188 break;
2189 tab_ctrl = tab_ctrl->GetParent();
2190 }
2191
2192 if (tab_ctrl)
2193 {
2194 wxAuiNotebook* nb = (wxAuiNotebook*)tab_ctrl->GetParent();
2195
2196 if (nb != this)
2197 {
2198 wxRect hint_rect = tab_ctrl->GetRect();
2199 tab_ctrl->ClientToScreen(&hint_rect.x, &hint_rect.y);
2200 m_mgr.ShowHint(hint_rect);
2201 return;
2202 }
2203
2204 }
2205 }
2206
2207
2208 // if tab moving is not allowed, leave
2209 if (!(m_flags & wxAUI_NB_TAB_SPLIT))
2210 {
2211 return;
2212 }
2213
2214
2215 if (src_tabs)
2216 {
2217 src_tabs->SetCursor(wxCursor(wxCURSOR_SIZING));
2218 }
2219
2220
2221 if (dest_tabs)
2222 {
2223 wxRect hint_rect = dest_tabs->GetRect();
2224 ClientToScreen(&hint_rect.x, &hint_rect.y);
2225 m_mgr.ShowHint(hint_rect);
2226 }
2227 else
2228 {
2229 m_mgr.DrawHintRect(m_dummy_wnd, client_pt, zero);
2230 }
2231 }
2232
2233
2234
2235 void wxAuiNotebook::OnTabEndDrag(wxCommandEvent& command_evt)
2236 {
2237 wxAuiNotebookEvent& evt = (wxAuiNotebookEvent&)command_evt;
2238
2239 m_mgr.HideHint();
2240
2241
2242 wxAuiTabCtrl* src_tabs = (wxAuiTabCtrl*)evt.GetEventObject();
2243 wxAuiTabCtrl* dest_tabs = NULL;
2244 if (src_tabs)
2245 {
2246 // set cursor back to an arrow
2247 src_tabs->SetCursor(wxCursor(wxCURSOR_ARROW));
2248 }
2249
2250 // get the mouse position, which will be used to determine the drop point
2251 wxPoint mouse_screen_pt = ::wxGetMousePosition();
2252 wxPoint mouse_client_pt = ScreenToClient(mouse_screen_pt);
2253
2254
2255
2256 // check for an external move
2257 if (m_flags & wxAUI_NB_TAB_EXTERNAL_MOVE)
2258 {
2259 wxWindow* tab_ctrl = ::wxFindWindowAtPoint(mouse_screen_pt);
2260
2261 while (tab_ctrl)
2262 {
2263 if (tab_ctrl->IsKindOf(CLASSINFO(wxAuiTabCtrl)))
2264 break;
2265 tab_ctrl = tab_ctrl->GetParent();
2266 }
2267
2268 if (tab_ctrl)
2269 {
2270 wxAuiNotebook* nb = (wxAuiNotebook*)tab_ctrl->GetParent();
2271
2272 if (nb != this)
2273 {
2274 // find out from the destination control
2275 // if it's ok to drop this tab here
2276 wxAuiNotebookEvent e(wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, m_windowId);
2277 e.SetSelection(evt.GetSelection());
2278 e.SetOldSelection(evt.GetSelection());
2279 e.SetEventObject(this);
2280 e.SetDragSource(this);
2281 e.Veto(); // dropping must be explicitly approved by control owner
2282
2283 nb->GetEventHandler()->ProcessEvent(e);
2284
2285 if (!e.IsAllowed())
2286 {
2287 // no answer or negative answer
2288 m_mgr.HideHint();
2289 return;
2290 }
2291
2292 // drop was allowed
2293 int src_idx = evt.GetSelection();
2294 wxWindow* src_page = src_tabs->GetWindowFromIdx(src_idx);
2295
2296 // get main index of the page
2297 int main_idx = m_tabs.GetIdxFromWindow(src_page);
2298
2299 // make a copy of the page info
2300 wxAuiNotebookPage page_info = m_tabs.GetPage((size_t)main_idx);
2301
2302 // remove the page from the source notebook
2303 RemovePage(main_idx);
2304
2305 // reparent the page
2306 src_page->Reparent(nb);
2307
2308
2309 // found out the insert idx
2310 wxAuiTabCtrl* dest_tabs = (wxAuiTabCtrl*)tab_ctrl;
2311 wxPoint pt = dest_tabs->ScreenToClient(mouse_screen_pt);
2312
2313 wxWindow* target = NULL;
2314 int insert_idx = -1;
2315 dest_tabs->TabHitTest(pt.x, pt.y, &target);
2316 if (target)
2317 {
2318 insert_idx = dest_tabs->GetIdxFromWindow(target);
2319 }
2320
2321
2322 // add the page to the new notebook
2323 if (insert_idx == -1)
2324 insert_idx = dest_tabs->GetPageCount();
2325 dest_tabs->InsertPage(page_info.window, page_info, insert_idx);
2326 nb->m_tabs.AddPage(page_info.window, page_info);
2327
2328 nb->DoSizing();
2329 dest_tabs->DoShowHide();
2330 dest_tabs->Refresh();
2331
2332 // set the selection in the destination tab control
2333 nb->SetSelection(nb->m_tabs.GetIdxFromWindow(page_info.window));
2334
2335 return;
2336 }
2337 }
2338 }
2339
2340
2341
2342
2343 // only perform a tab split if it's allowed
2344 if (m_flags & wxAUI_NB_TAB_SPLIT)
2345 {
2346 // If the pointer is in an existing tab frame, do a tab insert
2347 wxWindow* hit_wnd = ::wxFindWindowAtPoint(mouse_screen_pt);
2348 wxTabFrame* tab_frame = (wxTabFrame*)GetTabFrameFromTabCtrl(hit_wnd);
2349 int insert_idx = -1;
2350 if (tab_frame)
2351 {
2352 dest_tabs = tab_frame->m_tabs;
2353
2354 if (dest_tabs == src_tabs)
2355 return;
2356
2357
2358 wxPoint pt = dest_tabs->ScreenToClient(mouse_screen_pt);
2359 wxWindow* target = NULL;
2360 dest_tabs->TabHitTest(pt.x, pt.y, &target);
2361 if (target)
2362 {
2363 insert_idx = dest_tabs->GetIdxFromWindow(target);
2364 }
2365 }
2366 else
2367 {
2368 // If there is no tabframe at all, create one
2369 wxTabFrame* new_tabs = new wxTabFrame;
2370 new_tabs->SetTabCtrlHeight(m_tab_ctrl_height);
2371 new_tabs->m_tabs = new wxAuiTabCtrl(this,
2372 m_tab_id_counter++,
2373 wxDefaultPosition,
2374 wxDefaultSize,
2375 wxNO_BORDER);
2376 new_tabs->m_tabs->SetFlags(m_flags);
2377
2378 m_mgr.AddPane(new_tabs,
2379 wxAuiPaneInfo().Bottom().CaptionVisible(false),
2380 mouse_client_pt);
2381 m_mgr.Update();
2382 dest_tabs = new_tabs->m_tabs;
2383 }
2384
2385
2386
2387 // remove the page from the source tabs
2388 wxAuiNotebookPage page_info = src_tabs->GetPage(evt.GetSelection());
2389 page_info.active = false;
2390 src_tabs->RemovePage(page_info.window);
2391 if (src_tabs->GetPageCount() > 0)
2392 {
2393 src_tabs->SetActivePage((size_t)0);
2394 src_tabs->DoShowHide();
2395 src_tabs->Refresh();
2396 }
2397
2398
2399
2400 // add the page to the destination tabs
2401 if (insert_idx == -1)
2402 insert_idx = dest_tabs->GetPageCount();
2403 dest_tabs->InsertPage(page_info.window, page_info, insert_idx);
2404
2405 if (src_tabs->GetPageCount() == 0)
2406 {
2407 RemoveEmptyTabFrames();
2408 }
2409
2410 DoSizing();
2411 dest_tabs->DoShowHide();
2412 dest_tabs->Refresh();
2413
2414 SetSelection(m_tabs.GetIdxFromWindow(page_info.window));
2415 }
2416 }
2417
2418
2419
2420 wxAuiTabCtrl* wxAuiNotebook::GetTabCtrlFromPoint(const wxPoint& pt)
2421 {
2422 // if we've just removed the last tab from the source
2423 // tab set, the remove the tab control completely
2424 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2425 size_t i, pane_count = all_panes.GetCount();
2426 for (i = 0; i < pane_count; ++i)
2427 {
2428 if (all_panes.Item(i).name == wxT("dummy"))
2429 continue;
2430
2431 wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2432 if (tabframe->m_tab_rect.Contains(pt))
2433 return tabframe->m_tabs;
2434 }
2435
2436 return NULL;
2437 }
2438
2439 wxWindow* wxAuiNotebook::GetTabFrameFromTabCtrl(wxWindow* tab_ctrl)
2440 {
2441 // if we've just removed the last tab from the source
2442 // tab set, the remove the tab control completely
2443 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
2444 size_t i, pane_count = all_panes.GetCount();
2445 for (i = 0; i < pane_count; ++i)
2446 {
2447 if (all_panes.Item(i).name == wxT("dummy"))
2448 continue;
2449
2450 wxTabFrame* tabframe = (wxTabFrame*)all_panes.Item(i).window;
2451 if (tabframe->m_tabs == tab_ctrl)
2452 {
2453 return tabframe;
2454 }
2455 }
2456
2457 return NULL;
2458 }
2459
2460 void wxAuiNotebook::RemoveEmptyTabFrames()
2461 {
2462 // if we've just removed the last tab from the source
2463 // tab set, the remove the tab control completely
2464 wxAuiPaneInfoArray all_panes = m_mgr.GetAllPanes();
2465 size_t i, pane_count = all_panes.GetCount();
2466 for (i = 0; i < pane_count; ++i)
2467 {
2468 if (all_panes.Item(i).name == wxT("dummy"))
2469 continue;
2470
2471 wxTabFrame* tab_frame = (wxTabFrame*)all_panes.Item(i).window;
2472 if (tab_frame->m_tabs->GetPageCount() == 0)
2473 {
2474 m_mgr.DetachPane(tab_frame);
2475
2476 // use pending delete because sometimes during
2477 // window closing, refreshs are pending
2478 if (!wxPendingDelete.Member(tab_frame->m_tabs))
2479 wxPendingDelete.Append(tab_frame->m_tabs);
2480 //tab_frame->m_tabs->Destroy();
2481
2482 delete tab_frame;
2483 }
2484 }
2485
2486
2487 // check to see if there is still a center pane;
2488 // if there isn't, make a frame the center pane
2489 wxAuiPaneInfoArray panes = m_mgr.GetAllPanes();
2490 pane_count = panes.GetCount();
2491 wxWindow* first_good = NULL;
2492 bool center_found = false;
2493 for (i = 0; i < pane_count; ++i)
2494 {
2495 if (panes.Item(i).name == wxT("dummy"))
2496 continue;
2497 if (panes.Item(i).dock_direction == wxAUI_DOCK_CENTRE)
2498 center_found = true;
2499 if (!first_good)
2500 first_good = panes.Item(i).window;
2501 }
2502
2503 if (!center_found && first_good)
2504 {
2505 m_mgr.GetPane(first_good).Centre();
2506 }
2507
2508 m_mgr.Update();
2509 }
2510
2511 void wxAuiNotebook::OnChildFocus(wxChildFocusEvent& evt)
2512 {
2513 int idx = m_tabs.GetIdxFromWindow(evt.GetWindow());
2514 if (idx != -1 && idx != m_curpage)
2515 {
2516 SetSelection(idx);
2517 }
2518 }
2519
2520
2521 void wxAuiNotebook::OnTabButton(wxCommandEvent& command_evt)
2522 {
2523 wxAuiNotebookEvent& evt = (wxAuiNotebookEvent&)command_evt;
2524 wxAuiTabCtrl* tabs = (wxAuiTabCtrl*)evt.GetEventObject();
2525
2526 int button_id = evt.GetInt();
2527
2528 if (button_id == wxAUI_BUTTON_CLOSE)
2529 {
2530 int selection = tabs->GetActivePage();
2531
2532 if (selection != -1)
2533 {
2534 wxWindow* close_wnd = tabs->GetWindowFromIdx(selection);
2535
2536 if (close_wnd->IsKindOf(CLASSINFO(wxAuiMDIChildFrame)))
2537 {
2538 close_wnd->Close();
2539 }
2540 else
2541 {
2542 int main_idx = m_tabs.GetIdxFromWindow(close_wnd);
2543 DeletePage(main_idx);
2544 }
2545 }
2546 }
2547 }
2548
2549
2550
2551
2552 #endif // wxUSE_AUI