#include #include "commandqueue.h" #include "context_control.h" #include "filelist_statusbar.h" #include "filezillaapp.h" #include "LocalListView.h" #include "LocalTreeView.h" #include "Mainfrm.h" #include "Options.h" #include "queue.h" #include "recursive_operation.h" #include "recursive_operation_status.h" #include "RemoteListView.h" #include "RemoteTreeView.h" #include "sitemanager.h" #include "splitter.h" #include "view.h" #include "viewheader.h" #include DECLARE_EVENT_TYPE(fzEVT_TAB_CLOSING_DEFERRED, -1) DEFINE_EVENT_TYPE(fzEVT_TAB_CLOSING_DEFERRED) BEGIN_EVENT_TABLE(CContextControl, wxSplitterWindow) EVT_MENU(XRCID("ID_TABCONTEXT_REFRESH"), CContextControl::OnTabRefresh) EVT_COMMAND(wxID_ANY, fzEVT_TAB_CLOSING_DEFERRED, CContextControl::OnTabClosing_Deferred) EVT_MENU(XRCID("ID_TABCONTEXT_CLOSE"), CContextControl::OnTabContextClose) EVT_MENU(XRCID("ID_TABCONTEXT_CLOSEOTHERS"), CContextControl::OnTabContextCloseOthers) EVT_MENU(XRCID("ID_TABCONTEXT_NEW"), CContextControl::OnTabContextNew) END_EVENT_TABLE() CContextControl::CContextControl(CMainFrame& mainFrame) : CStateEventHandler(0) , m_mainFrame(mainFrame) { wxASSERT(!CContextManager::Get()->HandlerCount(STATECHANGE_CHANGEDCONTEXT)); CContextManager::Get()->RegisterHandler(this, STATECHANGE_CHANGEDCONTEXT, false); CContextManager::Get()->RegisterHandler(this, STATECHANGE_SERVER, false); } CContextControl::~CContextControl() { } void CContextControl::Create(wxWindow *parent) { wxSplitterWindow::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER); } void CContextControl::CreateTab() { wxGetApp().AddStartupProfileRecord(_T("CContextControl::CreateTab")); { #ifdef __WXMSW__ // Some reparenting is being done when creating tabs. Reparenting of frozen windows isn't working // on OS X. wxWindowUpdateLocker lock(this); #endif CState* pState = 0; // See if we can reuse an existing context for (size_t i = 0; i < m_context_controls.size(); i++) { if (m_context_controls[i].tab_index != -1) continue; if (m_context_controls[i].pState->IsRemoteConnected() || !m_context_controls[i].pState->IsRemoteIdle()) continue; pState = m_context_controls[i].pState; m_context_controls.erase(m_context_controls.begin() + i); if (m_current_context_controls > (int)i) m_current_context_controls--; break; } if (!pState) { pState = CContextManager::Get()->CreateState(m_mainFrame); if (!pState->CreateEngine()) { wxMessageBoxEx(_("Failed to initialize FTP engine")); } } // Restore last server and path CServer last_server; CServerPath last_path; if (COptions::Get()->GetLastServer(last_server) && last_path.SetSafePath(COptions::Get()->GetOption(OPTION_LASTSERVERPATH))) pState->SetLastServer(last_server, last_path); CreateContextControls(pState); pState->GetRecursiveOperationHandler()->SetQueue(m_mainFrame.GetQueue()); wxString const localDir = COptions::Get()->GetOption(OPTION_LASTLOCALDIR); if (!pState->SetLocalDir(localDir)) { wxString const homeDir = wxGetHomeDir(); if (!pState->SetLocalDir(homeDir)) { pState->SetLocalDir(_T("/")); } } CContextManager::Get()->SetCurrentContext(pState); } if (!m_mainFrame.RestoreSplitterPositions()) m_mainFrame.SetDefaultSplitterPositions(); if (m_tabs) m_tabs->SetSelection(m_tabs->GetPageCount() - 1); } void CContextControl::CreateContextControls(CState* pState) { wxGetApp().AddStartupProfileRecord(_T("CContextControl::CreateContextControls")); wxWindow* parent = this; #ifdef __WXGTK__ // This prevents some ugly flickering on tab creation. const wxPoint initial_position(1000000, 1000000); #else const wxPoint initial_position(wxDefaultPosition); #endif if (!m_context_controls.empty()) { if (!m_tabs) { m_tabs = new wxAuiNotebookEx(); wxSize splitter_size = m_context_controls[m_current_context_controls].pViewSplitter->GetSize(); 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); m_tabs->SetExArtProvider(); m_tabs->SetSelectedFont(*wxNORMAL_FONT); m_tabs->SetMeasuringFont(*wxNORMAL_FONT); m_context_controls[m_current_context_controls].pViewSplitter->Reparent(m_tabs); m_tabs->AddPage(m_context_controls[m_current_context_controls].pViewSplitter, m_context_controls[m_current_context_controls].pState->GetTitle()); ReplaceWindow(m_context_controls[m_current_context_controls].pViewSplitter, m_tabs); m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler(CContextControl::OnTabChanged), 0, this); m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, wxAuiNotebookEventHandler(CContextControl::OnTabClosing), 0, this); m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, wxAuiNotebookEventHandler(CContextControl::OnTabBgDoubleclick), 0, this); m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, wxAuiNotebookEventHandler(CContextControl::OnTabClosing), 0, this); m_tabs->Connect(wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, wxAuiNotebookEventHandler(CContextControl::OnTabRightclick), 0, this); #ifdef __WXMAC__ // We need to select the first page as the default selection is -1. Not doing so prevents selecting other pages later on. m_tabs->SetSelection(0); #endif } m_mainFrame.RememberSplitterPositions(); m_context_controls[m_current_context_controls].pLocalListView->SaveColumnSettings(OPTION_LOCALFILELIST_COLUMN_WIDTHS, OPTION_LOCALFILELIST_COLUMN_SHOWN, OPTION_LOCALFILELIST_COLUMN_ORDER); m_context_controls[m_current_context_controls].pRemoteListView->SaveColumnSettings(OPTION_REMOTEFILELIST_COLUMN_WIDTHS, OPTION_REMOTEFILELIST_COLUMN_SHOWN, OPTION_REMOTEFILELIST_COLUMN_ORDER); parent = m_tabs; } struct CContextControl::_context_controls context_controls; context_controls.pState = pState; context_controls.pViewSplitter = new CSplitterWindowEx(parent, -1, initial_position, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE); context_controls.pViewSplitter->SetMinimumPaneSize(50, 100); context_controls.pViewSplitter->SetSashGravity(0.5); context_controls.pLocalSplitter = new CSplitterWindowEx(context_controls.pViewSplitter, -1, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE); context_controls.pLocalSplitter->SetMinimumPaneSize(50, 100); context_controls.pRemoteSplitter = new CSplitterWindowEx(context_controls.pViewSplitter, -1, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE); context_controls.pRemoteSplitter->SetMinimumPaneSize(50, 100); context_controls.pLocalTreeViewPanel = new CView(context_controls.pLocalSplitter); context_controls.pLocalListViewPanel = new CView(context_controls.pLocalSplitter); context_controls.pLocalTreeView = new CLocalTreeView(context_controls.pLocalTreeViewPanel, -1, pState, m_mainFrame.GetQueue()); context_controls.pLocalListView = new CLocalListView(context_controls.pLocalListViewPanel, pState, m_mainFrame.GetQueue()); context_controls.pLocalTreeViewPanel->SetWindow(context_controls.pLocalTreeView); context_controls.pLocalListViewPanel->SetWindow(context_controls.pLocalListView); context_controls.pRemoteTreeViewPanel = new CView(context_controls.pRemoteSplitter); context_controls.pRemoteListViewPanel = new CView(context_controls.pRemoteSplitter); context_controls.pRemoteTreeView = new CRemoteTreeView(context_controls.pRemoteTreeViewPanel, -1, pState, m_mainFrame.GetQueue()); context_controls.pRemoteListView = new CRemoteListView(context_controls.pRemoteListViewPanel, pState, m_mainFrame.GetQueue()); context_controls.pRemoteTreeViewPanel->SetWindow(context_controls.pRemoteTreeView); context_controls.pRemoteListViewPanel->SetWindow(context_controls.pRemoteListView); bool show_filelist_statusbars = COptions::Get()->GetOptionVal(OPTION_FILELIST_STATUSBAR) != 0; CFilelistStatusBar* pLocalFilelistStatusBar = new CFilelistStatusBar(context_controls.pLocalListViewPanel); if (!show_filelist_statusbars) pLocalFilelistStatusBar->Hide(); context_controls.pLocalListViewPanel->SetStatusBar(pLocalFilelistStatusBar); context_controls.pLocalListView->SetFilelistStatusBar(pLocalFilelistStatusBar); pLocalFilelistStatusBar->SetConnected(true); CFilelistStatusBar* pRemoteFilelistStatusBar = new CFilelistStatusBar(context_controls.pRemoteListViewPanel); if (!show_filelist_statusbars) pRemoteFilelistStatusBar->Hide(); context_controls.pRemoteListViewPanel->SetStatusBar(pRemoteFilelistStatusBar); context_controls.pRemoteListView->SetFilelistStatusBar(pRemoteFilelistStatusBar); auto recursiveStatus = new CRecursiveOperationStatus(context_controls.pRemoteListViewPanel, context_controls.pState); context_controls.pRemoteListViewPanel->SetFooter(recursiveStatus); const int layout = COptions::Get()->GetOptionVal(OPTION_FILEPANE_LAYOUT); const int swap = COptions::Get()->GetOptionVal(OPTION_FILEPANE_SWAP); if (layout == 1) { if (swap) context_controls.pViewSplitter->SplitHorizontally(context_controls.pRemoteSplitter, context_controls.pLocalSplitter); else context_controls.pViewSplitter->SplitHorizontally(context_controls.pLocalSplitter, context_controls.pRemoteSplitter); } else { if (swap) context_controls.pViewSplitter->SplitVertically(context_controls.pRemoteSplitter, context_controls.pLocalSplitter); else context_controls.pViewSplitter->SplitVertically(context_controls.pLocalSplitter, context_controls.pRemoteSplitter); } if (COptions::Get()->GetOptionVal(OPTION_SHOW_TREE_LOCAL)) { context_controls.pLocalViewHeader = new CLocalViewHeader(context_controls.pLocalTreeViewPanel, pState); context_controls.pLocalTreeViewPanel->SetHeader(context_controls.pLocalViewHeader); if (layout == 3 && swap) context_controls.pLocalSplitter->SplitVertically(context_controls.pLocalListViewPanel, context_controls.pLocalTreeViewPanel); else if (layout) context_controls.pLocalSplitter->SplitVertically(context_controls.pLocalTreeViewPanel, context_controls.pLocalListViewPanel); else context_controls.pLocalSplitter->SplitHorizontally(context_controls.pLocalTreeViewPanel, context_controls.pLocalListViewPanel); } else { context_controls.pLocalTreeViewPanel->Hide(); context_controls.pLocalViewHeader = new CLocalViewHeader(context_controls.pLocalListViewPanel, pState); context_controls.pLocalListViewPanel->SetHeader(context_controls.pLocalViewHeader); context_controls.pLocalSplitter->Initialize(context_controls.pLocalListViewPanel); } if (COptions::Get()->GetOptionVal(OPTION_SHOW_TREE_REMOTE)) { context_controls.pRemoteViewHeader = new CRemoteViewHeader(context_controls.pRemoteTreeViewPanel, pState); context_controls.pRemoteTreeViewPanel->SetHeader(context_controls.pRemoteViewHeader); if (layout == 3 && !swap) context_controls.pRemoteSplitter->SplitVertically(context_controls.pRemoteListViewPanel, context_controls.pRemoteTreeViewPanel); else if (layout) context_controls.pRemoteSplitter->SplitVertically(context_controls.pRemoteTreeViewPanel, context_controls.pRemoteListViewPanel); else context_controls.pRemoteSplitter->SplitHorizontally(context_controls.pRemoteTreeViewPanel, context_controls.pRemoteListViewPanel); } else { context_controls.pRemoteTreeViewPanel->Hide(); context_controls.pRemoteViewHeader = new CRemoteViewHeader(context_controls.pRemoteListViewPanel, pState); context_controls.pRemoteListViewPanel->SetHeader(context_controls.pRemoteViewHeader); context_controls.pRemoteSplitter->Initialize(context_controls.pRemoteListViewPanel); } if (layout == 3) { if (!swap) context_controls.pRemoteSplitter->SetSashGravity(1.0); else context_controls.pLocalSplitter->SetSashGravity(1.0); } m_mainFrame.ConnectNavigationHandler(context_controls.pLocalListView); m_mainFrame.ConnectNavigationHandler(context_controls.pRemoteListView); m_mainFrame.ConnectNavigationHandler(context_controls.pLocalTreeView); m_mainFrame.ConnectNavigationHandler(context_controls.pRemoteTreeView); m_mainFrame.ConnectNavigationHandler(context_controls.pLocalViewHeader); m_mainFrame.ConnectNavigationHandler(context_controls.pRemoteViewHeader); pState->GetComparisonManager()->SetListings(context_controls.pLocalListView, context_controls.pRemoteListView); if (m_tabs) { context_controls.tab_index = m_tabs->GetPageCount(); m_tabs->AddPage(context_controls.pViewSplitter, pState->GetTitle()); // Copy reconnect and bookmark information pState->SetLastServer( m_context_controls[m_current_context_controls].pState->GetLastServer(), m_context_controls[m_current_context_controls].pState->GetLastServerPath()); context_controls.site_bookmarks = m_context_controls[m_current_context_controls].site_bookmarks; } else { context_controls.tab_index = 0; context_controls.site_bookmarks = std::make_shared(); context_controls.site_bookmarks->path = COptions::Get()->GetOption(OPTION_LAST_CONNECTED_SITE); CSiteManager::GetBookmarks(context_controls.site_bookmarks->path, context_controls.site_bookmarks->bookmarks); Initialize(context_controls.pViewSplitter); } m_context_controls.push_back(context_controls); } void CContextControl::OnTabRefresh(wxCommandEvent&) { if (m_right_clicked_tab == -1) return; for (size_t j = 0; j < m_context_controls.size(); j++) { if (m_context_controls[j].tab_index != m_right_clicked_tab) continue; m_context_controls[j].pState->RefreshLocal(); m_context_controls[j].pState->RefreshRemote(); break; } } struct CContextControl::_context_controls* CContextControl::GetCurrentControls() { if (m_current_context_controls == -1) return 0; return &m_context_controls[m_current_context_controls]; } struct CContextControl::_context_controls* CContextControl::GetControlsFromState(CState* pState) { size_t i = 0; for (i = 0; i < m_context_controls.size(); i++) { if (m_context_controls[i].pState == pState) return &m_context_controls[i]; } return 0; } bool CContextControl::CloseTab(int tab) { if (!m_tabs) return false; if (tab < 0) return false; size_t i = 0; for (i = 0; i < m_context_controls.size(); i++) { if (m_context_controls[i].tab_index == tab) break; } if (i == m_context_controls.size()) return false; CState* pState = m_context_controls[i].pState; if (!pState->m_pCommandQueue->Idle()) { if (wxMessageBoxEx(_("Cannot close tab while busy.\nCancel current operation and close tab?"), _T("FileZilla"), wxYES_NO | wxICON_QUESTION) != wxYES) return false; } #ifndef __WXMAC__ // Some reparenting is being done when closing tabs. Reparenting of frozen windows isn't working // on OS X. wxWindowUpdateLocker lock(this); #endif pState->m_pCommandQueue->Cancel(); pState->GetRecursiveOperationHandler()->StopRecursiveOperation(); pState->GetComparisonManager()->SetListings(0, 0); if (m_tabs->GetPageCount() == 2) { // Get rid again of tab bar m_tabs->Disconnect(wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler(CContextControl::OnTabChanged), 0, this); int keep = tab ? 0 : 1; m_tabs->RemovePage(keep); size_t j; for (j = 0; j < m_context_controls.size(); j++) { if (m_context_controls[j].tab_index != keep) continue; break; } m_context_controls[j].pViewSplitter->Reparent(this); ReplaceWindow(m_tabs, m_context_controls[j].pViewSplitter); m_context_controls[j].pViewSplitter->Show(); m_context_controls[j].tab_index = 0; wxAuiNotebookEx *tabs = m_tabs; m_tabs = 0; m_context_controls[i].tab_index = -1; m_context_controls[i].site_bookmarks.reset(); CContextManager::Get()->SetCurrentContext(m_context_controls[j].pState); tabs->Destroy(); } else { if (pState == CContextManager::Get()->GetCurrentContext()) { int newsel = tab + 1; if (newsel >= (int)m_tabs->GetPageCount()) newsel = m_tabs->GetPageCount() - 2; for (size_t j = 0; j < m_context_controls.size(); j++) { if (m_context_controls[j].tab_index != newsel) continue; m_tabs->SetSelection(newsel); CContextManager::Get()->SetCurrentContext(m_context_controls[j].pState); } } for (size_t j = 0; j < m_context_controls.size(); j++) { if (m_context_controls[j].tab_index > tab) m_context_controls[j].tab_index--; } m_context_controls[i].tab_index = -1; m_context_controls[i].site_bookmarks.reset(); m_tabs->DeletePage(tab); } pState->Disconnect(); return true; } void CContextControl::OnTabBgDoubleclick(wxAuiNotebookEvent&) { CreateTab(); } void CContextControl::OnTabRightclick(wxAuiNotebookEvent& event) { wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_TABCONTEXT")); if (!pMenu) { wxBell(); return; } if (!m_tabs || m_tabs->GetPageCount() < 2) { pMenu->Enable(XRCID("ID_TABCONTEXT_CLOSE"), false); pMenu->Enable(XRCID("ID_TABCONTEXT_CLOSEOTHERS"), false); } m_right_clicked_tab = event.GetSelection(); PopupMenu(pMenu); delete pMenu; } void CContextControl::OnTabContextClose(wxCommandEvent&) { if (m_right_clicked_tab == -1) return; // Need to defer event, wxAUI would write to free'd memory // if we'd actually delete tab and potenially the notebook with it QueueEvent(new wxCommandEvent(fzEVT_TAB_CLOSING_DEFERRED, m_right_clicked_tab)); } void CContextControl::OnTabContextCloseOthers(wxCommandEvent&) { QueueEvent(new wxCommandEvent (fzEVT_TAB_CLOSING_DEFERRED, -m_right_clicked_tab - 1)); } void CContextControl::OnTabClosing_Deferred(wxCommandEvent& event) { int tab = event.GetId(); if (tab < 0) { tab++; int count = GetTabCount(); for (int i = count - 1; i >= 0; i--) { if (i != -tab) CloseTab(i); } } else CloseTab(tab); } void CContextControl::OnTabChanged(wxAuiNotebookEvent&) { int i = m_tabs->GetSelection(); if (i < 0 || i >= (int)m_context_controls.size()) return; for (size_t j = 0; j < m_context_controls.size(); j++) { if (m_context_controls[j].tab_index != i) continue; CContextManager::Get()->SetCurrentContext(m_context_controls[j].pState); break; } } void CContextControl::OnTabClosing(wxAuiNotebookEvent& event) { // Need to defer event, wxAUI would write to free'd memory // if we'd actually delete tab and potenially the notebook with it QueueEvent(new wxCommandEvent(fzEVT_TAB_CLOSING_DEFERRED, event.GetSelection())); event.Veto(); } int CContextControl::GetCurrentTab() const { return m_tabs ? m_tabs->GetSelection() : (m_context_controls.empty() ? -1 : 0); } int CContextControl::GetTabCount() const { return m_tabs ? m_tabs->GetPageCount() : (m_context_controls.empty() ? 0 : 1); } struct CContextControl::_context_controls* CContextControl::GetControlsFromTabIndex(int i) { for (size_t j = 0; j < m_context_controls.size(); j++) { if (m_context_controls[j].tab_index == i) return &m_context_controls[j]; } return 0; } bool CContextControl::SelectTab(int i) { if (i < 0) return false; if (!m_tabs) { if (i != 0) return false; return true; } if ((int)m_tabs->GetPageCount() <= i) return false; m_tabs->SetSelection(i); return true; } void CContextControl::AdvanceTab(bool forward) { if (!m_tabs) return; m_tabs->AdvanceTab(forward); } void CContextControl::OnStateChange(CState* pState, enum t_statechange_notifications notification, const wxString&, const void*) { if (notification == STATECHANGE_CHANGEDCONTEXT) { if (!pState) { m_current_context_controls = 0; return; } // Get current controls for new current context for (m_current_context_controls = 0; m_current_context_controls < (int)m_context_controls.size(); m_current_context_controls++) { if (m_context_controls[m_current_context_controls].pState == pState) break; } if (m_current_context_controls == (int)m_context_controls.size()) m_current_context_controls = -1; } else if (notification == STATECHANGE_SERVER) { if (!m_tabs) return; CContextControl::_context_controls* controls = GetControlsFromState(pState); if (controls && controls->tab_index != -1) m_tabs->SetPageText(controls->tab_index, controls->pState->GetTitle()); } } void CContextControl::OnTabContextNew(wxCommandEvent&) { CreateTab(); }