1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3 * http://www.gnu.org/licenses/lgpl-3.0.html
4 *
5 * $Revision: 11431 $
6 * $Id: cbauibook.cpp 11431 2018-08-04 05:40:37Z ollydbg $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/cbauibook.cpp $
8 */
9
10
11 #include "sdk_precomp.h"
12
13 #ifndef CB_PRECOMP
14 #include "cbauibook.h"
15 #include "cbeditor.h"
16 #include "cbproject.h"
17 #include "configmanager.h"
18 #include "editormanager.h"
19 #include "manager.h"
20 #include "projectmanager.h"
21
22 #include <wx/app.h>
23 #include <wx/dcclient.h>
24 #include <wx/regex.h>
25 #endif
26
27 #include <wx/tooltip.h>
28 #include <wx/wupdlock.h>
29
30 // static
31 bool cbAuiNotebook::s_AllowMousewheel = true;
32 cbAuiNotebookArray cbAuiNotebook::s_cbAuiNotebookArray;
33 wxString cbAuiNotebook::s_modKeys = _T("Ctrl");
34 bool cbAuiNotebook::s_modToAdvance = false;
35 int cbAuiNotebook::s_advanceDirection = 1;
36 int cbAuiNotebook::s_moveDirection = 1;
37
38
BEGIN_EVENT_TABLE(cbAuiNotebook,wxAuiNotebook)39 BEGIN_EVENT_TABLE(cbAuiNotebook, wxAuiNotebook)
40 #if wxCHECK_VERSION(3, 0, 0)
41 EVT_NAVIGATION_KEY(cbAuiNotebook::OnNavigationKeyNotebook)
42 #else
43 EVT_NAVIGATION_KEY(cbAuiNotebook::OnNavigationKey)
44 #endif
45 EVT_IDLE(cbAuiNotebook::OnIdle)
46 EVT_AUINOTEBOOK_DRAG_DONE(wxID_ANY, cbAuiNotebook::OnDragDone)
47 END_EVENT_TABLE()
48
49 cbAuiNotebook::cbAuiNotebook(wxWindow* pParent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
50 : wxAuiNotebook(pParent, id, pos, size, style),
51 #ifdef __WXMSW__
52 m_LastSelected(wxNOT_FOUND),
53 m_LastId(0),
54 #endif
55 #if !wxCHECK_VERSION(3, 0, 0)
56 m_HasToolTip(false),
57 #endif
58 m_SetZoomOnIdle(false),
59 m_MinimizeFreeSpaceOnIdle(false),
60 m_TabCtrlSize(wxDefaultSize)
61 {
62 //ctor
63 #ifdef __WXGTK__
64 m_mgr.SetFlags((m_mgr.GetFlags() | wxAUI_MGR_VENETIAN_BLINDS_HINT) & ~wxAUI_MGR_TRANSPARENT_HINT);
65 #endif // #ifdef __WXGTK__
66 ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
67 #if defined __WXMSW__ && wxCHECK_VERSION(3, 0, 0)
68 wxToolTip::SetMaxWidth(-1);
69 #endif
70 s_AllowMousewheel = cfg->ReadBool(_T("/environment/tabs_use_mousewheel"),true);
71 s_modKeys = cfg->Read(_T("/environment/tabs_mousewheel_modifier"),_T("Ctrl"));
72 s_modToAdvance = cfg->ReadBool(_T("/environment/tabs_mousewheel_advance"),false);
73 cbAuiNotebook::InvertAdvanceDirection(cfg->ReadBool(_T("/environment/tabs_invert_advance"),false));
74 cbAuiNotebook::InvertMoveDirection(cfg->ReadBool(_T("/environment/tabs_invert_move"),false));
75
76 if (s_cbAuiNotebookArray.Index(this) == wxNOT_FOUND)
77 s_cbAuiNotebookArray.Add(this);
78 }
79
~cbAuiNotebook()80 cbAuiNotebook::~cbAuiNotebook()
81 {
82 s_cbAuiNotebookArray.Remove(this);
83 }
84
CheckKeyModifier()85 bool cbAuiNotebook::CheckKeyModifier()
86 {
87 bool result = true;
88 // this search must be case-insensitive
89 wxString str = s_modKeys;
90 str.MakeUpper();
91
92 if (result && str.Contains(wxT("ALT")))
93 result = wxGetKeyState(WXK_ALT);
94 if (result && str.Contains(wxT("CTRL")))
95 result = wxGetKeyState(WXK_CONTROL);
96 #if defined(__WXMAC__) || defined(__WXCOCOA__)
97 if (result && str.Contains(wxT("XCTRL")))
98 result = wxGetKeyState(WXK_COMMAND);
99 #endif
100 if (result && str.Contains(wxT("SHIFT")))
101 result = wxGetKeyState(WXK_SHIFT);
102 return result;
103 }
104
UpdateTabControlsArray()105 void cbAuiNotebook::UpdateTabControlsArray()
106 {
107 cbAuiTabCtrlArray saveTabCtrls = m_TabCtrls;
108 m_TabCtrls.Clear();
109 // first get all tab-controls
110 const size_t tab_Count = GetPageCount();
111 for (size_t i = 0; i < tab_Count; ++i)
112 {
113 wxAuiTabCtrl* tabCtrl = nullptr;
114 int idx = -1;
115 if (FindTab(GetPage(i), &tabCtrl, &idx))
116 {
117 if (tabCtrl && m_TabCtrls.Index(tabCtrl) == wxNOT_FOUND)
118 m_TabCtrls.Add(tabCtrl);
119 }
120 else
121 continue;
122 }
123 bool needEventRebind = m_TabCtrls.GetCount() != saveTabCtrls.GetCount();
124 if (!needEventRebind)
125 {
126 for (size_t i = 0; i < m_TabCtrls.GetCount(); ++i)
127 {
128 if (saveTabCtrls.Index(m_TabCtrls[i]) == wxNOT_FOUND)
129 {
130 needEventRebind = true;
131 break;
132 }
133 }
134 }
135 if (needEventRebind)
136 {
137 ResetTabCtrlEvents();
138 }
139 // make sure the active page is valid and shown
140 for (size_t i = 0; i < m_TabCtrls.GetCount(); ++i)
141 {
142 int pageCount = m_TabCtrls[i]->GetPageCount();
143 if (m_TabCtrls[i]->GetActivePage() < 0 && pageCount > 0)
144 m_TabCtrls[i]->SetActivePage((size_t)0);
145 if (m_TabCtrls[i]->GetActivePage() >= pageCount && pageCount > 0)
146 m_TabCtrls[i]->SetActivePage(pageCount - 1);
147 m_TabCtrls[i]->DoShowHide();
148 }
149 }
150
ResetTabCtrlEvents()151 void cbAuiNotebook::ResetTabCtrlEvents()
152 {
153 for (size_t i = 0; i < m_TabCtrls.GetCount(); ++i)
154 {
155 #if !wxCHECK_VERSION(3, 0, 0)
156 m_TabCtrls[i]->Disconnect(wxEVT_MOTION, wxMouseEventHandler(cbAuiNotebook::OnMotion));
157 m_TabCtrls[i]->Connect(wxEVT_MOTION , wxMouseEventHandler(cbAuiNotebook::OnMotion));
158 #endif
159 m_TabCtrls[i]->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(cbAuiNotebook::OnTabCtrlDblClick));
160 m_TabCtrls[i]->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(cbAuiNotebook::OnTabCtrlDblClick));
161 m_TabCtrls[i]->Disconnect(wxEVT_SIZE, wxSizeEventHandler(cbAuiNotebook::OnResize));
162 m_TabCtrls[i]->Connect(wxEVT_SIZE, wxSizeEventHandler(cbAuiNotebook::OnResize));
163 m_TabCtrls[i]->Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(cbAuiNotebook::OnTabCtrlMouseWheel));
164 #ifdef __WXMSW__
165 m_TabCtrls[i]->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(cbAuiNotebook::OnEnterTabCtrl));
166 m_TabCtrls[i]->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(cbAuiNotebook::OnLeaveTabCtrl));
167 #endif
168 if (GetPageCount() > 1)
169 {
170 if (s_AllowMousewheel)
171 m_TabCtrls[i]->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(cbAuiNotebook::OnTabCtrlMouseWheel));
172 #ifdef __WXMSW__
173 m_TabCtrls[i]->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(cbAuiNotebook::OnEnterTabCtrl));
174 m_TabCtrls[i]->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(cbAuiNotebook::OnLeaveTabCtrl));
175 #endif
176 }
177 }
178 }
179
FocusActiveTabCtrl()180 void cbAuiNotebook::FocusActiveTabCtrl()
181 {
182 UpdateTabControlsArray();
183 int sel = GetSelection();
184 if (sel < 0)
185 return;
186
187 wxWindow* wnd = GetPage(static_cast<size_t>(sel));
188 if (!wnd)
189 return;
190
191 for (size_t i = 0; i < m_TabCtrls.GetCount(); ++i)
192 {
193 wxWindow* win = m_TabCtrls[i]->GetWindowFromIdx(m_TabCtrls[i]->GetActivePage());
194 if (win && (win == wnd))
195 {
196 m_TabCtrls[i]->SetFocus();
197 break;
198 }
199 }
200 }
201
GetTabCtrl(wxWindow * page)202 wxAuiTabCtrl* cbAuiNotebook::GetTabCtrl(wxWindow *page)
203 {
204 if (page)
205 {
206 wxAuiTabCtrl *tabCtrl = nullptr;
207 int idx;
208 if (!FindTab(page, &tabCtrl, &idx))
209 return nullptr;
210 return tabCtrl;
211 }
212 else
213 return GetActiveTabCtrl();
214 }
215
GetPagesInTabCtrl(std::vector<wxWindow * > & result,wxWindow * page)216 void cbAuiNotebook::GetPagesInTabCtrl(std::vector<wxWindow*> &result, wxWindow *page)
217 {
218 result.clear();
219 wxAuiTabCtrl* tabCtrl = GetTabCtrl(page);
220 if (tabCtrl)
221 {
222 int count = tabCtrl->GetPageCount();
223 for (int ii = 0; ii < count; ++ii)
224 result.push_back(tabCtrl->GetPage(ii).window);
225 }
226 }
227
SetZoom(int zoom)228 void cbAuiNotebook::SetZoom(int zoom)
229 {
230 // we only set zoom-factor for active (visible) tabs,
231 // all others are set if system is idle
232 UpdateTabControlsArray();
233 for (size_t i = 0; i < m_TabCtrls.GetCount(); ++i)
234 {
235 wxWindow* win = m_TabCtrls[i]->GetWindowFromIdx(m_TabCtrls[i]->GetActivePage());
236 if (win && static_cast<EditorBase*>(win)->IsBuiltinEditor())
237 static_cast<cbEditor*>(win)->SetZoom(zoom);
238 }
239 m_SetZoomOnIdle = true;
240 }
241
OnIdle(cb_unused wxIdleEvent & event)242 void cbAuiNotebook::OnIdle(cb_unused wxIdleEvent& event)
243 {
244 if (m_SetZoomOnIdle)
245 {
246 m_SetZoomOnIdle = false;
247 int zoom = Manager::Get()->GetEditorManager()->GetZoom();
248 for (size_t i = 0; i < GetPageCount(); ++i)
249 {
250 wxWindow* win = GetPage(i);
251 if (win && static_cast<EditorBase*>(win)->IsBuiltinEditor())
252 static_cast<cbEditor*>(win)->SetZoom(zoom);
253 }
254 }
255
256 if (m_MinimizeFreeSpaceOnIdle)
257 {
258 m_MinimizeFreeSpaceOnIdle = false;
259 UpdateTabControlsArray();
260 for (size_t i = 0; i < m_TabCtrls.GetCount(); ++i)
261 MinimizeFreeSpace(m_TabCtrls[i]);
262 }
263
264 }
265
OnDragDone(cb_unused wxAuiNotebookEvent & event)266 void cbAuiNotebook::OnDragDone(cb_unused wxAuiNotebookEvent& event)
267 {
268 UpdateTabControlsArray();
269 }
270
271 #ifdef __WXMSW__
OnEnterTabCtrl(wxMouseEvent & event)272 void cbAuiNotebook::OnEnterTabCtrl(wxMouseEvent& event)
273 {
274 if (!wxTheApp->IsActive())
275 return;
276
277 wxAuiTabCtrl* tabCtrl = (wxAuiTabCtrl*)event.GetEventObject();
278 if (tabCtrl)
279 {
280 cbAuiNotebook* nb = (cbAuiNotebook*)tabCtrl->GetParent();
281 if (nb)
282 {
283 if ( s_AllowMousewheel
284 && (nb->m_LastSelected == wxNOT_FOUND)
285 && (nb->m_LastId == 0) )
286 {
287 nb->StoreFocus();
288 tabCtrl->SetFocus();
289 }
290 }
291 }
292 }
293
OnLeaveTabCtrl(wxMouseEvent & event)294 void cbAuiNotebook::OnLeaveTabCtrl(wxMouseEvent& event)
295 {
296 if (!wxTheApp->IsActive())
297 return;
298
299 wxAuiTabCtrl* tabCtrl = (wxAuiTabCtrl*)event.GetEventObject();
300 if (tabCtrl)
301 {
302 cbAuiNotebook* nb = (cbAuiNotebook*)tabCtrl->GetParent();
303 if (nb)
304 nb->RestoreFocus();
305 }
306
307 }
308
IsFocusStored(wxWindow * page)309 bool cbAuiNotebook::IsFocusStored(wxWindow* page)
310 {
311 wxWindow* win = FindWindowById(m_LastId);
312 while (win)
313 {
314 if (win == page)
315 return true;
316 win = win->GetParent();
317 }
318 return false;
319 }
320
StoreFocus()321 void cbAuiNotebook::StoreFocus()
322 {
323 // save Id of last focused window and last selected tab
324 wxWindow* win = wxWindow::FindFocus();
325 if (win)
326 m_LastId = win->GetId();
327 else
328 m_LastId = 0;
329 m_LastSelected = GetSelection();
330 }
331
RestoreFocus()332 void cbAuiNotebook::RestoreFocus()
333 {
334 // if selected tab has changed, we set the focus on the window it belongs too
335 if ((m_LastSelected != wxNOT_FOUND) && (GetSelection() != m_LastSelected))
336 {
337 wxWindow* win = GetPage(GetSelection());
338 if (win)
339 win->SetFocus();
340 }
341 // otherwise, we restore the former focus, if the window
342 // with the saved Id still exists
343 else if (m_LastId != 0)
344 {
345 wxWindow* win = FindWindowById(m_LastId);
346 if (win)
347 win->SetFocus();
348 }
349 m_LastSelected = wxNOT_FOUND;
350 m_LastId = 0;
351 }
352 #endif // #ifdef __WXMSW__
353
OnTabCtrlDblClick(wxMouseEvent & event)354 void cbAuiNotebook::OnTabCtrlDblClick(wxMouseEvent& event)
355 {
356 wxWindow* win = nullptr;
357 wxAuiTabCtrl* tabCtrl = (wxAuiTabCtrl*)event.GetEventObject();
358 if (tabCtrl && tabCtrl->TabHitTest(event.GetX(), event.GetY(), &win))
359 {
360 if (win != nullptr)
361 {
362 // send double-click-event
363 CodeBlocksEvent theEvent(cbEVT_CBAUIBOOK_LEFT_DCLICK, GetParent()->GetId());
364 theEvent.SetEventObject(win);
365 GetParent()->GetEventHandler()->ProcessEvent(theEvent);
366 }
367 }
368 }
369
OnTabCtrlMouseWheel(wxMouseEvent & event)370 void cbAuiNotebook::OnTabCtrlMouseWheel(wxMouseEvent& event)
371 {
372 wxAuiTabCtrl* tabCtrl = (wxAuiTabCtrl*)event.GetEventObject();
373 if (!tabCtrl)
374 return;
375 cbAuiNotebook* nb = (cbAuiNotebook*)tabCtrl->GetParent();
376 if (!nb)
377 return;
378
379 nb->SetSelection(nb->GetPageIndex(tabCtrl->GetWindowFromIdx(tabCtrl->GetActivePage())));
380
381 bool modkeys = CheckKeyModifier();
382
383 bool advance = (!s_modToAdvance && !modkeys) || (s_modToAdvance && modkeys);
384
385 if (advance)
386 nb->AdvanceSelection((event.GetWheelRotation() * s_advanceDirection) < 0);
387 else
388 {
389 size_t tabOffset = tabCtrl->GetTabOffset();
390 size_t lastTabIdx = tabCtrl->GetPageCount()-1;
391 wxWindow* win = nb->GetPage(nb->GetSelection());
392 if (win)
393 {
394 wxClientDC dc(win);
395 if ((event.GetWheelRotation() * s_moveDirection) > 0)
396 {
397 if (!tabCtrl->IsTabVisible(lastTabIdx,tabOffset,&dc,win))
398 tabOffset++;
399 }
400 else
401 {
402 if (tabOffset > 0)
403 tabOffset--;
404 }
405 tabCtrl->SetTabOffset(tabOffset);
406 nb->Refresh();
407 }
408 }
409 }
410
OnResize(wxSizeEvent & event)411 void cbAuiNotebook::OnResize(wxSizeEvent& event)
412 {
413 wxAuiTabCtrl* tabCtrl = (wxAuiTabCtrl*)event.GetEventObject();
414 if (tabCtrl)
415 {
416 cbAuiNotebook* nb = (cbAuiNotebook*)tabCtrl->GetParent();
417 if (nb)
418 {
419 if (nb->m_TabCtrlSize != event.GetSize())
420 {
421 nb->m_TabCtrlSize = event.GetSize();
422 nb->MinimizeFreeSpace();
423 }
424 }
425 }
426 event.Skip();
427 }
428
429 #if !wxCHECK_VERSION(3, 0, 0)
OnMotion(wxMouseEvent & event)430 void cbAuiNotebook::OnMotion(wxMouseEvent& event)
431 {
432 event.Skip();
433 wxAuiTabCtrl* tabCtrl = (wxAuiTabCtrl*)event.GetEventObject();
434 if (!tabCtrl)
435 return;
436 cbAuiNotebook* nb = (cbAuiNotebook*)tabCtrl->GetParent();
437 if (!nb || !nb->m_HasToolTip)
438 return;
439
440 wxWindow* win = nullptr;
441 if (event.Moving() && tabCtrl->TabHitTest(event.m_x, event.m_y, &win))
442 {
443 if (!win)
444 {
445 tabCtrl->UnsetToolTip();
446 return;
447 }
448 wxString text(win->GetName());
449 // If the text changes, set it else, keep old, to avoid
450 // 'moving tooltip' effect
451 wxToolTip* tooltip = tabCtrl->GetToolTip();
452 if (!tooltip || tooltip->GetTip() != text)
453 tabCtrl->SetToolTip(text);
454 }
455 else
456 tabCtrl->UnsetToolTip();
457 }
458
SetPageToolTip(size_t idx,const wxString & text)459 bool cbAuiNotebook::SetPageToolTip(size_t idx, const wxString & text)
460 {
461 if (!m_HasToolTip)
462 UpdateTabControlsArray();
463
464 m_HasToolTip = true;
465 wxWindow* win = GetPage(idx);
466 if (win && win->GetName() != text)
467 win->SetName(text);
468 else
469 return false;
470 return true;
471 }
GetPageToolTip(size_t idx)472 wxString cbAuiNotebook::GetPageToolTip(size_t idx)
473 {
474 wxWindow* win = GetPage(idx);
475 if (win)
476 return win->GetName();
477 return wxEmptyString;
478 }
479 #endif
MinimizeFreeSpace()480 void cbAuiNotebook::MinimizeFreeSpace()
481 {
482 if (GetPageCount() < 2)
483 return;
484 m_MinimizeFreeSpaceOnIdle = true;
485 }
486
MinimizeFreeSpace(wxAuiTabCtrl * tabCtrl)487 void cbAuiNotebook::MinimizeFreeSpace(wxAuiTabCtrl* tabCtrl)
488 {
489 if (!tabCtrl || tabCtrl->GetPageCount() < 2 || !IsWindowReallyShown(this))
490 return;
491
492 int ctrl_idx = tabCtrl->GetActivePage();
493 wxWindow* win = GetPage(ctrl_idx);
494 if (win)
495 {
496 int tabOffset = tabCtrl->GetTabOffset();
497
498 wxClientDC dc(win);
499 size_t lastTabIdx = tabCtrl->GetPageCount() - 1;
500
501 if (!tabCtrl->IsTabVisible(ctrl_idx, tabOffset, & dc, win))
502 {
503 for (int i = lastTabIdx ; i >= 0; --i)
504 {
505 if (tabCtrl->IsTabVisible(ctrl_idx, i, & dc, win))
506 {
507 tabOffset = i;
508 break;
509 }
510 }
511 }
512 while (tabOffset > 0 && tabCtrl->IsTabVisible(lastTabIdx, tabOffset-1, & dc, win))
513 --tabOffset;
514
515 tabCtrl->SetTabOffset(tabOffset);
516 }
517 tabCtrl->Refresh();
518 }
519
DeletePage(size_t page)520 bool cbAuiNotebook::DeletePage(size_t page)
521 {
522 #ifdef __WXMSW__
523 if (IsFocusStored(GetPage(page)))
524 {
525 m_LastSelected = wxNOT_FOUND;
526 m_LastId = 0;
527 }
528 #endif // #ifdef __WXMSW__
529 bool result = wxAuiNotebook::DeletePage(page);
530 MinimizeFreeSpace();
531 return result;
532 }
533
RemovePage(size_t page)534 bool cbAuiNotebook::RemovePage(size_t page)
535 {
536 #ifdef __WXMSW__
537 if (IsFocusStored(GetPage(page)))
538 {
539 m_LastSelected = wxNOT_FOUND;
540 m_LastId = 0;
541 }
542 #endif // #ifdef __WXMSW__
543 bool result = wxAuiNotebook::RemovePage(page);
544 MinimizeFreeSpace();
545 return result;
546 }
547
MovePage(wxWindow * page,size_t new_idx)548 bool cbAuiNotebook::MovePage(wxWindow* page, size_t new_idx)
549 {
550 UpdateTabControlsArray();
551 bool result = false;
552 if (m_TabCtrls.GetCount() > 0)
553 {
554 result = m_TabCtrls[0]->MovePage(page, new_idx);
555 Refresh();
556 MinimizeFreeSpace();
557 }
558 return result;
559 }
560
AddPage(wxWindow * page,const wxString & caption,bool select,const wxBitmap & bitmap)561 bool cbAuiNotebook::AddPage(wxWindow* page,
562 const wxString& caption,
563 bool select,
564 const wxBitmap& bitmap)
565 {
566 bool result = wxAuiNotebook::AddPage(page, caption, select, bitmap);
567 MinimizeFreeSpace();
568 return result;
569 }
570
InsertPage(size_t page_idx,wxWindow * page,const wxString & caption,bool select,const wxBitmap & bitmap)571 bool cbAuiNotebook::InsertPage(size_t page_idx,
572 wxWindow* page,
573 const wxString& caption,
574 bool select,
575 const wxBitmap& bitmap)
576 {
577 bool result = wxAuiNotebook::InsertPage(page_idx, page, caption, select, bitmap);
578 MinimizeFreeSpace();
579 return result;
580 }
581
GetTabPositionFromIndex(int index)582 int cbAuiNotebook::GetTabPositionFromIndex(int index)
583 {
584 if (GetPageCount() <= 0)
585 return wxNOT_FOUND;
586
587 UpdateTabControlsArray();
588
589 wxAuiTabCtrl* tabCtrl = nullptr;
590 int idx = -1;
591
592 if (!FindTab(GetPage(index), &tabCtrl, &idx))
593 return wxNOT_FOUND;
594
595 if (!tabCtrl || idx < 0)
596 return wxNOT_FOUND;
597
598 int indexOffset = 0;
599 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
600 const size_t pane_count = all_panes.GetCount();
601 for (size_t i = 0; i < pane_count; ++i)
602 {
603 wxAuiPaneInfo& pane = all_panes[i];
604 if (pane.name == wxT("dummy"))
605 continue;
606
607 if (pane.window == GetTabFrameFromTabCtrl(tabCtrl))
608 break;
609
610 for (size_t j = 0; j < m_TabCtrls.GetCount(); ++j)
611 {
612 if (pane.window == GetTabFrameFromTabCtrl(m_TabCtrls[j]))
613 {
614 indexOffset += m_TabCtrls[j]->GetPageCount();
615 break;
616 }
617 }
618 }
619 return idx + indexOffset;
620 }
621
AdvanceSelection(bool forward)622 void cbAuiNotebook::AdvanceSelection(bool forward)
623 {
624 if (GetPageCount() <= 1)
625 return;
626
627 int currentSelection = GetSelection();
628
629 wxAuiTabCtrl* tabCtrl = nullptr;
630 int idx = -1;
631
632 if (!FindTab(GetPage(currentSelection), &tabCtrl, &idx))
633 return;
634
635 if (!tabCtrl || idx < 0)
636 return;
637
638 wxWindow* page = nullptr;
639 size_t maxPages = tabCtrl->GetPageCount();
640
641 forward?idx++:idx--;
642
643 if (idx < 0)
644 idx = maxPages - 1;
645
646 if ((size_t)idx < maxPages)
647 page = tabCtrl->GetPage(idx).window;
648
649 if (!page && maxPages > 0)
650 page = tabCtrl->GetPage(0).window;
651
652 if (page)
653 {
654 currentSelection = GetPageIndex(page);
655 SetSelection(currentSelection);
656 }
657 }
658
659 #if wxCHECK_VERSION(3, 0, 0)
OnNavigationKeyNotebook(wxNavigationKeyEvent & event)660 void cbAuiNotebook::OnNavigationKeyNotebook(wxNavigationKeyEvent& event)
661 #else
662 void cbAuiNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
663 #endif
664 {
665 // if we change window, we call our own AdvanceSelection
666 if ( event.IsWindowChange() )
667 AdvanceSelection(event.GetDirection());
668 else // otherwise we call the event-handler from the parent-class
669 {
670 #if wxCHECK_VERSION(3, 0, 0)
671 wxAuiNotebook::OnNavigationKeyNotebook(event);
672 #else
673 wxAuiNotebook::OnNavigationKey(event);
674 #endif
675 }
676 }
677
SavePerspective(const wxString projectTitle)678 wxString cbAuiNotebook::SavePerspective(const wxString projectTitle)
679 {
680 // Build list of panes/tabs
681 wxString tabs, tabsTmp;
682 wxArrayString panes;
683
684 // first get all tab-controls
685 UpdateTabControlsArray();
686
687 wxAuiPaneInfoArray& all_panes = m_mgr.GetAllPanes();
688 const size_t pane_count = all_panes.GetCount();
689 for (size_t i = 0; i < pane_count; ++i)
690 {
691 wxAuiPaneInfo& pane = all_panes.Item(i);
692 if (pane.name == wxT("dummy"))
693 continue;
694
695 wxAuiTabCtrl* tabCtrl = nullptr;
696 for (size_t j = 0; j < m_TabCtrls.GetCount(); ++j)
697 {
698 if (pane.window == GetTabFrameFromTabCtrl(m_TabCtrls.Item(j)))
699 {
700 tabCtrl = m_TabCtrls.Item(j);
701 break;
702 }
703 }
704 if (tabCtrl)
705 {
706 tabsTmp.Clear();
707 // add tab id's
708 size_t page_count = tabCtrl->GetPageCount();
709 for (size_t p = 0; p < page_count; ++p)
710 {
711 wxAuiNotebookPage& page = tabCtrl->GetPage(p);
712 const size_t page_idx = m_tabs.GetIdxFromWindow(page.window);
713
714 wxString id = UniqueIdFromTooltip(GetPageToolTip(page_idx));
715
716 // file does not belong to any project, so don't save
717 if (id.BeforeLast(':').empty())
718 continue;
719
720 // if we save a project (projectTitle non empty), but file does not belong to the project
721 // skip it
722 if (!projectTitle.empty() && id.BeforeLast(':') != projectTitle)
723 continue;
724
725 if (!tabsTmp.empty())
726 tabsTmp += wxT(",");
727
728 #if wxCHECK_VERSION(3, 0, 0)
729 if ((int)page_idx == m_curPage)
730 #else
731 if ((int)page_idx == m_curpage)
732 #endif
733 tabsTmp += wxT("*");
734 else if ((int)p == tabCtrl->GetActivePage())
735 tabsTmp += wxT("+");
736
737 tabsTmp += wxString::Format(wxT("%lu"), static_cast<unsigned long>(page_idx));
738 tabsTmp += wxT(";");
739 tabsTmp += id;
740 }
741 if (!tabsTmp.empty())
742 {
743 if (!tabs.empty())
744 tabs += wxT("|");
745
746 panes.Add(pane.name);
747 tabs += pane.name;
748 tabs += wxT("=");
749 tabs += tabsTmp;
750 }
751 }
752 }
753 tabs += wxT("@");
754
755 tabsTmp = m_mgr.SavePerspective();
756
757 wxArrayString arTabsTmp = GetArrayFromString(tabsTmp, wxT("|"));
758
759 for (size_t i = arTabsTmp.GetCount(); i > 0 ; )
760 {
761 if (arTabsTmp.Item(--i).StartsWith(wxT("name=")))
762 {
763 wxString strTmp = arTabsTmp.Item(i).AfterFirst('=').BeforeFirst(';');
764 if (strTmp == wxT("dummy"))
765 continue;
766 if (panes.Index(strTmp) < 0)
767 arTabsTmp.RemoveAt(i);
768 }
769 }
770
771 tabsTmp = GetStringFromArray(arTabsTmp, wxT("|"));
772
773 // Add frame perspective
774 tabs += tabsTmp;
775
776 return tabs;
777 }
778
UniqueIdFromTooltip(const wxString & text)779 wxString cbAuiNotebook::UniqueIdFromTooltip(const wxString& text)
780 {
781 ProjectFile* pf = nullptr;
782 cbProject* prj = nullptr;
783 wxString id = wxT("");
784 wxString fn = text.BeforeFirst(wxT('\n'));
785 prj = Manager::Get()->GetProjectManager()->FindProjectForFile(fn, &pf, false, true);
786 if (prj && pf)
787 id = prj->GetTitle() + wxT(':') + pf->relativeFilename;
788 return id;
789 }
790
GetTabIndexFromTooltip(const wxString & text)791 int cbAuiNotebook::GetTabIndexFromTooltip(const wxString& text)
792 {
793 if (text == wxT(""))
794 return -1;
795 for (size_t i = 0; i < m_tabs.GetPageCount(); ++i)
796 {
797 if (UniqueIdFromTooltip(GetPageToolTip(i)) == text)
798 return i;
799 }
800 return -1;
801 }
802
LoadPerspective(const wxString & layout,bool mergeLayouts)803 bool cbAuiNotebook::LoadPerspective(const wxString& layout, bool mergeLayouts)
804 {
805 if (layout.IsEmpty())
806 return false;
807
808 wxString tabs = layout.BeforeFirst (wxT ('@') );
809 // Remove all tab ctrls (but still keep them in main index)
810 const size_t tab_count = m_tabs.GetPageCount();
811 for (size_t i = 0; i < tab_count; ++i)
812 {
813 // only remove tabs that are in the layout-string, do not touch others,
814 // so the layout of an already loaded workspace or project will not get destroyed, if the
815 // current layout did not know the tab. The layout file which is loaded last takes precedence,
816 // if there are two or more layout strings containing the actual tab.
817 if ( tabs.Find( UniqueIdFromTooltip(GetPageToolTip(i)) ) < 0 )
818 continue;
819
820 wxWindow* wnd = m_tabs.GetWindowFromIdx (i);
821
822 // find out which onscreen tab ctrl owns this tab
823 wxAuiTabCtrl* ctrl;
824 int ctrl_idx;
825 if ( !FindTab(wnd, &ctrl, &ctrl_idx) )
826 return false;
827
828 // remove the tab from ctrl
829 if ( !ctrl->RemovePage(wnd) )
830 return false;
831 }
832 RemoveEmptyTabFrames();
833 wxString currentLayout;
834 if (mergeLayouts)
835 {
836 currentLayout = m_mgr.SavePerspective();
837 wxString tempLayout;
838 while (!currentLayout.empty())
839 {
840 if ( currentLayout.BeforeFirst('|').StartsWith(_("layout2")) ||
841 currentLayout.BeforeFirst('|').StartsWith(_("name=dummy")) )
842 {
843 currentLayout = currentLayout.AfterFirst(('|'));
844 currentLayout.Trim();
845 currentLayout.Trim(true);
846 }
847 else
848 {
849 wxString pane_part = currentLayout.BeforeFirst('|');
850 pane_part.Trim();
851 pane_part.Trim(true);
852 if (!pane_part.empty())
853 tempLayout += pane_part + wxT("|");
854
855 currentLayout = currentLayout.AfterFirst('|');
856 currentLayout.Trim();
857 currentLayout.Trim(true);
858 }
859 }
860 currentLayout = tempLayout;
861 if (currentLayout.empty())
862 mergeLayouts = false;
863 }
864
865 size_t sel_page = 0;
866 int active_tab = 0;
867 bool found = false;
868
869 wxString frames = layout.AfterFirst (wxT ('@') );
870 // if we load an additional project to an exiting layout, the first new tab always goes into a new frame
871 bool firstTabInCtrl =! currentLayout.empty();
872 // This creates a new tabframe if none exists; a workaround, because we can not directly access
873 // the needed wxTabFrame class, because it is not exported.
874 // This also takes care of all needed pane-info
875 wxAuiTabCtrl* dest_tabs = GetActiveTabCtrl();
876 while (1)
877 {
878 const wxString tab_part = tabs.BeforeFirst(wxT('|'));
879
880 // if the string is empty, we're done parsing
881 if (tab_part.empty())
882 break;
883
884 // Get pane name
885 wxString pane_name = tab_part.BeforeFirst(wxT('='));
886
887 // create a new tab frame
888 #if wxCHECK_VERSION(3, 0, 0)
889 m_curPage = -1;
890 #else
891 m_curpage = -1;
892 #endif
893
894 // Get list of tab id's and move them to pane
895 wxString tab_list = tab_part.AfterFirst(wxT('='));
896 while (1)
897 {
898 wxString tab = tab_list.BeforeFirst(wxT(','));
899 wxString name = tab.AfterFirst(wxT(';'));
900 tab = tab.BeforeFirst(wxT(';'));
901
902 if (tab.empty())
903 break;
904 tab_list = tab_list.AfterFirst(wxT(','));
905
906 // Check if this page has an 'active' marker
907 const wxChar c = tab[0];
908 if (c == wxT('+') || c == wxT('*'))
909 tab = tab.Mid(1);
910
911 // Move tab to pane
912 const int index_in_m_tabs = GetTabIndexFromTooltip(name);
913 if (index_in_m_tabs < 0)
914 continue;
915
916 wxAuiNotebookPage& page = m_tabs.GetPage(index_in_m_tabs);
917
918 // save the actual active tab, because it will be set to 0 after a Split()
919 active_tab = dest_tabs->GetActivePage();
920 const size_t newpage_idx = dest_tabs->GetPageCount();
921 dest_tabs->InsertPage(page.window, page, newpage_idx);
922
923 if (c == wxT('+'))
924 dest_tabs->SetActivePage(newpage_idx);
925 else if (c == wxT('*'))
926 sel_page = index_in_m_tabs;
927
928 // If we should be the first tab in a tab-ctrl we switch to the next existing tab,
929 // or create a new one by calling Split() and update the dest_tabs accordingly.
930 if (firstTabInCtrl)
931 {
932 int nextIndex = m_TabCtrls.Index(dest_tabs) + 1;
933 if (nextIndex == 0 || nextIndex >= static_cast<int>(m_TabCtrls.GetCount()))
934 {
935 Split(index_in_m_tabs, wxRIGHT);
936 // reset the active tab, because a Split() set it to zero
937 dest_tabs->SetActivePage(active_tab);
938 dest_tabs = GetActiveTabCtrl();
939 }
940 else
941 dest_tabs = m_TabCtrls.Item(nextIndex);
942 }
943 // Change the pane name to the one we have stored in the layout-string.
944 wxAuiPaneInfo& pane = m_mgr.GetPane( GetTabFrameFromTabCtrl(dest_tabs) );
945 if (pane.name != pane_name)
946 {
947 tab.Replace(pane_name, pane.name);
948 frames.Replace(pane_name, pane.name);
949 pane_name = pane.name;
950 }
951 firstTabInCtrl = false;
952 found = true;
953 }
954 // We come here after at least one tabctrl is filled, so the next tab should go in a new one
955 firstTabInCtrl = true;
956
957 tabs = tabs.AfterFirst(wxT('|'));
958 }
959
960 // Check for windows not readded to the notebook and add the at the end.
961 for (size_t i = 0; i < tab_count; ++i)
962 {
963 wxAuiNotebookPage& page = m_tabs.GetPage(i);
964
965 // find out which onscreen tab ctrl owns this tab
966 // if none then add it to the last used tabctrl
967 wxAuiTabCtrl* ctrl;
968 int ctrl_idx;
969 if ( !FindTab(page.window, &ctrl, &ctrl_idx) )
970 {
971 const size_t newpage_idx = dest_tabs->GetPageCount();
972 dest_tabs->InsertPage (page.window, page, newpage_idx);
973 }
974
975 }
976
977 if (mergeLayouts)
978 {
979 wxRegEx reDockSize(_T("(dock_size[()0-9,]+=)[0-9]+"));
980 const wxString replacement(wxT("\\1-1"));
981 // Make a centered frame left docked
982 frames.Replace(wxString::Format(wxT("dock_size(%d"), wxAUI_DOCK_CENTER), wxString::Format(wxT("dock_size(%d"), wxAUI_DOCK_LEFT));
983 frames.Replace(wxString::Format(wxT("dir=%d"), wxAUI_DOCK_CENTER), wxString::Format(wxT("dir=%d"), wxAUI_DOCK_LEFT));
984 if (reDockSize.Matches(frames))
985 reDockSize.ReplaceAll(&frames,replacement);
986 if (reDockSize.Matches(currentLayout))
987 reDockSize.ReplaceAll(¤tLayout,replacement);
988 while (!currentLayout.empty())
989 {
990 wxString pane_part = currentLayout.BeforeFirst('|');
991 pane_part.Trim();
992 pane_part.Trim(true);
993 if (!pane_part.empty())
994 frames += pane_part + wxT("|");
995 currentLayout = currentLayout.AfterFirst('|');
996 currentLayout.Trim();
997 currentLayout.Trim(true);
998 }
999 }
1000
1001 if (found)
1002 m_mgr.LoadPerspective(frames);
1003 RemoveEmptyTabFrames();
1004
1005 // Force refresh of selection
1006 #if wxCHECK_VERSION(3, 0, 0)
1007 m_curPage = -1;
1008 #else
1009 m_curpage = -1;
1010 #endif
1011 SetSelection(sel_page);
1012
1013 UpdateTabControlsArray();
1014 return true;
1015 }
1016
1017
1018 //bool cbAuiNotebook::LoadPerspective(const wxString& layout) {
1019 // // Remove all tab ctrls (but still keep them in main index)
1020 // const size_t tab_count = m_tabs.GetPageCount();
1021 // for (size_t i = 0; i < tab_count; ++i) {
1022 // wxWindow* wnd = m_tabs.GetWindowFromIdx(i);
1023 //
1024 // // find out which onscreen tab ctrl owns this tab
1025 // wxAuiTabCtrl* ctrl;
1026 // int ctrl_idx;
1027 // if (!FindTab(wnd, &ctrl, &ctrl_idx))
1028 // return false;
1029 //
1030 // // remove the tab from ctrl
1031 // if (!ctrl->RemovePage(wnd))
1032 // return false;
1033 // }
1034 // RemoveEmptyTabFrames();
1035 //
1036 // size_t sel_page = 0;
1037 //
1038 // wxString tabs = layout.BeforeFirst(wxT('@'));
1039 // while (1)
1040 // {
1041 // const wxString tab_part = tabs.BeforeFirst(wxT('|'));
1042 //
1043 // // if the string is empty, we're done parsing
1044 // if (tab_part.empty())
1045 // break;
1046 //
1047 // // Get pane name
1048 // const wxString pane_name = tab_part.BeforeFirst(wxT('='));
1049 //
1050 // // create a new tab frame
1051 // wxTabFrame* new_tabs = new wxTabFrame;
1052 // new_tabs->m_tabs = new wxAuiTabCtrl(this,
1053 // m_tab_id_counter++);
1054 //// wxDefaultPosition,
1055 //// wxDefaultSize,
1056 //// wxNO_BORDER|wxWANTS_CHARS);
1057 // new_tabs->m_tabs->SetArtProvider(m_tabs.GetArtProvider()->Clone());
1058 // new_tabs->SetTabCtrlHeight(m_tab_ctrl_height);
1059 // new_tabs->m_tabs->SetFlags(m_flags);
1060 // wxAuiTabCtrl *dest_tabs = new_tabs->m_tabs;
1061 //
1062 // // create a pane info structure with the information
1063 // // about where the pane should be added
1064 // wxAuiPaneInfo pane_info = wxAuiPaneInfo().Name(pane_name).Bottom().CaptionVisible(false);
1065 // m_mgr.AddPane(new_tabs, pane_info);
1066 //
1067 // // Get list of tab id's and move them to pane
1068 // wxString tab_list = tab_part.AfterFirst(wxT('='));
1069 // while (1)
1070 // {
1071 // wxString tab = tab_list.BeforeFirst(wxT(','));
1072 // if (tab.empty()) break;
1073 // tab_list = tab_list.AfterFirst(wxT(','));
1074 //
1075 // // Check if this page has an 'active' marker
1076 // const wxChar c = tab[0];
1077 // if (c == wxT('+') || c == wxT('*'))
1078 // tab = tab.Mid(1);
1079 //
1080 // const size_t tab_idx = wxAtoi(tab.c_str());
1081 // if (tab_idx >= GetPageCount()) continue;
1082 //
1083 // // Move tab to pane
1084 // wxAuiNotebookPage& page = m_tabs.GetPage(tab_idx);
1085 // const size_t newpage_idx = dest_tabs->GetPageCount();
1086 // dest_tabs->InsertPage(page.window, page, newpage_idx);
1087 //
1088 // if ( c == wxT('+')) dest_tabs->SetActivePage(newpage_idx);
1089 // else if ( c == wxT('*')) sel_page = tab_idx;
1090 // }
1091 // dest_tabs->DoShowHide();
1092 //
1093 // tabs = tabs.AfterFirst(wxT('|'));
1094 // }
1095 //
1096 // // Load the frame perspective
1097 // const wxString frames = layout.AfterFirst(wxT('@'));
1098 // m_mgr.LoadPerspective(frames);
1099 //
1100 // // Force refresh of selection
1101 // m_curpage = -1;
1102 // SetSelection(sel_page);
1103 //
1104 // return true;
1105 //}
1106
1107 // static functions(common to all cbAuiNotebooks)
1108
AllowScrolling(bool allow)1109 void cbAuiNotebook::AllowScrolling(bool allow)
1110 {
1111 s_AllowMousewheel = allow;
1112 for (size_t i = 0; i < s_cbAuiNotebookArray.GetCount(); ++i)
1113 {
1114 s_cbAuiNotebookArray[i]->UpdateTabControlsArray();
1115 s_cbAuiNotebookArray[i]->ResetTabCtrlEvents();
1116 }
1117 }
1118
SetModKeys(wxString keys)1119 void cbAuiNotebook::SetModKeys(wxString keys)
1120 {
1121 s_modKeys = keys;
1122 }
1123
UseModToAdvance(bool use)1124 void cbAuiNotebook::UseModToAdvance(bool use)
1125 {
1126 s_modToAdvance = use;
1127 }
1128
InvertAdvanceDirection(bool invert)1129 void cbAuiNotebook::InvertAdvanceDirection(bool invert)
1130 {
1131 s_advanceDirection=invert ? -1 : 1;
1132 }
1133
InvertMoveDirection(bool invert)1134 void cbAuiNotebook::InvertMoveDirection(bool invert)
1135 {
1136 s_moveDirection=invert ? -1 : 1;
1137 }
1138