"Fossies" - the Fresh Open Source Software Archive

Member "filezilla-3.48.1/src/interface/context_control.cpp" (18 May 2020, 25818 Bytes) of package /linux/misc/FileZilla_3.48.1_src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "context_control.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.48.0_vs_3.48.1.

    1 #include <filezilla.h>
    2 #include "cmdline.h"
    3 #include "commandqueue.h"
    4 #include "context_control.h"
    5 #include "filelist_statusbar.h"
    6 #include "filezillaapp.h"
    7 #include "list_search_panel.h"
    8 #include "local_recursive_operation.h"
    9 #include "LocalListView.h"
   10 #include "LocalTreeView.h"
   11 #include "Mainfrm.h"
   12 #include "Options.h"
   13 #include "queue.h"
   14 #include "remote_recursive_operation.h"
   15 #include "recursive_operation_status.h"
   16 #include "RemoteListView.h"
   17 #include "RemoteTreeView.h"
   18 #include "sitemanager.h"
   19 #include "splitter.h"
   20 #include "view.h"
   21 #include "viewheader.h"
   22 #include "xmlfunctions.h"
   23 
   24 #ifdef USE_MAC_SANDBOX
   25 #include "osx_sandbox_userdirs.h"
   26 #endif
   27 
   28 #include <wx/menu.h>
   29 #include <wx/wupdlock.h>
   30 
   31 #include <array>
   32 
   33 DECLARE_EVENT_TYPE(fzEVT_TAB_CLOSING_DEFERRED, -1)
   34 DEFINE_EVENT_TYPE(fzEVT_TAB_CLOSING_DEFERRED)
   35 
   36 BEGIN_EVENT_TABLE(CContextControl, wxSplitterWindow)
   37 EVT_MENU(XRCID("ID_TABCONTEXT_REFRESH"), CContextControl::OnTabRefresh)
   38 EVT_COMMAND(wxID_ANY, fzEVT_TAB_CLOSING_DEFERRED, CContextControl::OnTabClosing_Deferred)
   39 EVT_MENU(XRCID("ID_TABCONTEXT_CLOSE"), CContextControl::OnTabContextClose)
   40 EVT_MENU(XRCID("ID_TABCONTEXT_CLOSEOTHERS"), CContextControl::OnTabContextCloseOthers)
   41 EVT_MENU(XRCID("ID_TABCONTEXT_NEW"), CContextControl::OnTabContextNew)
   42 END_EVENT_TABLE()
   43 
   44 CContextControl::CContextControl(CMainFrame& mainFrame)
   45     : m_mainFrame(mainFrame)
   46 {
   47     wxASSERT(!CContextManager::Get()->HandlerCount(STATECHANGE_CHANGEDCONTEXT));
   48     CContextManager::Get()->RegisterHandler(this, STATECHANGE_CHANGEDCONTEXT, false);
   49     CContextManager::Get()->RegisterHandler(this, STATECHANGE_SERVER, false);
   50     CContextManager::Get()->RegisterHandler(this, STATECHANGE_REWRITE_CREDENTIALS, false);
   51 }
   52 
   53 CContextControl::~CContextControl()
   54 {
   55 }
   56 
   57 void CContextControl::Create(wxWindow *parent)
   58 {
   59     wxSplitterWindow::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER);
   60 }
   61 
   62 bool CContextControl::CreateTab()
   63 {
   64     CLocalPath localPath;
   65     Site site;
   66     CServerPath remotePath;
   67 
   68     auto const* controls = GetCurrentControls();
   69     if (controls && controls->pState) {
   70         localPath = controls->pState->GetLocalDir();
   71         site = controls->pState->GetLastSite();
   72         remotePath = controls->pState->GetLastServerPath();
   73     }
   74     return CreateTab(localPath, site, remotePath);
   75 }
   76 
   77 bool CContextControl::CreateTab(CLocalPath const& localPath, Site const& site, CServerPath const& remotePath)
   78 {
   79     wxGetApp().AddStartupProfileRecord("CContextControl::CreateTab");
   80 
   81     if (GetTabCount() >= 200) {
   82         wxBell();
   83         return false;
   84     }
   85 
   86     {
   87     #ifdef __WXMSW__
   88         // Some reparenting is being done when creating tabs. Reparenting of frozen windows isn't working
   89         // on OS X.
   90         wxWindowUpdateLocker lock(this);
   91     #endif
   92 
   93         CState* pState = 0;
   94 
   95         // See if we can reuse an existing context
   96         for (size_t i = 0; i < m_context_controls.size(); i++) {
   97             if (m_context_controls[i].used()) {
   98                 continue;
   99             }
  100 
  101             if (m_context_controls[i].pState->IsRemoteConnected() ||
  102                 !m_context_controls[i].pState->IsRemoteIdle())
  103             {
  104                 continue;
  105             }
  106 
  107             pState = m_context_controls[i].pState;
  108             m_context_controls.erase(m_context_controls.begin() + i);
  109             if (m_current_context_controls > (int)i) {
  110                 --m_current_context_controls;
  111             }
  112             break;
  113         }
  114         if (!pState) {
  115             pState = CContextManager::Get()->CreateState(m_mainFrame);
  116             if (!pState) {
  117                 return false;
  118             }
  119         }
  120     
  121         pState->SetLastSite(site, remotePath);
  122 
  123         CreateContextControls(*pState);
  124 
  125 
  126         pState->GetLocalRecursiveOperation()->SetQueue(m_mainFrame.GetQueue());
  127         pState->GetRemoteRecursiveOperation()->SetQueue(m_mainFrame.GetQueue());
  128 
  129         if (localPath.empty() || !pState->SetLocalDir(localPath)) {
  130 #ifdef USE_MAC_SANDBOX
  131             auto const dirs = OSXSandboxUserdirs::Get().GetDirs();
  132             if (dirs.empty() || !pState->SetLocalDir(dirs.front())) {
  133                 pState->SetLocalDir(L"/");
  134             }
  135 #else
  136             std::wstring const homeDir = wxGetHomeDir().ToStdWstring();
  137             if (!pState->SetLocalDir(homeDir)) {
  138                 pState->SetLocalDir(L"/");
  139             }
  140 #endif
  141         }
  142 
  143         CContextManager::Get()->SetCurrentContext(pState);
  144     }
  145 
  146     if (m_tabs) {
  147         m_tabs->SetSelection(m_tabs->GetPageCount() - 1);
  148     }
  149 
  150     return true;
  151 }
  152 
  153 void CContextControl::CreateContextControls(CState& state)
  154 {
  155     wxGetApp().AddStartupProfileRecord("CContextControl::CreateContextControls");
  156     wxWindow* parent = this;
  157 
  158 #ifdef __WXGTK__
  159     // This prevents some ugly flickering on tab creation.
  160     const wxPoint initial_position(1000000, 1000000);
  161 #else
  162     const wxPoint initial_position(wxDefaultPosition);
  163 #endif
  164 
  165     std::tuple<double, int, int> splitterPositions;
  166 
  167     if (!m_context_controls.empty()) {
  168 
  169         splitterPositions = m_context_controls[m_current_context_controls].GetSplitterPositions();
  170         m_context_controls[m_current_context_controls].pLocalListView->SaveColumnSettings(OPTION_LOCALFILELIST_COLUMN_WIDTHS, OPTION_LOCALFILELIST_COLUMN_SHOWN, OPTION_LOCALFILELIST_COLUMN_ORDER);
  171         m_context_controls[m_current_context_controls].pRemoteListView->SaveColumnSettings(OPTION_REMOTEFILELIST_COLUMN_WIDTHS, OPTION_REMOTEFILELIST_COLUMN_SHOWN, OPTION_REMOTEFILELIST_COLUMN_ORDER);
  172         
  173         if (!m_tabs) {
  174             m_tabs = new wxAuiNotebookEx();
  175 
  176             wxSize splitter_size = m_context_controls[m_current_context_controls].pViewSplitter->GetSize();
  177             m_tabs->Create(this, wxID_ANY, initial_position, splitter_size, wxNO_BORDER | wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_WINDOWLIST_BUTTON | wxAUI_NB_CLOSE_ON_ALL_TABS | wxAUI_NB_TAB_MOVE);
  178             m_tabs->SetExArtProvider();
  179             m_tabs->SetSelectedFont(*wxNORMAL_FONT);
  180             m_tabs->SetMeasuringFont(*wxNORMAL_FONT);
  181 
  182             m_context_controls[m_current_context_controls].pViewSplitter->Reparent(m_tabs);
  183 
  184             m_tabs->AddPage(m_context_controls[m_current_context_controls].pViewSplitter, m_context_controls[m_current_context_controls].pState->GetTitle());
  185             m_tabs->SetTabColour(0, m_context_controls[m_current_context_controls].pState->GetSite().m_colour);
  186             ReplaceWindow(m_context_controls[m_current_context_controls].pViewSplitter, m_tabs);
  187 
  188             m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler(CContextControl::OnTabChanged), 0, this);
  189             m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, wxAuiNotebookEventHandler(CContextControl::OnTabClosing), 0, this);
  190             m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, wxAuiNotebookEventHandler(CContextControl::OnTabBgDoubleclick), 0, this);
  191             m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, wxAuiNotebookEventHandler(CContextControl::OnTabClosing), 0, this);
  192             m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, wxAuiNotebookEventHandler(CContextControl::OnTabRightclick), 0, this);
  193 
  194 #ifdef __WXMAC__
  195             // We need to select the first page as the default selection is -1. Not doing so prevents selecting other pages later on.
  196             m_tabs->SetSelection(0);
  197 #endif
  198         }
  199 
  200         parent = m_tabs;
  201     }
  202 
  203     CContextControl::_context_controls context_controls;
  204 
  205     context_controls.pState = &state;
  206     context_controls.pViewSplitter = new CSplitterWindowEx(parent, -1, initial_position, wxDefaultSize, wxSP_NOBORDER  | wxSP_LIVE_UPDATE);
  207     context_controls.pViewSplitter->SetMinimumPaneSize(50, 100);
  208     context_controls.pViewSplitter->SetSashGravity(0.5);
  209 
  210     context_controls.pLocalSplitter = new CSplitterWindowEx(context_controls.pViewSplitter, -1, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER  | wxSP_LIVE_UPDATE);
  211     context_controls.pLocalSplitter->SetMinimumPaneSize(50, 100);
  212 
  213     context_controls.pRemoteSplitter = new CSplitterWindowEx(context_controls.pViewSplitter, -1, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER  | wxSP_LIVE_UPDATE);
  214     context_controls.pRemoteSplitter->SetMinimumPaneSize(50, 100);
  215 
  216     context_controls.pLocalTreeViewPanel = new CView(context_controls.pLocalSplitter);
  217     context_controls.pLocalListViewPanel = new CView(context_controls.pLocalSplitter);
  218     context_controls.pLocalTreeView = new CLocalTreeView(context_controls.pLocalTreeViewPanel, -1, state, m_mainFrame.GetQueue());
  219     context_controls.pLocalListView = new CLocalListView(context_controls.pLocalListViewPanel, state, m_mainFrame.GetQueue());
  220     context_controls.pLocalTreeViewPanel->SetWindow(context_controls.pLocalTreeView);
  221     context_controls.pLocalListViewPanel->SetWindow(context_controls.pLocalListView);
  222 
  223     context_controls.pRemoteTreeViewPanel = new CView(context_controls.pRemoteSplitter);
  224     context_controls.pRemoteListViewPanel = new CView(context_controls.pRemoteSplitter);
  225     context_controls.pRemoteTreeView = new CRemoteTreeView(context_controls.pRemoteTreeViewPanel, -1, state, m_mainFrame.GetQueue());
  226     context_controls.pRemoteListView = new CRemoteListView(context_controls.pRemoteListViewPanel, state, m_mainFrame.GetQueue());
  227     context_controls.pRemoteTreeViewPanel->SetWindow(context_controls.pRemoteTreeView);
  228     context_controls.pRemoteListViewPanel->SetWindow(context_controls.pRemoteListView);
  229 
  230     bool show_filelist_statusbars = COptions::Get()->GetOptionVal(OPTION_FILELIST_STATUSBAR) != 0;
  231 
  232     CFilelistStatusBar* pLocalFilelistStatusBar = new CFilelistStatusBar(context_controls.pLocalListViewPanel);
  233     if (!show_filelist_statusbars) {
  234         pLocalFilelistStatusBar->Hide();
  235     }
  236     context_controls.pLocalListViewPanel->SetStatusBar(pLocalFilelistStatusBar);
  237     if (context_controls.pLocalListView) {
  238         context_controls.pLocalListView->SetFilelistStatusBar(pLocalFilelistStatusBar);
  239     }
  240     pLocalFilelistStatusBar->SetConnected(true);
  241 
  242     CFilelistStatusBar* pRemoteFilelistStatusBar = new CFilelistStatusBar(context_controls.pRemoteListViewPanel);
  243     if (!show_filelist_statusbars) {
  244         pRemoteFilelistStatusBar->Hide();
  245     }
  246     context_controls.pRemoteListViewPanel->SetStatusBar(pRemoteFilelistStatusBar);
  247     if (context_controls.pRemoteListView) {
  248         context_controls.pRemoteListView->SetFilelistStatusBar(pRemoteFilelistStatusBar);
  249     }
  250 
  251     auto localRecursiveStatus = new CRecursiveOperationStatus(context_controls.pLocalListViewPanel, state, true);
  252     context_controls.pLocalListViewPanel->SetFooter(localRecursiveStatus);
  253 
  254     auto remoteRecursiveStatus = new CRecursiveOperationStatus(context_controls.pRemoteListViewPanel, state, false);
  255     context_controls.pRemoteListViewPanel->SetFooter(remoteRecursiveStatus);
  256 
  257     context_controls.pLocalListSearchPanel = new CListSearchPanel(context_controls.pLocalListViewPanel, context_controls.pLocalListView, context_controls.pState, true);
  258     context_controls.pLocalListViewPanel->SetSearchPanel(context_controls.pLocalListSearchPanel);
  259 
  260     context_controls.pRemoteListSearchPanel = new CListSearchPanel(context_controls.pRemoteListViewPanel, context_controls.pRemoteListView, context_controls.pState, false);
  261     context_controls.pRemoteListViewPanel->SetSearchPanel(context_controls.pRemoteListSearchPanel);
  262 
  263     const int layout = COptions::Get()->GetOptionVal(OPTION_FILEPANE_LAYOUT);
  264     const int swap = COptions::Get()->GetOptionVal(OPTION_FILEPANE_SWAP);
  265 
  266     if (layout == 1) {
  267         if (swap) {
  268             context_controls.pViewSplitter->SplitHorizontally(context_controls.pRemoteSplitter, context_controls.pLocalSplitter);
  269         }
  270         else {
  271             context_controls.pViewSplitter->SplitHorizontally(context_controls.pLocalSplitter, context_controls.pRemoteSplitter);
  272         }
  273     }
  274     else {
  275         if (swap) {
  276             context_controls.pViewSplitter->SplitVertically(context_controls.pRemoteSplitter, context_controls.pLocalSplitter);
  277         }
  278         else {
  279             context_controls.pViewSplitter->SplitVertically(context_controls.pLocalSplitter, context_controls.pRemoteSplitter);
  280         }
  281     }
  282 
  283     if (COptions::Get()->GetOptionVal(OPTION_SHOW_TREE_LOCAL)) {
  284         context_controls.pLocalViewHeader = new CLocalViewHeader(context_controls.pLocalTreeViewPanel, state);
  285         context_controls.pLocalTreeViewPanel->SetHeader(context_controls.pLocalViewHeader);
  286         if (layout == 3 && swap) {
  287             context_controls.pLocalSplitter->SplitVertically(context_controls.pLocalListViewPanel, context_controls.pLocalTreeViewPanel);
  288         }
  289         else if (layout) {
  290             context_controls.pLocalSplitter->SplitVertically(context_controls.pLocalTreeViewPanel, context_controls.pLocalListViewPanel);
  291         }
  292         else {
  293             context_controls.pLocalSplitter->SplitHorizontally(context_controls.pLocalTreeViewPanel, context_controls.pLocalListViewPanel);
  294         }
  295     }
  296     else {
  297         context_controls.pLocalTreeViewPanel->Hide();
  298         context_controls.pLocalViewHeader = new CLocalViewHeader(context_controls.pLocalListViewPanel, state);
  299         context_controls.pLocalListViewPanel->SetHeader(context_controls.pLocalViewHeader);
  300         context_controls.pLocalSplitter->Initialize(context_controls.pLocalListViewPanel);
  301     }
  302 
  303     if (COptions::Get()->GetOptionVal(OPTION_SHOW_TREE_REMOTE)) {
  304         context_controls.pRemoteViewHeader = new CRemoteViewHeader(context_controls.pRemoteTreeViewPanel, state);
  305         context_controls.pRemoteTreeViewPanel->SetHeader(context_controls.pRemoteViewHeader);
  306         if (layout == 3 && !swap) {
  307             context_controls.pRemoteSplitter->SplitVertically(context_controls.pRemoteListViewPanel, context_controls.pRemoteTreeViewPanel);
  308         }
  309         else if (layout) {
  310             context_controls.pRemoteSplitter->SplitVertically(context_controls.pRemoteTreeViewPanel, context_controls.pRemoteListViewPanel);
  311         }
  312         else {
  313             context_controls.pRemoteSplitter->SplitHorizontally(context_controls.pRemoteTreeViewPanel, context_controls.pRemoteListViewPanel);
  314         }
  315     }
  316     else {
  317         context_controls.pRemoteTreeViewPanel->Hide();
  318         context_controls.pRemoteViewHeader = new CRemoteViewHeader(context_controls.pRemoteListViewPanel, state);
  319         context_controls.pRemoteListViewPanel->SetHeader(context_controls.pRemoteViewHeader);
  320         context_controls.pRemoteSplitter->Initialize(context_controls.pRemoteListViewPanel);
  321     }
  322 
  323     if (layout == 3) {
  324         if (!swap) {
  325             context_controls.pRemoteSplitter->SetSashGravity(1.0);
  326         }
  327         else {
  328             context_controls.pLocalSplitter->SetSashGravity(1.0);
  329         }
  330     }
  331 
  332     if (!m_context_controls.empty()) {
  333         context_controls.SetSplitterPositions(splitterPositions);
  334     }
  335     else {
  336         context_controls.pViewSplitter->SetRelativeSashPosition(0.5);
  337         context_controls.pLocalSplitter->SetRelativeSashPosition(0.4);
  338         context_controls.pLocalSplitter->SetRelativeSashPosition(0.4);
  339     }
  340 
  341     m_mainFrame.ConnectNavigationHandler(context_controls.pLocalListView);
  342     m_mainFrame.ConnectNavigationHandler(context_controls.pRemoteListView);
  343     m_mainFrame.ConnectNavigationHandler(context_controls.pLocalTreeView);
  344     m_mainFrame.ConnectNavigationHandler(context_controls.pRemoteTreeView);
  345     m_mainFrame.ConnectNavigationHandler(context_controls.pLocalViewHeader);
  346     m_mainFrame.ConnectNavigationHandler(context_controls.pRemoteViewHeader);
  347 
  348     if (context_controls.pLocalListView && context_controls.pRemoteListView) {
  349         state.GetComparisonManager()->SetListings(context_controls.pLocalListView, context_controls.pRemoteListView);
  350     }
  351 
  352     if (m_tabs) {
  353         m_tabs->AddPage(context_controls.pViewSplitter, state.GetTitle());
  354     }
  355     else {
  356         Initialize(context_controls.pViewSplitter);
  357     }
  358 
  359     m_context_controls.push_back(context_controls);
  360 }
  361 
  362 void CContextControl::OnTabRefresh(wxCommandEvent&)
  363 {
  364     if (m_right_clicked_tab == -1) {
  365         return;
  366     }
  367 
  368     auto * controls = GetControlsFromTabIndex(m_right_clicked_tab);
  369     if (controls) {
  370         controls->pState->RefreshLocal();
  371         controls->pState->RefreshRemote();
  372     }
  373 }
  374 
  375  CContextControl::_context_controls* CContextControl::GetCurrentControls()
  376 {
  377     if (m_current_context_controls == -1) {
  378         return 0;
  379     }
  380 
  381     return &m_context_controls[m_current_context_controls];
  382 }
  383 
  384 CContextControl::_context_controls* CContextControl::GetControlsFromState(CState* pState)
  385 {
  386     size_t i = 0;
  387     for (i = 0; i < m_context_controls.size(); ++i) {
  388         if (m_context_controls[i].pState == pState) {
  389             return &m_context_controls[i];
  390         }
  391     }
  392     return 0;
  393 }
  394 
  395 bool CContextControl::CloseTab(int tab)
  396 {
  397     if (!m_tabs) {
  398         return false;
  399     }
  400     if (tab < 0 || static_cast<size_t>(tab) >= m_tabs->GetPageCount()) {
  401         return false;
  402     }
  403 
  404 
  405     auto *const removeControls = GetControlsFromTabIndex(tab);
  406 
  407     CState *const pState = removeControls->pState;
  408 
  409     if (!pState->m_pCommandQueue->Idle()) {
  410         if (wxMessageBoxEx(_("Cannot close tab while busy.\nCancel current operation and close tab?"), _T("FileZilla"), wxYES_NO | wxICON_QUESTION) != wxYES) {
  411             return false;
  412         }
  413     }
  414 
  415 #ifndef __WXMAC__
  416     // Some reparenting is being done when closing tabs. Reparenting of frozen windows isn't working
  417     // on OS X.
  418     wxWindowUpdateLocker lock(this);
  419 #endif
  420 
  421     pState->m_pCommandQueue->Cancel();
  422     pState->GetLocalRecursiveOperation()->StopRecursiveOperation();
  423     pState->GetRemoteRecursiveOperation()->StopRecursiveOperation();
  424 
  425     pState->GetComparisonManager()->SetListings(0, 0);
  426 
  427     if (m_tabs->GetPageCount() == 2) {
  428         // Get rid again of tab bar
  429         m_tabs->Disconnect(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler(CContextControl::OnTabChanged), 0, this);
  430 
  431         int keep = tab ? 0 : 1;
  432 
  433         auto * keptControls = GetControlsFromTabIndex(keep);
  434         m_tabs->RemovePage(keep);
  435 
  436         CContextManager::Get()->SetCurrentContext(keptControls->pState);
  437 
  438         keptControls->pViewSplitter->Reparent(this);
  439         ReplaceWindow(m_tabs, keptControls->pViewSplitter);
  440         keptControls->pViewSplitter->Show();
  441 
  442         wxAuiNotebookEx *tabs = m_tabs;
  443         m_tabs = 0;
  444 
  445         // We don't actually delete the controls outselves, that's done by wx as part of the RemovePage call.
  446         removeControls->pViewSplitter = 0;
  447 
  448         CContextManager::Get()->SetCurrentContext(keptControls->pState);
  449 
  450         tabs->Destroy();
  451     }
  452     else {
  453         if (pState == CContextManager::Get()->GetCurrentContext()) {
  454             int newsel = tab + 1;
  455             if (newsel >= (int)m_tabs->GetPageCount()) {
  456                 newsel = m_tabs->GetPageCount() - 2;
  457             }
  458 
  459             m_tabs->SetSelection(newsel);
  460             CContextManager::Get()->SetCurrentContext(GetControlsFromTabIndex(newsel)->pState);
  461         }
  462 
  463         removeControls->pViewSplitter = 0;
  464         m_tabs->DeletePage(tab);
  465     }
  466 
  467     pState->Disconnect();
  468 
  469     return true;
  470 }
  471 
  472 void CContextControl::OnTabBgDoubleclick(wxAuiNotebookEvent&)
  473 {
  474     CreateTab();
  475 }
  476 
  477 void CContextControl::OnTabRightclick(wxAuiNotebookEvent& event)
  478 {
  479     wxMenu menu;
  480     menu.Append(XRCID("ID_TABCONTEXT_NEW"), _("&Create new tab"));
  481 
  482     menu.AppendSeparator();
  483     menu.Append(XRCID("ID_TABCONTEXT_CLOSE"), _("Cl&ose tab"));
  484     menu.Append(XRCID("ID_TABCONTEXT_CLOSEOTHERS"), _("Close &all other tabs"));
  485 
  486     menu.AppendSeparator();
  487     menu.Append(XRCID("ID_TABCONTEXT_REFRESH"), _("&Refresh"));
  488 
  489     if (!m_tabs || m_tabs->GetPageCount() < 2) {
  490         menu.Enable(XRCID("ID_TABCONTEXT_CLOSE"), false);
  491         menu.Enable(XRCID("ID_TABCONTEXT_CLOSEOTHERS"), false);
  492     }
  493 
  494     m_right_clicked_tab = event.GetSelection();
  495 
  496     PopupMenu(&menu);
  497 }
  498 
  499 void CContextControl::OnTabContextClose(wxCommandEvent&)
  500 {
  501     if (m_right_clicked_tab == -1) {
  502         return;
  503     }
  504 
  505     // Need to defer event, wxAUI would write to free'd memory
  506     // if we'd actually delete tab and potenially the notebook with it
  507     QueueEvent(new wxCommandEvent(fzEVT_TAB_CLOSING_DEFERRED, m_right_clicked_tab));
  508 }
  509 
  510 void CContextControl::OnTabContextCloseOthers(wxCommandEvent&)
  511 {
  512     QueueEvent(new wxCommandEvent (fzEVT_TAB_CLOSING_DEFERRED, -m_right_clicked_tab - 1));
  513 }
  514 
  515 void CContextControl::OnTabClosing_Deferred(wxCommandEvent& event)
  516 {
  517     int tab = event.GetId();
  518     if (tab < 0) {
  519         ++tab;
  520         int count = GetTabCount();
  521         for (int i = count - 1; i >= 0; --i) {
  522             if (i != -tab) {
  523                 CloseTab(i);
  524             }
  525         }
  526     }
  527     else {
  528         CloseTab(tab);
  529     }
  530 }
  531 
  532 
  533 void CContextControl::OnTabChanged(wxAuiNotebookEvent&)
  534 {
  535     int i = m_tabs->GetSelection();
  536     auto * const controls = GetControlsFromTabIndex(i);
  537     if (!controls) {
  538         return;
  539     }
  540 
  541     CContextManager::Get()->SetCurrentContext(controls->pState);
  542 }
  543 
  544 void CContextControl::OnTabClosing(wxAuiNotebookEvent& event)
  545 {
  546     // Need to defer event, wxAUI would write to free'd memory
  547     // if we'd actually delete tab and potenially the notebook with it
  548     QueueEvent(new wxCommandEvent(fzEVT_TAB_CLOSING_DEFERRED, event.GetSelection()));
  549 
  550     event.Veto();
  551 }
  552 
  553 int CContextControl::GetCurrentTab() const
  554 {
  555     return m_tabs ? m_tabs->GetSelection() : (m_context_controls.empty() ? -1 : 0);
  556 }
  557 
  558 int CContextControl::GetTabCount() const
  559 {
  560     return m_tabs ? m_tabs->GetPageCount() : (m_context_controls.empty() ? 0 : 1);
  561 }
  562 
  563 CContextControl::_context_controls* CContextControl::GetControlsFromTabIndex(int i)
  564 {
  565     if (!m_tabs) {
  566         if (i == 0 && !m_context_controls.empty()) {
  567             for (auto & controls : m_context_controls) {
  568                 if (controls.pViewSplitter != 0) {
  569                     return &controls;
  570                 }
  571             }
  572         }
  573         return 0;
  574     }
  575 
  576     wxWindow* page = m_tabs->GetPage(i);
  577     if (page) {
  578         for (auto & controls : m_context_controls) {
  579             if (controls.pViewSplitter == page) {
  580                 return &controls;
  581             }
  582         }
  583     }
  584 
  585     return 0;
  586 }
  587 
  588 bool CContextControl::SelectTab(int i)
  589 {
  590     if (i < 0) {
  591         return false;
  592     }
  593 
  594     if (!m_tabs) {
  595         if (i != 0) {
  596             return false;
  597         }
  598 
  599         return true;
  600     }
  601 
  602     if ((int)m_tabs->GetPageCount() <= i) {
  603         return false;
  604     }
  605 
  606     m_tabs->SetSelection(i);
  607 
  608     return true;
  609 }
  610 
  611 void CContextControl::AdvanceTab(bool forward)
  612 {
  613     if (!m_tabs) {
  614         return;
  615     }
  616 
  617     m_tabs->AdvanceTab(forward);
  618 }
  619 
  620 void CContextControl::OnStateChange(CState* pState, t_statechange_notifications notification, std::wstring const&, const void*)
  621 {
  622     if (notification == STATECHANGE_CHANGEDCONTEXT) {
  623         if (!pState) {
  624             m_current_context_controls = m_context_controls.empty() ? -1 : 0;
  625             return;
  626         }
  627 
  628         // Get current controls for new current context
  629         for (m_current_context_controls = 0; m_current_context_controls < static_cast<int>(m_context_controls.size()); ++m_current_context_controls) {
  630             if (m_context_controls[m_current_context_controls].pState == pState) {
  631                 break;
  632             }
  633         }
  634         if (m_current_context_controls == static_cast<int>(m_context_controls.size())) {
  635             m_current_context_controls = -1;
  636         }
  637     }
  638     else if (notification == STATECHANGE_SERVER) {
  639         if (!m_tabs) {
  640             return;
  641         }
  642 
  643         CContextControl::_context_controls* controls = GetControlsFromState(pState);
  644         if (controls && controls->used()) {
  645             int i = m_tabs->GetPageIndex(controls->pViewSplitter);
  646             if (i != wxNOT_FOUND) {
  647                 m_tabs->SetTabColour(i, controls->pState->GetSite().m_colour);
  648                 m_tabs->SetPageText(i, controls->pState->GetTitle());
  649             }
  650         }
  651     }
  652     else if (notification == STATECHANGE_REWRITE_CREDENTIALS) {
  653         SaveTabs();
  654     }
  655 }
  656 
  657 void CContextControl::OnTabContextNew(wxCommandEvent&)
  658 {
  659     CreateTab();
  660 }
  661 
  662 void CContextControl::SaveTabs()
  663 {
  664     pugi::xml_document xml;
  665     auto tabs = xml.append_child("Tabs");
  666 
  667     int const currentTab = GetCurrentTab();
  668 
  669     for (int i = 0; i < GetTabCount(); ++i) {
  670         auto controls = GetControlsFromTabIndex(i);
  671         if (!controls || !controls->pState) {
  672             continue;
  673         }
  674 
  675         Site const site = controls->pState->GetLastSite();
  676 
  677         auto tab = tabs.append_child("Tab");
  678         SetServer(tab, site);
  679         tab.append_child("Site").text().set(fz::to_utf8(site.SitePath()).c_str());
  680         tab.append_child("RemotePath").text().set(fz::to_utf8(controls->pState->GetLastServerPath().GetSafePath()).c_str());
  681         tab.append_child("LocalPath").text().set(fz::to_utf8(controls->pState->GetLocalDir().GetPath()).c_str());
  682 
  683         if (controls->pState->IsRemoteConnected()) {
  684             tab.append_attribute("connected").set_value(1);
  685         }
  686         if (i == currentTab) {
  687             tab.append_attribute("selected").set_value(1);
  688         }
  689     }
  690 
  691     COptions::Get()->SetOptionXml(OPTION_TAB_DATA, xml);
  692 }
  693 
  694 void CContextControl::RestoreTabs()
  695 {
  696     if (!m_context_controls.empty()) {
  697         return;
  698     }
  699 
  700     int selected = 0;
  701 
  702     auto xml = COptions::Get()->GetOptionXml(OPTION_TAB_DATA);
  703 
  704     bool selectedOnly = COptions::Get()->GetOptionVal(OPTION_STARTUP_ACTION) != 2;
  705 
  706     CCommandLine const* pCommandLine = wxGetApp().GetCommandLine();
  707     if (pCommandLine && pCommandLine->BlocksReconnectAtStartup()) {
  708         selectedOnly = true;
  709     }
  710 
  711     pugi::xml_node tabs = xml.child("Tabs");
  712     if (tabs) {
  713         for (auto tab = tabs.child("Tab"); tab; tab = tab.next_sibling("Tab")) {
  714 
  715             if (tab.attribute("selected").as_int()) {
  716                 selected = m_context_controls.size();
  717             }
  718             else if (selectedOnly) {
  719                 continue;
  720             }
  721 
  722             CLocalPath localPath(fz::to_wstring_from_utf8(tab.child("LocalPath").child_value()));
  723 
  724             Site site;
  725             CServerPath last_path;
  726 
  727             if (GetServer(tab, site) && last_path.SetSafePath(fz::to_wstring_from_utf8(tab.child("RemotePath").child_value()))) {
  728                 std::wstring last_site_path = fz::to_wstring_from_utf8(tab.child("Site").child_value());
  729 
  730                 std::unique_ptr<Site> ssite;
  731                 if (!last_site_path.empty()) {
  732                     auto ssite = CSiteManager::GetSiteByPath(last_site_path, false).first;
  733                     if (ssite && ssite->SameResource(site)) {
  734                         site = *ssite;
  735                     }
  736                 }
  737             }
  738 
  739             CreateTab(localPath, site, last_path);
  740         }
  741     }
  742 
  743     if (m_context_controls.empty()) {
  744         CreateTab();
  745     }
  746 
  747     SelectTab(selected);
  748 }
  749 
  750 namespace {
  751 bool SwitchFocus(wxWindow *focus, wxWindow *first, wxWindow *second)
  752 {
  753     if (focus == first) {
  754         if (second && second->IsShownOnScreen() && second->IsEnabled()) {
  755             second->SetFocus();
  756         }
  757         return true;
  758     }
  759     return false;
  760 }
  761 }
  762 
  763 void CContextControl::_context_controls::SwitchFocusedSide()
  764 {
  765     std::array<std::pair<wxWindow*, wxWindow*>, 3> ctrls =
  766     {{
  767         {pLocalListView, pRemoteListView},
  768         {pLocalTreeView, pRemoteTreeView},
  769         {pLocalViewHeader, pRemoteViewHeader}
  770     }};
  771     auto *focus = wxWindow::FindFocus();
  772     while (focus) {
  773         for (auto & p : ctrls) {
  774             if (SwitchFocus(focus, p.first, p.second)) {
  775                     return;
  776             }
  777             if (SwitchFocus(focus, p.second, p.first)) {
  778                     return;
  779             }
  780         }
  781         focus = focus->GetParent();
  782     }
  783 }
  784 
  785 std::tuple<double, int, int> CContextControl::_context_controls::GetSplitterPositions()
  786 {
  787     std::tuple<double, int, int> ret;
  788 
  789     std::get<0>(ret) = pViewSplitter ? pViewSplitter->GetRelativeSashPosition() : 0.5f;
  790     std::get<1>(ret) = pLocalSplitter ? pLocalSplitter->GetSashPosition() : 135;
  791     std::get<2>(ret) = pRemoteSplitter ? pRemoteSplitter->GetSashPosition() : 135;
  792 
  793     return ret;
  794 }
  795 
  796 void CContextControl::_context_controls::SetSplitterPositions(std::tuple<double, int, int> const& positions)
  797 {
  798     if (pViewSplitter) {
  799         double pos = std::get<0>(positions);
  800         if (pos < 0 || pos > 1) {
  801             pos = 0.5;
  802         }
  803         pViewSplitter->SetRelativeSashPosition(pos);
  804     }
  805     if (pLocalSplitter) {
  806         pLocalSplitter->SetSashPosition(std::get<1>(positions));
  807     }
  808     if (pRemoteSplitter) {
  809         pRemoteSplitter->SetSashPosition(std::get<2>(positions));
  810     }
  811 }