"Fossies" - the Fresh Open Source Software Archive

Member "filezilla-3.48.1/src/interface/Mainfrm.cpp" (18 May 2020, 80290 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 "Mainfrm.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 "Mainfrm.h"
    3 
    4 #include "aboutdialog.h"
    5 #include "asyncrequestqueue.h"
    6 #include "auto_ascii_files.h"
    7 #include "bookmarks_dialog.h"
    8 #include "buildinfo.h"
    9 #include "clearprivatedata.h"
   10 #include "cmdline.h"
   11 #include "commandqueue.h"
   12 #include "conditionaldialog.h"
   13 #include "context_control.h"
   14 #include "defaultfileexistsdlg.h"
   15 #include "edithandler.h"
   16 #include "encoding_converter.h"
   17 #include "export.h"
   18 #include "filelist_statusbar.h"
   19 #include "filezillaapp.h"
   20 #include "filter.h"
   21 #include "import.h"
   22 #include "inputdialog.h"
   23 #include "led.h"
   24 #include "list_search_panel.h"
   25 #include "local_recursive_operation.h"
   26 #include "LocalListView.h"
   27 #include "LocalTreeView.h"
   28 #include "loginmanager.h"
   29 #include "manual_transfer.h"
   30 #include "menu_bar.h"
   31 #include "netconfwizard.h"
   32 #include "Options.h"
   33 #include "power_management.h"
   34 #include "queue.h"
   35 #include "quickconnectbar.h"
   36 #include "remote_recursive_operation.h"
   37 #include "RemoteListView.h"
   38 #include "RemoteTreeView.h"
   39 #include "search.h"
   40 #include "settings/settingsdialog.h"
   41 #include "sitemanager_dialog.h"
   42 #include "speedlimits_dialog.h"
   43 #include "splitter.h"
   44 #include "StatusView.h"
   45 #include "state.h"
   46 #include "themeprovider.h"
   47 #include "toolbar.h"
   48 #include "update_dialog.h"
   49 #include "updater.h"
   50 #include "view.h"
   51 #include "viewheader.h"
   52 #include "welcome_dialog.h"
   53 #include "window_state_manager.h"
   54 
   55 #ifdef __WXMSW__
   56 #include <wx/module.h>
   57 #endif
   58 #ifndef __WXMAC__
   59 #include <wx/taskbar.h>
   60 #else
   61 #include <wx/combobox.h>
   62 #endif
   63 
   64 #include <functional>
   65 #include <limits>
   66 #include <map>
   67 
   68 #ifdef __WXGTK__
   69 DECLARE_EVENT_TYPE(fzEVT_TASKBAR_CLICK_DELAYED, -1)
   70 DEFINE_EVENT_TYPE(fzEVT_TASKBAR_CLICK_DELAYED)
   71 #endif
   72 
   73 static int tab_hotkey_ids[10];
   74 
   75 #if FZ_MANUALUPDATECHECK
   76 static int GetAvailableUpdateMenuId()
   77 {
   78     static int updateAvailableMenuId = wxNewId();
   79     return updateAvailableMenuId;
   80 }
   81 #endif
   82 
   83 std::map<int, std::pair<std::function<void(wxTextEntry*)>, wxChar>> keyboardCommands;
   84 
   85 #ifdef __WXMAC__
   86 wxTextEntry* GetSpecialTextEntry(wxWindow* w, wxChar cmd)
   87 {
   88     if (cmd == 'A' || cmd == 'V') {
   89         wxTextCtrl* text = dynamic_cast<wxTextCtrl*>(w);
   90         if (text && text->GetWindowStyle() & wxTE_PASSWORD) {
   91             return text;
   92         }
   93     }
   94     return dynamic_cast<wxComboBox*>(w);
   95 }
   96 #else
   97 wxTextEntry* GetSpecialTextEntry(wxWindow*, wxChar)
   98 {
   99     return 0;
  100 }
  101 #endif
  102 
  103 bool HandleKeyboardCommand(wxCommandEvent& event, wxWindow& parent)
  104 {
  105     auto const& it = keyboardCommands.find(event.GetId());
  106     if (it == keyboardCommands.end()) {
  107         return false;
  108     }
  109 
  110     wxTextEntry* e = GetSpecialTextEntry(parent.FindFocus(), it->second.second);
  111     if (e) {
  112         it->second.first(e);
  113     }
  114     else {
  115         event.Skip();
  116     }
  117     return true;
  118 }
  119 
  120 BEGIN_EVENT_TABLE(CMainFrame, wxNavigationEnabled<wxFrame>)
  121     EVT_SIZE(CMainFrame::OnSize)
  122     EVT_MENU(wxID_ANY, CMainFrame::OnMenuHandler)
  123     EVT_COMMAND(wxID_ANY, fzEVT_UPDATE_LED_TOOLTIP, CMainFrame::OnUpdateLedTooltip)
  124     EVT_TOOL(XRCID("ID_TOOLBAR_DISCONNECT"), CMainFrame::OnDisconnect)
  125     EVT_MENU(XRCID("ID_MENU_SERVER_DISCONNECT"), CMainFrame::OnDisconnect)
  126     EVT_TOOL(XRCID("ID_TOOLBAR_CANCEL"), CMainFrame::OnCancel)
  127     EVT_MENU(XRCID("ID_CANCEL"), CMainFrame::OnCancel)
  128     EVT_TOOL(XRCID("ID_TOOLBAR_RECONNECT"), CMainFrame::OnReconnect)
  129     EVT_TOOL(XRCID("ID_MENU_SERVER_RECONNECT"), CMainFrame::OnReconnect)
  130     EVT_TOOL(XRCID("ID_TOOLBAR_REFRESH"), CMainFrame::OnRefresh)
  131     EVT_MENU(XRCID("ID_REFRESH"), CMainFrame::OnRefresh)
  132     EVT_TOOL(XRCID("ID_TOOLBAR_SITEMANAGER"), CMainFrame::OnSiteManager)
  133     EVT_CLOSE(CMainFrame::OnClose)
  134 #ifdef WITH_LIBDBUS
  135     EVT_END_SESSION(CMainFrame::OnClose)
  136 #endif
  137     EVT_TIMER(wxID_ANY, CMainFrame::OnTimer)
  138     EVT_TOOL(XRCID("ID_TOOLBAR_PROCESSQUEUE"), CMainFrame::OnProcessQueue)
  139     EVT_TOOL(XRCID("ID_TOOLBAR_LOGVIEW"), CMainFrame::OnToggleLogView)
  140     EVT_TOOL(XRCID("ID_TOOLBAR_LOCALTREEVIEW"), CMainFrame::OnToggleDirectoryTreeView)
  141     EVT_TOOL(XRCID("ID_TOOLBAR_REMOTETREEVIEW"), CMainFrame::OnToggleDirectoryTreeView)
  142     EVT_TOOL(XRCID("ID_TOOLBAR_QUEUEVIEW"), CMainFrame::OnToggleQueueView)
  143     EVT_MENU(XRCID("ID_VIEW_TOOLBAR"), CMainFrame::OnToggleToolBar)
  144     EVT_MENU(XRCID("ID_VIEW_MESSAGELOG"), CMainFrame::OnToggleLogView)
  145     EVT_MENU(XRCID("ID_VIEW_LOCALTREE"), CMainFrame::OnToggleDirectoryTreeView)
  146     EVT_MENU(XRCID("ID_VIEW_REMOTETREE"), CMainFrame::OnToggleDirectoryTreeView)
  147     EVT_MENU(XRCID("ID_VIEW_QUEUE"), CMainFrame::OnToggleQueueView)
  148     EVT_MENU(wxID_ABOUT, CMainFrame::OnMenuHelpAbout)
  149     EVT_TOOL(XRCID("ID_TOOLBAR_FILTER"), CMainFrame::OnFilter)
  150     EVT_TOOL_RCLICKED(XRCID("ID_TOOLBAR_FILTER"), CMainFrame::OnFilterRightclicked)
  151 #if FZ_MANUALUPDATECHECK
  152     EVT_MENU(XRCID("ID_CHECKFORUPDATES"), CMainFrame::OnCheckForUpdates)
  153     EVT_MENU(GetAvailableUpdateMenuId(), CMainFrame::OnCheckForUpdates)
  154 #endif //FZ_MANUALUPDATECHECK
  155     EVT_TOOL_RCLICKED(XRCID("ID_TOOLBAR_SITEMANAGER"), CMainFrame::OnSitemanagerDropdown)
  156 #ifdef EVT_TOOL_DROPDOWN
  157     EVT_TOOL_DROPDOWN(XRCID("ID_TOOLBAR_SITEMANAGER"), CMainFrame::OnSitemanagerDropdown)
  158 #endif
  159     EVT_NAVIGATION_KEY(CMainFrame::OnNavigationKeyEvent)
  160     EVT_CHAR_HOOK(CMainFrame::OnChar)
  161     EVT_MENU(XRCID("ID_MENU_VIEW_FILTERS"), CMainFrame::OnFilter)
  162     EVT_ACTIVATE(CMainFrame::OnActivate)
  163     EVT_TOOL(XRCID("ID_TOOLBAR_COMPARISON"), CMainFrame::OnToolbarComparison)
  164     EVT_TOOL_RCLICKED(XRCID("ID_TOOLBAR_COMPARISON"), CMainFrame::OnToolbarComparisonDropdown)
  165 #ifdef EVT_TOOL_DROPDOWN
  166     EVT_TOOL_DROPDOWN(XRCID("ID_TOOLBAR_COMPARISON"), CMainFrame::OnToolbarComparisonDropdown)
  167 #endif
  168     EVT_MENU(XRCID("ID_COMPARE_SIZE"), CMainFrame::OnDropdownComparisonMode)
  169     EVT_MENU(XRCID("ID_COMPARE_DATE"), CMainFrame::OnDropdownComparisonMode)
  170     EVT_MENU(XRCID("ID_COMPARE_HIDEIDENTICAL"), CMainFrame::OnDropdownComparisonHide)
  171     EVT_TOOL(XRCID("ID_TOOLBAR_SYNCHRONIZED_BROWSING"), CMainFrame::OnSyncBrowse)
  172 #ifdef __WXMAC__
  173     EVT_CHILD_FOCUS(CMainFrame::OnChildFocused)
  174 #else
  175     EVT_ICONIZE(CMainFrame::OnIconize)
  176 #endif
  177 #ifdef __WXGTK__
  178     EVT_COMMAND(wxID_ANY, fzEVT_TASKBAR_CLICK_DELAYED, CMainFrame::OnTaskBarClick_Delayed)
  179 #endif
  180     EVT_TOOL(XRCID("ID_TOOLBAR_FIND"), CMainFrame::OnSearch)
  181     EVT_MENU(XRCID("ID_MENU_SERVER_SEARCH"), CMainFrame::OnSearch)
  182     EVT_MENU(XRCID("ID_MENU_FILE_NEWTAB"), CMainFrame::OnMenuNewTab)
  183     EVT_MENU(XRCID("ID_MENU_FILE_CLOSETAB"), CMainFrame::OnMenuCloseTab)
  184 END_EVENT_TABLE()
  185 
  186 class CMainFrameStateEventHandler final : public CGlobalStateEventHandler
  187 {
  188 public:
  189     CMainFrameStateEventHandler(CMainFrame* pMainFrame)
  190     {
  191         m_pMainFrame = pMainFrame;
  192 
  193         CContextManager::Get()->RegisterHandler(this, STATECHANGE_REMOTE_IDLE, false);
  194         CContextManager::Get()->RegisterHandler(this, STATECHANGE_SERVER, false);
  195 
  196         CContextManager::Get()->RegisterHandler(this, STATECHANGE_CHANGEDCONTEXT, false);
  197     }
  198 
  199 protected:
  200     virtual void OnStateChange(CState* pState, t_statechange_notifications notification, std::wstring const&, const void*) override
  201     {
  202         if (notification == STATECHANGE_CHANGEDCONTEXT) {
  203             // Update window title
  204             if (!pState || !pState->GetSite()) {
  205                 m_pMainFrame->SetTitle(_T("FileZilla"));
  206             }
  207             else {
  208                 m_pMainFrame->SetTitle(pState->GetTitle() + _T(" - FileZilla"));
  209             }
  210 
  211             return;
  212         }
  213 
  214         if (!pState) {
  215             return;
  216         }
  217 
  218         if (!m_pMainFrame->m_pContextControl) {
  219             return;
  220         }
  221 
  222         CContextControl::_context_controls* controls = m_pMainFrame->m_pContextControl->GetControlsFromState(pState);
  223         if (!controls) {
  224             return;
  225         }
  226 
  227         if (!controls->used()) {
  228             if (notification == STATECHANGE_REMOTE_IDLE || notification == STATECHANGE_SERVER) {
  229                 pState->Disconnect();
  230             }
  231 
  232             return;
  233         }
  234 
  235         if (notification == STATECHANGE_SERVER) {
  236             if (pState == CContextManager::Get()->GetCurrentContext()) {
  237                 Site const& site = pState->GetSite();
  238                 if (!site) {
  239                     m_pMainFrame->SetTitle(_T("FileZilla"));
  240                 }
  241                 else {
  242                     m_pMainFrame->SetTitle(pState->GetTitle() + _T(" - FileZilla"));
  243                 }
  244             }
  245 
  246             return;
  247         }
  248     }
  249 
  250     CMainFrame* m_pMainFrame;
  251 };
  252 
  253 
  254 /*#include "overlay.h"
  255 #include <wx/hyperlink.h>
  256 
  257 namespace {
  258 void ShowStorjOverlay(wxWindow* parent, wxWindow* anchor, wxPoint const& offset)
  259 {
  260     auto p = new OverlayWindow(parent);
  261 
  262     auto box = new wxBoxSizer(wxVERTICAL);
  263     auto sizer = layout::createFlex(1);
  264     box->Add(sizer, 0, wxALL, 7);
  265 
  266     p->SetSizer(box);
  267 
  268     auto title = new wxStaticText(p, -1, _("Did you know about this new FileZilla feature?"));
  269 
  270     wxFont f = title->GetFont();
  271     f.SetWeight(wxBOLD);
  272     title->SetFont(f);
  273     sizer->Add(title);
  274 
  275     sizer->AddSpacer(0);
  276 
  277     sizer->Add(new wxStaticText(p, -1, _("You can use FileZilla to store your files securely in the Storj decentralized cloud.")));
  278     sizer->Add(new wxHyperlinkCtrl(p, -1, _("Click to learn more"), L"https://wiki.filezilla-project.org/Storj"));
  279 
  280     sizer->AddSpacer(10);
  281 
  282     //sizer->Add(new wxCheckBox(p, -1, L"Don't show this again"), 0, wxALIGN_RIGHT);
  283 
  284     auto dismiss = new wxButton(p, -1, L"Dismiss");
  285     dismiss->Bind(wxEVT_BUTTON, [p](wxCommandEvent&) { p->Destroy(); });
  286     sizer->Add(dismiss, 0, wxALIGN_RIGHT);
  287 
  288     wxGetApp().GetWrapEngine()->WrapRecursive(p, 3);
  289 
  290     box->Fit(p);
  291 
  292     p->SetAnchor(anchor, offset);
  293 }
  294 }*/
  295 
  296 CMainFrame::CMainFrame()
  297     : m_engineContext(*COptions::Get(), CustomEncodingConverter::Get())
  298     , m_comparisonToggleAcceleratorId(wxNewId())
  299 {
  300     m_pActivityLed[0] = m_pActivityLed[1] = 0;
  301 
  302     wxGetApp().AddStartupProfileRecord("CMainFrame::CMainFrame");
  303     wxRect screen_size = CWindowStateManager::GetScreenDimensions();
  304 
  305     wxSize initial_size;
  306     initial_size.x = wxMin(1200, screen_size.GetWidth() - 10);
  307     initial_size.y = wxMin(950, screen_size.GetHeight() - 50);
  308 
  309     Create(NULL, -1, _T("FileZilla"), wxDefaultPosition, initial_size);
  310     SetSizeHints(700, 500);
  311 
  312 #ifdef __WXMSW__
  313     // In order for the --close commandline argument to work,
  314     // there has to be a way to find other instances.
  315     // Create a hidden window with a title no other program uses
  316     wxWindow* pChild = new wxWindow();
  317     pChild->Hide();
  318     pChild->Create(this, wxID_ANY);
  319     ::SetWindowText((HWND)pChild->GetHandle(), _T("FileZilla process identificator 3919DB0A-082D-4560-8E2F-381A35969FB4"));
  320 #endif
  321 
  322 #ifdef __WXMSW__
  323     SetIcon(wxICON(appicon));
  324 #else
  325     SetIcons(CThemeProvider::GetIconBundle(_T("ART_FILEZILLA")));
  326 #endif
  327 
  328     CPowerManagement::Create(this);
  329 
  330     // It's important that the context control gets created before our own state handler
  331     // so that contextchange events can be processed in the right order.
  332     m_pContextControl = new CContextControl(*this);
  333 
  334     m_pStatusBar = new CStatusBar(this);
  335     if (m_pStatusBar) {
  336         m_pActivityLed[0] = new CLed(m_pStatusBar, 0);
  337         m_pActivityLed[1] = new CLed(m_pStatusBar, 1);
  338 
  339         m_pStatusBar->AddField(-1, widget_led_recv, m_pActivityLed[1]);
  340         m_pStatusBar->AddField(-1, widget_led_send, m_pActivityLed[0]);
  341 
  342         SetStatusBar(m_pStatusBar);
  343     }
  344 
  345     m_closeEventTimer.SetOwner(this);
  346 
  347     if (CFilterManager::HasActiveFilters(true)) {
  348         if (COptions::Get()->GetOptionVal(OPTION_FILTERTOGGLESTATE)) {
  349             CFilterManager::ToggleFilters();
  350         }
  351     }
  352 
  353     CreateMenus();
  354     CreateMainToolBar();
  355     if (COptions::Get()->GetOptionVal(OPTION_SHOW_QUICKCONNECT)) {
  356         CreateQuickconnectBar();
  357     }
  358 
  359     m_pAsyncRequestQueue = new CAsyncRequestQueue(this);
  360 
  361 #ifdef __WXMSW__
  362     long style = wxSP_NOBORDER | wxSP_LIVE_UPDATE;
  363 #elif !defined(__WXMAC__)
  364     long style = wxSP_3DBORDER | wxSP_LIVE_UPDATE;
  365 #else
  366     long style = wxSP_LIVE_UPDATE;
  367 #endif
  368 
  369     wxSize clientSize = GetClientSize();
  370 
  371     m_pTopSplitter = new CSplitterWindowEx(this, -1, wxDefaultPosition, clientSize, style);
  372     m_pTopSplitter->SetMinimumPaneSize(50);
  373 
  374     m_pBottomSplitter = new CSplitterWindowEx(m_pTopSplitter, -1, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE);
  375     m_pBottomSplitter->SetMinimumPaneSize(20, 70);
  376     m_pBottomSplitter->SetSashGravity(1.0);
  377 
  378     const int message_log_position = COptions::Get()->GetOptionVal(OPTION_MESSAGELOG_POSITION);
  379     m_pQueueLogSplitter = new CSplitterWindowEx(m_pBottomSplitter, -1, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE);
  380     m_pQueueLogSplitter->SetMinimumPaneSize(50, 250);
  381     m_pQueueLogSplitter->SetSashGravity(0.5);
  382     m_pQueuePane = new CQueue(m_pQueueLogSplitter, this, m_pAsyncRequestQueue);
  383 
  384     if (message_log_position == 1) {
  385         m_pStatusView = new CStatusView(m_pQueueLogSplitter, -1);
  386     }
  387     else {
  388         m_pStatusView = new CStatusView(m_pTopSplitter, -1);
  389     }
  390 
  391     m_pQueueView = m_pQueuePane->GetQueueView();
  392 
  393     m_pContextControl->Create(m_pBottomSplitter);
  394 
  395     m_pStateEventHandler = new CMainFrameStateEventHandler(this);
  396 
  397     m_pContextControl->RestoreTabs();
  398 
  399     switch (message_log_position) {
  400     case 1:
  401         m_pTopSplitter->Initialize(m_pBottomSplitter);
  402         if (COptions::Get()->GetOptionVal(OPTION_SHOW_MESSAGELOG)) {
  403             if (COptions::Get()->GetOptionVal(OPTION_SHOW_QUEUE)) {
  404                 m_pQueueLogSplitter->SplitVertically(m_pQueuePane, m_pStatusView);
  405             }
  406             else {
  407                 m_pQueueLogSplitter->Initialize(m_pStatusView);
  408                 m_pQueuePane->Hide();
  409             }
  410         }
  411         else {
  412             if (COptions::Get()->GetOptionVal(OPTION_SHOW_QUEUE)) {
  413                 m_pStatusView->Hide();
  414                 m_pQueueLogSplitter->Initialize(m_pQueuePane);
  415             }
  416             else {
  417                 m_pQueuePane->Hide();
  418                 m_pStatusView->Hide();
  419                 m_pQueueLogSplitter->Hide();
  420             }
  421         }
  422         break;
  423     case 2:
  424         m_pTopSplitter->Initialize(m_pBottomSplitter);
  425         if (COptions::Get()->GetOptionVal(OPTION_SHOW_QUEUE)) {
  426             m_pQueueLogSplitter->Initialize(m_pQueuePane);
  427         }
  428         else {
  429             m_pQueueLogSplitter->Hide();
  430             m_pQueuePane->Hide();
  431         }
  432         m_pQueuePane->AddPage(m_pStatusView, _("Message log"));
  433         break;
  434     default:
  435         if (COptions::Get()->GetOptionVal(OPTION_SHOW_QUEUE)) {
  436             m_pQueueLogSplitter->Initialize(m_pQueuePane);
  437         }
  438         else {
  439             m_pQueuePane->Hide();
  440             m_pQueueLogSplitter->Hide();
  441         }
  442         if (COptions::Get()->GetOptionVal(OPTION_SHOW_MESSAGELOG)) {
  443             m_pTopSplitter->SplitHorizontally(m_pStatusView, m_pBottomSplitter);
  444         }
  445         else {
  446             m_pStatusView->Hide();
  447             m_pTopSplitter->Initialize(m_pBottomSplitter);
  448         }
  449         break;
  450     }
  451 
  452     if (m_pQueueLogSplitter->IsShown()) {
  453         m_pBottomSplitter->SplitHorizontally(m_pContextControl, m_pQueueLogSplitter);
  454     }
  455     else {
  456         m_pQueueLogSplitter->Hide();
  457         m_pBottomSplitter->Initialize(m_pContextControl);
  458     }
  459 
  460     wxGetApp().AddStartupProfileRecord("CMainFrame::CMainFrame pre layout");
  461     m_pWindowStateManager = new CWindowStateManager(this);
  462     m_pWindowStateManager->Restore(OPTION_MAINWINDOW_POSITION);
  463 
  464     Layout();
  465     HandleResize();
  466 
  467     if (!RestoreSplitterPositions()) {
  468         SetDefaultSplitterPositions();
  469     }
  470 
  471     SetupKeyboardAccelerators();
  472 
  473     ConnectNavigationHandler(m_pStatusView);
  474     ConnectNavigationHandler(m_pQueuePane);
  475 
  476     CEditHandler::Create()->SetQueue(m_pQueueView);
  477 
  478     CAutoAsciiFiles::SettingsChanged();
  479 
  480     FixTabOrder();
  481 
  482     RegisterOption(OPTION_ICONS_THEME);
  483     RegisterOption(OPTION_ICONS_SCALE);
  484     RegisterOption(OPTION_MESSAGELOG_POSITION);
  485     RegisterOption(OPTION_FILEPANE_LAYOUT);
  486     RegisterOption(OPTION_FILEPANE_SWAP);
  487 }
  488 
  489 CMainFrame::~CMainFrame()
  490 {
  491     UnregisterAllOptions();
  492 
  493     CPowerManagement::Destroy();
  494 
  495     delete m_pStateEventHandler;
  496 
  497     delete m_pContextControl;
  498     m_pContextControl = 0;
  499 
  500     CContextManager::Get()->DestroyAllStates();
  501     delete m_pAsyncRequestQueue;
  502 #if FZ_MANUALUPDATECHECK
  503     delete m_pUpdater;
  504 #endif
  505 
  506     CEditHandler* pEditHandler = CEditHandler::Get();
  507     if (pEditHandler) {
  508         // This might leave temporary files behind,
  509         // edit handler should clean them on next startup
  510         pEditHandler->Release();
  511     }
  512 
  513 #ifndef __WXMAC__
  514     delete m_taskBarIcon;
  515 #endif
  516 }
  517 
  518 void CMainFrame::HandleResize()
  519 {
  520     wxSize clientSize = GetClientSize();
  521     if (clientSize.y <= 0) { // Can happen if restoring from tray on XP if using ugly XP themes
  522         return;
  523     }
  524 
  525     if (m_pQuickconnectBar) {
  526         m_pQuickconnectBar->SetSize(0, 0, clientSize.GetWidth(), -1, wxSIZE_USE_EXISTING);
  527     }
  528     if (m_pTopSplitter) {
  529         if (!m_pQuickconnectBar) {
  530             m_pTopSplitter->SetSize(0, 0, clientSize.GetWidth(), clientSize.GetHeight());
  531         }
  532         else {
  533             wxSize panelSize = m_pQuickconnectBar->GetSize();
  534             m_pTopSplitter->SetSize(0, panelSize.GetHeight(), clientSize.GetWidth(), clientSize.GetHeight() - panelSize.GetHeight());
  535         }
  536     }
  537 }
  538 
  539 void CMainFrame::OnSize(wxSizeEvent &event)
  540 {
  541     wxFrame::OnSize(event);
  542 
  543     if (!m_pBottomSplitter) {
  544         return;
  545     }
  546 
  547     HandleResize();
  548 
  549 #ifdef __WXGTK__
  550     if (m_pWindowStateManager && m_pWindowStateManager->m_maximize_requested && IsMaximized()) {
  551         m_pWindowStateManager->m_maximize_requested = 0;
  552         if (!RestoreSplitterPositions()) {
  553             SetDefaultSplitterPositions();
  554         }
  555     }
  556 #endif
  557 }
  558 
  559 bool CMainFrame::CreateMenus()
  560 {
  561     wxGetApp().AddStartupProfileRecord("CMainFrame::CreateMenus");
  562     CMenuBar* old = m_pMenuBar;
  563 
  564     m_pMenuBar = CMenuBar::Load(this);
  565 
  566     if (!m_pMenuBar) {
  567         m_pMenuBar = old;
  568         return false;
  569     }
  570 
  571     SetMenuBar(m_pMenuBar);
  572     delete old;
  573 
  574     return true;
  575 }
  576 
  577 bool CMainFrame::CreateQuickconnectBar()
  578 {
  579     wxGetApp().AddStartupProfileRecord("CMainFrame::CreateQuickconnectBar");
  580     delete m_pQuickconnectBar;
  581 
  582     m_pQuickconnectBar = new CQuickconnectBar();
  583     if (!m_pQuickconnectBar->Create(this)) {
  584         delete m_pQuickconnectBar;
  585         m_pQuickconnectBar = 0;
  586     }
  587     else {
  588         wxSize clientSize = GetClientSize();
  589         if (m_pTopSplitter) {
  590             wxSize panelSize = m_pQuickconnectBar->GetSize();
  591             m_pTopSplitter->SetSize(-1, panelSize.GetHeight(), -1, clientSize.GetHeight() - panelSize.GetHeight(), wxSIZE_USE_EXISTING);
  592         }
  593         m_pQuickconnectBar->SetSize(0, 0, clientSize.GetWidth(), -1);
  594     }
  595 
  596     return true;
  597 }
  598 
  599 void CMainFrame::OnMenuHandler(wxCommandEvent &event)
  600 {
  601     if (event.GetId() == XRCID("wxID_EXIT")) {
  602         Close();
  603     }
  604     else if (event.GetId() == XRCID("ID_MENU_FILE_SITEMANAGER")) {
  605         OpenSiteManager();
  606     }
  607     else if (event.GetId() == XRCID("ID_MENU_FILE_COPYSITEMANAGER")) {
  608         Site site;
  609         CState* pState = CContextManager::Get()->GetCurrentContext();
  610         if (pState) {
  611             site = pState->GetSite();
  612         }
  613         if (!site) {
  614             wxMessageBoxEx(_("Not connected to any server."), _("Cannot add server to Site Manager"), wxICON_EXCLAMATION);
  615             return;
  616         }
  617         OpenSiteManager(&site);
  618     }
  619     else if (event.GetId() == XRCID("ID_MENU_SERVER_CMD")) {
  620         CState* pState = CContextManager::Get()->GetCurrentContext();
  621         if (!pState || !pState->m_pCommandQueue || !pState->IsRemoteConnected() || !pState->IsRemoteIdle()) {
  622             return;
  623         }
  624 
  625         CInputDialog dlg;
  626         dlg.Create(this, _("Enter custom command"), _("Please enter raw FTP command.\nUsing raw ftp commands will clear the directory cache."));
  627         if (dlg.ShowModal() != wxID_OK) {
  628             return;
  629         }
  630 
  631         pState = CContextManager::Get()->GetCurrentContext();
  632         if (!pState || !pState->m_pCommandQueue || !pState->IsRemoteConnected() || !pState->IsRemoteIdle()) {
  633             wxBell();
  634             return;
  635         }
  636 
  637         const wxString &command = dlg.GetValue();
  638 
  639         if (!command.Left(5).CmpNoCase(_T("quote")) || !command.Left(6).CmpNoCase(_T("quote "))) {
  640             CConditionalDialog condDlg(this, CConditionalDialog::rawcommand_quote, CConditionalDialog::yesno);
  641             condDlg.SetTitle(_("Raw FTP command"));
  642 
  643             condDlg.AddText(_("'quote' is usually a local command used by commandline clients to send the arguments following 'quote' to the server. You might want to enter the raw command without the leading 'quote'."));
  644             condDlg.AddText(wxString::Format(_("Do you really want to send '%s' to the server?"), command));
  645 
  646             if (!condDlg.Run()) {
  647                 return;
  648             }
  649         }
  650 
  651         pState = CContextManager::Get()->GetCurrentContext();
  652         if (!pState || !pState->m_pCommandQueue || !pState->IsRemoteConnected() || !pState->IsRemoteIdle()) {
  653             wxBell();
  654             return;
  655         }
  656         pState->m_pCommandQueue->ProcessCommand(new CRawCommand(dlg.GetValue().ToStdWstring()));
  657     }
  658     else if (event.GetId() == XRCID("wxID_PREFERENCES")) {
  659         OnMenuEditSettings(event);
  660     }
  661     else if (event.GetId() == XRCID("ID_MENU_EDIT_NETCONFWIZARD")) {
  662         CNetConfWizard wizard(this, COptions::Get(), m_engineContext);
  663         wizard.Load();
  664         wizard.Run();
  665     }
  666     // Debug menu
  667     else if (event.GetId() == XRCID("ID_CIPHERS")) {
  668         CInputDialog dlg;
  669         dlg.Create(this, _T("Ciphers"), _T("Priority string:"));
  670         dlg.AllowEmpty(true);
  671         if (dlg.ShowModal() == wxID_OK) {
  672             std::string ciphers = ListTlsCiphers(fz::to_string(dlg.GetValue().ToStdWstring()));
  673             wxMessageBoxEx(fz::to_wstring(ciphers), _T("Ciphers"));
  674         }
  675     }
  676     else if (event.GetId() == XRCID("ID_CLEARCACHE_LAYOUT")) {
  677         CWrapEngine::ClearCache();
  678     }
  679     else if (event.GetId() == XRCID("ID_CLEAR_UPDATER")) {
  680 #if FZ_MANUALUPDATECHECK
  681         if (m_pUpdater) {
  682             COptions::Get()->SetOption(OPTION_UPDATECHECK_LASTDATE, std::wstring());
  683             COptions::Get()->SetOption(OPTION_UPDATECHECK_NEWVERSION, std::wstring());
  684             m_pUpdater->Init();
  685         }
  686 #endif
  687     }
  688     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_FILEEXISTS")) {
  689         CDefaultFileExistsDlg dlg;
  690         if (!dlg.Load(this, false)) {
  691             return;
  692         }
  693 
  694         dlg.Run();
  695     }
  696     else if (event.GetId() == XRCID("ID_MENU_EDIT_CLEARPRIVATEDATA")) {
  697         CClearPrivateDataDialog* pDlg = CClearPrivateDataDialog::Create(this);
  698         if (!pDlg) {
  699             return;
  700         }
  701 
  702         pDlg->Run();
  703         pDlg->Delete();
  704 
  705         if (m_pMenuBar) {
  706             m_pMenuBar->UpdateMenubarState();
  707         }
  708         if (m_pToolBar) {
  709             m_pToolBar->UpdateToolbarState();
  710         }
  711     }
  712     else if (event.GetId() == XRCID("ID_MENU_SERVER_VIEWHIDDEN")) {
  713         bool showHidden = COptions::Get()->GetOptionVal(OPTION_VIEW_HIDDEN_FILES) ? 0 : 1;
  714         if (showHidden) {
  715             CConditionalDialog dlg(this, CConditionalDialog::viewhidden, CConditionalDialog::ok, false);
  716             dlg.SetTitle(_("Force showing hidden files"));
  717 
  718             dlg.AddText(_("Note that this feature is only supported using the FTP protocol."));
  719             dlg.AddText(_("A proper server always shows all files, but some broken servers hide files from the user. Use this option to force the server to show all files."));
  720             dlg.AddText(_("Keep in mind that not all servers support this feature and may return incorrect listings if this option is enabled. Although FileZilla performs some tests to check if the server supports this feature, the test may fail."));
  721             dlg.AddText(_("Disable this option again if you will not be able to see the correct directory contents anymore."));
  722             (void)dlg.Run();
  723         }
  724 
  725         COptions::Get()->SetOption(OPTION_VIEW_HIDDEN_FILES, showHidden ? 1 : 0);
  726         const std::vector<CState*> *pStates = CContextManager::Get()->GetAllStates();
  727         for (auto & pState : *pStates) {
  728             CServerPath path = pState->GetRemotePath();
  729             if (!path.empty() && pState->m_pCommandQueue) {
  730                 pState->ChangeRemoteDir(path, std::wstring(), LIST_FLAG_REFRESH);
  731             }
  732         }
  733     }
  734     else if (event.GetId() == XRCID("ID_EXPORT")) {
  735         CExportDialog dlg(this, m_pQueueView);
  736         dlg.Run();
  737     }
  738     else if (event.GetId() == XRCID("ID_IMPORT")) {
  739         CImportDialog dlg(this, m_pQueueView);
  740         dlg.Run();
  741     }
  742     else if (event.GetId() == XRCID("ID_MENU_FILE_EDITED")) {
  743         CEditHandlerStatusDialog dlg(this);
  744         dlg.ShowModal();
  745     }
  746     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_TYPE_AUTO")) {
  747         COptions::Get()->SetOption(OPTION_ASCIIBINARY, 0);
  748     }
  749     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_TYPE_ASCII")) {
  750         COptions::Get()->SetOption(OPTION_ASCIIBINARY, 1);
  751     }
  752     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_TYPE_BINARY")) {
  753         COptions::Get()->SetOption(OPTION_ASCIIBINARY, 2);
  754     }
  755     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_PRESERVETIMES")) {
  756         if (event.IsChecked()) {
  757             CConditionalDialog dlg(this, CConditionalDialog::confirm_preserve_timestamps, CConditionalDialog::ok, true);
  758             dlg.SetTitle(_("Preserving file timestamps"));
  759             dlg.AddText(_("Please note that preserving timestamps on uploads on FTP, FTPS and FTPES servers only works if they support the MFMT command."));
  760             dlg.Run();
  761         }
  762         COptions::Get()->SetOption(OPTION_PRESERVE_TIMESTAMPS, event.IsChecked() ? 1 : 0);
  763     }
  764     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_PROCESSQUEUE")) {
  765         if (m_pQueueView) {
  766             m_pQueueView->SetActive(event.IsChecked());
  767         }
  768     }
  769     else if (event.GetId() == XRCID("ID_MENU_HELP_GETTINGHELP") ||
  770              event.GetId() == XRCID("ID_MENU_HELP_BUGREPORT"))
  771     {
  772         wxString url(_T("https://filezilla-project.org/support.php?type=client&mode="));
  773         if (event.GetId() == XRCID("ID_MENU_HELP_GETTINGHELP")) {
  774             url += _T("help");
  775         }
  776         else {
  777             url += _T("bugreport");
  778         }
  779         wxString version = CBuildInfo::GetVersion();
  780         if (version != _T("custom build")) {
  781             url += _T("&version=");
  782             // We need to urlencode version number
  783 
  784             // Unbelievable, but wxWidgets does not have any method
  785             // to urlencode strings.
  786             // Do a crude approach: Drop everything unexpected...
  787             for (unsigned int i = 0; i < version.Len(); i++) {
  788                 wxChar c = version.GetChar(i);
  789                 if ((c >= '0' && c <= '9') ||
  790                     (c >= 'a' && c <= 'z') ||
  791                     (c >= 'A' && c <= 'Z') ||
  792                     c == '-' || c == '.' ||
  793                     c == '_')
  794                 {
  795                     url.Append(c);
  796                 }
  797             }
  798         }
  799         wxLaunchDefaultBrowser(url);
  800     }
  801     else if (event.GetId() == XRCID("ID_MENU_VIEW_FILELISTSTATUSBAR")) {
  802         bool show = COptions::Get()->GetOptionVal(OPTION_FILELIST_STATUSBAR) == 0;
  803         COptions::Get()->SetOption(OPTION_FILELIST_STATUSBAR, show ? 1 : 0);
  804         CContextControl::_context_controls* controls = m_pContextControl ? m_pContextControl->GetCurrentControls() : 0;
  805         if (controls && controls->pLocalListViewPanel) {
  806             wxStatusBar* pStatusBar = controls->pLocalListViewPanel->GetStatusBar();
  807             if (pStatusBar) {
  808                 pStatusBar->Show(show);
  809                 wxSizeEvent evt;
  810                 controls->pLocalListViewPanel->ProcessWindowEvent(evt);
  811             }
  812         }
  813         if (controls && controls->pRemoteListViewPanel) {
  814             wxStatusBar* pStatusBar = controls->pRemoteListViewPanel->GetStatusBar();
  815             if (pStatusBar) {
  816                 pStatusBar->Show(show);
  817                 wxSizeEvent evt;
  818                 controls->pRemoteListViewPanel->ProcessWindowEvent(evt);
  819             }
  820         }
  821     }
  822     else if (event.GetId() == XRCID("ID_VIEW_QUICKCONNECT")) {
  823         if (!m_pQuickconnectBar) {
  824             CreateQuickconnectBar();
  825         }
  826         else {
  827             m_pQuickconnectBar->Destroy();
  828             m_pQuickconnectBar = 0;
  829             wxSize clientSize = GetClientSize();
  830             m_pTopSplitter->SetSize(0, 0, clientSize.GetWidth(), clientSize.GetHeight());
  831         }
  832         COptions::Get()->SetOption(OPTION_SHOW_QUICKCONNECT, m_pQuickconnectBar != 0);
  833     }
  834     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_MANUAL")) {
  835         CState* pState = CContextManager::Get()->GetCurrentContext();
  836         if (!pState || !m_pQueueView) {
  837             wxBell();
  838             return;
  839         }
  840         CManualTransfer dlg(m_pQueueView);
  841         dlg.Run(this, pState);
  842     }
  843     else if (event.GetId() == XRCID("ID_BOOKMARK_ADD") || event.GetId() == XRCID("ID_BOOKMARK_MANAGE")) {
  844         CState* pState = CContextManager::Get()->GetCurrentContext();
  845         if (!pState) {
  846             return;
  847         }
  848 
  849         Site const old_site = pState->GetSite();
  850         std::wstring sitePath = old_site.SitePath();
  851 
  852         CContextControl::_context_controls* controls = m_pContextControl->GetCurrentControls();
  853         if (!controls) {
  854             return;
  855         }
  856 
  857         // controls->last_bookmark_path can get modified if it's empty now
  858         int res;
  859         if (event.GetId() == XRCID("ID_BOOKMARK_ADD")) {
  860             CNewBookmarkDialog dlg(this, sitePath, old_site ? &old_site : 0);
  861             res = dlg.Run(pState->GetLocalDir().GetPath(), pState->GetRemotePath());
  862         }
  863         else {
  864             CBookmarksDialog dlg(this, sitePath, old_site ? &old_site : 0);
  865             res = dlg.Run();
  866         }
  867         if (res == wxID_OK) {
  868             if (!sitePath.empty()) {
  869                 std::unique_ptr<Site> site = CSiteManager::GetSiteByPath(sitePath, false).first;
  870                 if (site) {
  871                     for (int i = 0; i < m_pContextControl->GetTabCount(); ++i) {
  872                         CContextControl::_context_controls *tab_controls = m_pContextControl->GetControlsFromTabIndex(i);
  873                         if (tab_controls) {
  874                             tab_controls->pState->UpdateSite(old_site.SitePath(), *site);
  875                         }
  876                     }
  877                 }
  878             }
  879         }
  880     }
  881     else if (event.GetId() == XRCID("ID_MENU_HELP_WELCOME")) {
  882         CWelcomeDialog dlg;
  883         dlg.Run(this, true);
  884     }
  885     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_SPEEDLIMITS_ENABLE")) {
  886         bool enable = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_ENABLE) == 0;
  887 
  888         const int downloadLimit = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_INBOUND);
  889         const int uploadLimit = COptions::Get()->GetOptionVal(OPTION_SPEEDLIMIT_OUTBOUND);
  890         if (enable && !downloadLimit && !uploadLimit) {
  891             CSpeedLimitsDialog dlg;
  892             dlg.Run(this);
  893         }
  894         else {
  895             COptions::Get()->SetOption(OPTION_SPEEDLIMIT_ENABLE, enable ? 1 : 0);
  896         }
  897     }
  898     else if (event.GetId() == XRCID("ID_MENU_TRANSFER_SPEEDLIMITS_CONFIGURE")) {
  899         CSpeedLimitsDialog dlg;
  900         dlg.Run(this);
  901     }
  902     else if (event.GetId() == m_comparisonToggleAcceleratorId) {
  903         CState* pState = CContextManager::Get()->GetCurrentContext();
  904         if (!pState) {
  905             return;
  906         }
  907 
  908         int old_mode = COptions::Get()->GetOptionVal(OPTION_COMPARISONMODE);
  909         COptions::Get()->SetOption(OPTION_COMPARISONMODE, old_mode ? 0 : 1);
  910 
  911         CComparisonManager* pComparisonManager = pState->GetComparisonManager();
  912         if (pComparisonManager && pComparisonManager->IsComparing()) {
  913             pComparisonManager->CompareListings();
  914         }
  915     }
  916     else if (HandleKeyboardCommand(event, *this)) {
  917         return;
  918     }
  919     else {
  920         for (int i = 0; i < 10; ++i) {
  921             if (event.GetId() != tab_hotkey_ids[i]) {
  922                 continue;
  923             }
  924 
  925             if (!m_pContextControl) {
  926                 return;
  927             }
  928 
  929             int sel = i - 1;
  930             if (sel < 0) {
  931                 sel = 9;
  932             }
  933             m_pContextControl->SelectTab(sel);
  934 
  935             return;
  936         }
  937 
  938         std::unique_ptr<Site> pData = CSiteManager::GetSiteById(event.GetId());
  939 
  940         if (!pData) {
  941             event.Skip();
  942         }
  943         else {
  944             ConnectToSite(*pData, pData->m_default_bookmark);
  945         }
  946     }
  947 }
  948 
  949 void CMainFrame::OnEngineEvent(CFileZillaEngine* engine)
  950 {
  951     CallAfter(&CMainFrame::DoOnEngineEvent, engine);
  952 }
  953 
  954 void CMainFrame::DoOnEngineEvent(CFileZillaEngine* engine)
  955 {
  956     const std::vector<CState*> *pStates = CContextManager::Get()->GetAllStates();
  957     CState* pState = 0;
  958     for (std::vector<CState*>::const_iterator iter = pStates->begin(); iter != pStates->end(); ++iter) {
  959         if ((*iter)->m_pEngine != engine) {
  960             continue;
  961         }
  962 
  963         pState = *iter;
  964         break;
  965     }
  966     if (!pState) {
  967         return;
  968     }
  969 
  970     std::unique_ptr<CNotification> pNotification = pState->m_pEngine->GetNextNotification();
  971     while (pNotification) {
  972         switch (pNotification->GetID())
  973         {
  974         case nId_logmsg:
  975             if (m_pStatusView) {
  976                 m_pStatusView->AddToLog(std::move(static_cast<CLogmsgNotification&>(*pNotification.get())));
  977             }
  978             if (COptions::Get()->GetOptionVal(OPTION_MESSAGELOG_POSITION) == 2 && m_pQueuePane) {
  979                 m_pQueuePane->Highlight(3);
  980             }
  981             break;
  982         case nId_operation:
  983             if (pState->m_pCommandQueue) {
  984                 pState->m_pCommandQueue->Finish(unique_static_cast<COperationNotification>(std::move(pNotification)));
  985             }
  986             if (m_bQuit) {
  987                 Close();
  988                 return;
  989             }
  990             break;
  991         case nId_listing:
  992             {
  993                 auto const& listingNotification = static_cast<CDirectoryListingNotification const&>(*pNotification.get());
  994                 if (pState->m_pCommandQueue) {
  995                     pState->m_pCommandQueue->ProcessDirectoryListing(listingNotification);
  996                 }
  997             }
  998             break;
  999         case nId_asyncrequest:
 1000             {
 1001                 auto pAsyncRequest = unique_static_cast<CAsyncRequestNotification>(std::move(pNotification));
 1002                 if (pAsyncRequest->GetRequestID() == reqId_fileexists) {
 1003                     if (m_pQueueView) {
 1004                         m_pQueueView->ProcessNotification(pState->m_pEngine, std::move(pAsyncRequest));
 1005                     }
 1006                 }
 1007                 else {
 1008                     if (pAsyncRequest->GetRequestID() == reqId_certificate) {
 1009                         pState->SetSecurityInfo(static_cast<CCertificateNotification&>(*pAsyncRequest));
 1010                     }
 1011                     if (m_pAsyncRequestQueue) {
 1012                         m_pAsyncRequestQueue->AddRequest(pState->m_pEngine, std::move(pAsyncRequest));
 1013                     }
 1014                 }
 1015             }
 1016             break;
 1017         case nId_active:
 1018             {
 1019                 CActiveNotification const& activeNotification = static_cast<CActiveNotification const&>(*pNotification.get());
 1020                 UpdateActivityLed(activeNotification.GetDirection());
 1021             }
 1022             break;
 1023         case nId_transferstatus:
 1024             if (m_pQueueView) {
 1025                 m_pQueueView->ProcessNotification(pState->m_pEngine, std::move(pNotification));
 1026             }
 1027             break;
 1028         case nId_sftp_encryption:
 1029             {
 1030                 pState->SetSecurityInfo(static_cast<CSftpEncryptionNotification&>(*pNotification));
 1031             }
 1032             break;
 1033         case nId_local_dir_created:
 1034             if (pState) {
 1035                 auto const& localDirCreatedNotification = static_cast<CLocalDirCreatedNotification const&>(*pNotification.get());
 1036                 pState->LocalDirCreated(localDirCreatedNotification.dir);
 1037             }
 1038             break;
 1039         case nId_serverchange:
 1040             if (pState) {
 1041                 auto const& notification = static_cast<ServerChangeNotification const&>(*pNotification.get());
 1042                 pState->ChangeServer(notification.newServer_);
 1043             }
 1044             break;
 1045         default:
 1046             break;
 1047         }
 1048 
 1049         pNotification = pState->m_pEngine->GetNextNotification();
 1050     }
 1051 }
 1052 
 1053 void CMainFrame::OnUpdateLedTooltip(wxCommandEvent&)
 1054 {
 1055     wxString tooltipText;
 1056 
 1057     wxFileOffset downloadSpeed = m_pQueueView ? m_pQueueView->GetCurrentDownloadSpeed() : 0;
 1058     wxFileOffset uploadSpeed = m_pQueueView ? m_pQueueView->GetCurrentUploadSpeed() : 0;
 1059 
 1060     CSizeFormat::_format format = static_cast<CSizeFormat::_format>(COptions::Get()->GetOptionVal(OPTION_SIZE_FORMAT));
 1061     if (format == CSizeFormat::bytes) {
 1062         format = CSizeFormat::iec;
 1063     }
 1064 
 1065     const wxString downloadSpeedStr = CSizeFormat::Format(downloadSpeed, true, format,
 1066                                                           COptions::Get()->GetOptionVal(OPTION_SIZE_USETHOUSANDSEP) != 0,
 1067                                                           COptions::Get()->GetOptionVal(OPTION_SIZE_DECIMALPLACES));
 1068     const wxString uploadSpeedStr = CSizeFormat::Format(uploadSpeed, true, format,
 1069                                                         COptions::Get()->GetOptionVal(OPTION_SIZE_USETHOUSANDSEP) != 0,
 1070                                                         COptions::Get()->GetOptionVal(OPTION_SIZE_DECIMALPLACES));
 1071     tooltipText.Printf(_("Download speed: %s/s\nUpload speed: %s/s"), downloadSpeedStr, uploadSpeedStr);
 1072 
 1073     m_pActivityLed[0]->SetToolTip(tooltipText);
 1074     m_pActivityLed[1]->SetToolTip(tooltipText);
 1075 }
 1076 
 1077 bool CMainFrame::CreateMainToolBar()
 1078 {
 1079     wxGetApp().AddStartupProfileRecord("CMainFrame::CreateMainToolBar");
 1080     if (m_pToolBar) {
 1081 #ifdef __WXMAC__
 1082         if (m_pToolBar) {
 1083             COptions::Get()->SetOption(OPTION_TOOLBAR_HIDDEN, m_pToolBar->IsShown() ? 0 : 1);
 1084         }
 1085 #endif
 1086         SetToolBar(0);
 1087         delete m_pToolBar;
 1088         m_pToolBar = 0;
 1089     }
 1090 
 1091 #ifndef __WXMAC__
 1092     if (COptions::Get()->GetOptionVal(OPTION_TOOLBAR_HIDDEN) != 0) {
 1093         return true;
 1094     }
 1095 #endif
 1096 
 1097     m_pToolBar = CToolBar::Load(this);
 1098     if (!m_pToolBar) {
 1099         wxLogError(_("Cannot load toolbar from resource file"));
 1100         return false;
 1101     }
 1102     SetToolBar(m_pToolBar);
 1103 
 1104 #ifdef __WXMAC__
 1105     if (COptions::Get()->GetOptionVal(OPTION_TOOLBAR_HIDDEN) != 0) {
 1106         m_pToolBar->Show(false);
 1107     }
 1108 #endif
 1109 
 1110 
 1111     if (m_pQuickconnectBar) {
 1112         m_pQuickconnectBar->Refresh();
 1113     }
 1114 
 1115     return true;
 1116 }
 1117 
 1118 void CMainFrame::OnDisconnect(wxCommandEvent&)
 1119 {
 1120     CState* pState = CContextManager::Get()->GetCurrentContext();
 1121     if (!pState || !pState->IsRemoteConnected()) {
 1122         return;
 1123     }
 1124 
 1125     if (!pState->IsRemoteIdle()) {
 1126         return;
 1127     }
 1128 
 1129     pState->Disconnect();
 1130 }
 1131 
 1132 void CMainFrame::OnCancel(wxCommandEvent&)
 1133 {
 1134     CState* pState = CContextManager::Get()->GetCurrentContext();
 1135     if (!pState || pState->m_pCommandQueue->Idle()) {
 1136         return;
 1137     }
 1138 
 1139     if (wxMessageBoxEx(_("Really cancel current operation?"), _T("FileZilla"), wxYES_NO | wxICON_QUESTION) == wxYES) {
 1140         pState->m_pCommandQueue->Cancel();
 1141         pState->GetRemoteRecursiveOperation()->StopRecursiveOperation();
 1142     }
 1143 }
 1144 
 1145 #ifdef __WXMSW__
 1146 
 1147 BOOL CALLBACK FzEnumThreadWndProc(HWND hwnd, LPARAM lParam)
 1148 {
 1149     // This function enumerates all dialogs and calls EndDialog for them
 1150     TCHAR buffer[10];
 1151     int c = GetClassName(hwnd, buffer, 9);
 1152     // #32770 is the dialog window class.
 1153     if (c && !_tcscmp(buffer, _T("#32770")))
 1154     {
 1155         *((bool*)lParam) = true;
 1156         EndDialog(hwnd, IDCANCEL);
 1157         return FALSE;
 1158     }
 1159 
 1160     return TRUE;
 1161 }
 1162 #endif //__WXMSW__
 1163 
 1164 
 1165 bool CMainFrame::CloseDialogsAndQuit(wxCloseEvent &event)
 1166 {
 1167 #ifndef __WXMAC__
 1168     if (m_taskBarIcon) {
 1169         delete m_taskBarIcon;
 1170         m_taskBarIcon = 0;
 1171         m_closeEvent = event.GetEventType();
 1172         m_closeEventTimer.Start(1, true);
 1173         return false;
 1174     }
 1175 #endif
 1176 
 1177     // We need to close all other top level windows on the stack before closing the main frame.
 1178     // In other words, all open dialogs need to be closed.
 1179     static int prev_size = 0;
 1180 
 1181     int size = wxTopLevelWindows.size();
 1182     static wxTopLevelWindow* pLast = 0;
 1183     if (wxTopLevelWindows.size()) {
 1184         wxWindowList::reverse_iterator iter = wxTopLevelWindows.rbegin();
 1185         wxTopLevelWindow* pTop = (wxTopLevelWindow*)(*iter);
 1186         while (pTop != this && (size != prev_size || pLast != pTop)) {
 1187             if (!pTop) {
 1188                 ++iter;
 1189                 if (iter == wxTopLevelWindows.rend()) {
 1190                     break;
 1191                 }
 1192                 pTop = (wxTopLevelWindow*)(*iter);
 1193                 continue;
 1194             }
 1195 
 1196             wxDialog* pDialog = dynamic_cast<wxDialog*>(pTop);
 1197             if (pDialog && pDialog->IsModal()) {
 1198                 pDialog->EndModal(wxID_CANCEL);
 1199             }
 1200             else {
 1201                 wxWindow* pParent = pTop->GetParent();
 1202                 if (m_pQueuePane && pParent == m_pQueuePane) {
 1203                     // It's the AUI frame manager hint window. Ignore it
 1204                     ++iter;
 1205                     if (iter == wxTopLevelWindows.rend()) {
 1206                         break;
 1207                     }
 1208                     pTop = (wxTopLevelWindow*)(*iter);
 1209                     continue;
 1210                 }
 1211                 wxString title = pTop->GetTitle();
 1212                 pTop->Destroy();
 1213             }
 1214 
 1215             prev_size = size;
 1216             pLast = pTop;
 1217 
 1218             m_closeEvent = event.GetEventType();
 1219             m_closeEventTimer.Start(1, true);
 1220 
 1221             return false;
 1222         }
 1223     }
 1224 
 1225 #ifdef __WXMSW__
 1226     // wxMessageBoxEx does not use wxTopLevelWindow, close it too
 1227     bool dialog = false;
 1228     EnumThreadWindows(GetCurrentThreadId(), FzEnumThreadWndProc, (LPARAM)&dialog);
 1229     if (dialog) {
 1230         m_closeEvent = event.GetEventType();
 1231         m_closeEventTimer.Start(1, true);
 1232 
 1233         return false;
 1234     }
 1235 #endif //__WXMSW__
 1236 
 1237     // At this point all other top level windows should be closed.
 1238     return true;
 1239 }
 1240 
 1241 
 1242 void CMainFrame::OnClose(wxCloseEvent &event)
 1243 {
 1244     if (!m_bQuit) {
 1245         static bool quit_confirmation_displayed = false;
 1246         if (quit_confirmation_displayed && event.CanVeto()) {
 1247             event.Veto();
 1248             return;
 1249         }
 1250         if (event.CanVeto()) {
 1251             quit_confirmation_displayed = true;
 1252 
 1253             if (m_pQueueView && m_pQueueView->IsActive()) {
 1254                 CConditionalDialog dlg(this, CConditionalDialog::confirmexit, CConditionalDialog::yesno);
 1255                 dlg.SetTitle(_("Close FileZilla"));
 1256 
 1257                 dlg.AddText(_("File transfers still in progress."));
 1258                 dlg.AddText(_("Do you really want to close FileZilla?"));
 1259 
 1260                 if (!dlg.Run()) {
 1261                     event.Veto();
 1262                     quit_confirmation_displayed = false;
 1263                     return;
 1264                 }
 1265                 if (m_bQuit) {
 1266                     return;
 1267                 }
 1268             }
 1269 
 1270             CEditHandler* pEditHandler = CEditHandler::Get();
 1271             if (pEditHandler) {
 1272                 if (pEditHandler->GetFileCount(CEditHandler::remote, CEditHandler::edit) || pEditHandler->GetFileCount(CEditHandler::none, CEditHandler::upload) ||
 1273                     pEditHandler->GetFileCount(CEditHandler::none, CEditHandler::upload_and_remove) ||
 1274                     pEditHandler->GetFileCount(CEditHandler::none, CEditHandler::upload_and_remove_failed))
 1275                 {
 1276                     CConditionalDialog dlg(this, CConditionalDialog::confirmexit_edit, CConditionalDialog::yesno);
 1277                     dlg.SetTitle(_("Close FileZilla"));
 1278 
 1279                     dlg.AddText(_("Some files are still being edited or need to be uploaded."));
 1280                     dlg.AddText(_("If you close FileZilla, your changes will be lost."));
 1281                     dlg.AddText(_("Do you really want to close FileZilla?"));
 1282 
 1283                     if (!dlg.Run()) {
 1284                         event.Veto();
 1285                         quit_confirmation_displayed = false;
 1286                         return;
 1287                     }
 1288                     if (m_bQuit) {
 1289                         return;
 1290                     }
 1291                 }
 1292             }
 1293             quit_confirmation_displayed = false;
 1294         }
 1295 
 1296         if (m_pWindowStateManager) {
 1297             m_pWindowStateManager->Remember(OPTION_MAINWINDOW_POSITION);
 1298             delete m_pWindowStateManager;
 1299             m_pWindowStateManager = 0;
 1300         }
 1301 
 1302         RememberSplitterPositions();
 1303 
 1304 #ifdef __WXMAC__
 1305         if (m_pToolBar) {
 1306             COptions::Get()->SetOption(OPTION_TOOLBAR_HIDDEN, m_pToolBar->IsShown() ? 0 : 1);
 1307         }
 1308 #endif
 1309         m_bQuit = true;
 1310     }
 1311 
 1312     Show(false);
 1313     if (!CloseDialogsAndQuit(event)) {
 1314         return;
 1315     }
 1316 
 1317     // Getting deleted by wxWidgets
 1318     for (int i = 0; i < 2; ++i) {
 1319         m_pActivityLed[i] = 0;
 1320     }
 1321     m_pStatusBar = 0;
 1322     m_pMenuBar = 0;
 1323     m_pToolBar = 0;
 1324 
 1325     // We're no longer interested in these events
 1326     delete m_pStateEventHandler;
 1327     m_pStateEventHandler = 0;
 1328 
 1329     if (m_pQueueView && !m_pQueueView->Quit()) {
 1330         if (event.CanVeto()) {
 1331             event.Veto();
 1332         }
 1333         return;
 1334     }
 1335 
 1336     CEditHandler* pEditHandler = CEditHandler::Get();
 1337     if (pEditHandler) {
 1338         pEditHandler->RemoveAll(true);
 1339         pEditHandler->Release();
 1340     }
 1341 
 1342     bool res = true;
 1343     const std::vector<CState*> *pStates = CContextManager::Get()->GetAllStates();
 1344     for (CState* pState : *pStates) {
 1345         if (pState->GetLocalRecursiveOperation()) {
 1346             pState->GetLocalRecursiveOperation()->StopRecursiveOperation();
 1347         }
 1348         if (pState->GetRemoteRecursiveOperation()) {
 1349             pState->GetRemoteRecursiveOperation()->StopRecursiveOperation();
 1350         }
 1351 
 1352         if (pState->m_pCommandQueue) {
 1353             if (!pState->m_pCommandQueue->Quit()) {
 1354                 res = false;
 1355             }
 1356         }
 1357     }
 1358 
 1359     if (!res) {
 1360         if (event.CanVeto()) {
 1361             event.Veto();
 1362         }
 1363         return;
 1364     }
 1365 
 1366     if (m_pContextControl) {
 1367         CContextControl::_context_controls* controls = m_pContextControl->GetCurrentControls();
 1368         if (controls) {
 1369             if (controls->pLocalListView) {
 1370                 controls->pLocalListView->SaveColumnSettings(OPTION_LOCALFILELIST_COLUMN_WIDTHS, OPTION_LOCALFILELIST_COLUMN_SHOWN, OPTION_LOCALFILELIST_COLUMN_ORDER);
 1371             }
 1372             if (controls->pRemoteListView) {
 1373                 controls->pRemoteListView->SaveColumnSettings(OPTION_REMOTEFILELIST_COLUMN_WIDTHS, OPTION_REMOTEFILELIST_COLUMN_SHOWN, OPTION_REMOTEFILELIST_COLUMN_ORDER);
 1374             }
 1375         }
 1376 
 1377         m_pContextControl->SaveTabs();
 1378     }
 1379 
 1380     for (std::vector<CState*>::const_iterator iter = pStates->begin(); iter != pStates->end(); ++iter) {
 1381         CState *pState = *iter;
 1382         pState->DestroyEngine();
 1383     }
 1384 
 1385     CSiteManager::ClearIdMap();
 1386 
 1387     bool filters_toggled = CFilterManager::HasActiveFilters(true) && !CFilterManager::HasActiveFilters(false);
 1388     COptions::Get()->SetOption(OPTION_FILTERTOGGLESTATE, filters_toggled ? 1 : 0);
 1389 
 1390     Destroy();
 1391 }
 1392 
 1393 void CMainFrame::OnReconnect(wxCommandEvent &)
 1394 {
 1395     CState* pState = CContextManager::Get()->GetCurrentContext();
 1396     if (!pState) {
 1397         return;
 1398     }
 1399 
 1400     if (pState->IsRemoteConnected() || !pState->IsRemoteIdle()) {
 1401         return;
 1402     }
 1403 
 1404     Site site = pState->GetLastSite();
 1405     CServerPath path = pState->GetLastServerPath();
 1406     Bookmark bm;
 1407     bm.m_remoteDir = path;
 1408     ConnectToSite(site, bm);
 1409 }
 1410 
 1411 void CMainFrame::OnRefresh(wxCommandEvent &)
 1412 {
 1413     CState* pState = CContextManager::Get()->GetCurrentContext();
 1414     if (!pState) {
 1415         return;
 1416     }
 1417 
 1418     bool clear_cache = false;
 1419     if (wxGetKeyState(WXK_CONTROL)) {
 1420         clear_cache = true;
 1421     }
 1422     pState->RefreshRemote(clear_cache);
 1423     pState->RefreshLocal();
 1424 }
 1425 
 1426 void CMainFrame::OnTimer(wxTimerEvent& event)
 1427 {
 1428     if (event.GetId() == m_closeEventTimer.GetId()) {
 1429         if (m_closeEvent == 0) {
 1430             return;
 1431         }
 1432 
 1433         // When we get idle event, a dialog's event loop has been left.
 1434         // Now we can close the top level window on the stack.
 1435         wxCloseEvent *evt = new wxCloseEvent(m_closeEvent);
 1436         evt->SetCanVeto(false);
 1437         QueueEvent(evt);
 1438     }
 1439 #if FZ_MANUALUPDATECHECK
 1440     else if( event.GetId() == update_dialog_timer_.GetId() ) {
 1441         TriggerUpdateDialog();
 1442     }
 1443 #endif
 1444 }
 1445 
 1446 void CMainFrame::OpenSiteManager(Site const* site)
 1447 {
 1448     CState* pState = CContextManager::Get()->GetCurrentContext();
 1449     if (!pState) {
 1450         return;
 1451     }
 1452 
 1453     CSiteManagerDialog dlg;
 1454 
 1455     std::vector<CSiteManagerDialog::_connected_site> connected_sites;
 1456 
 1457     if (site) {
 1458         CSiteManagerDialog::_connected_site connected_site;
 1459         connected_site.site = *site;
 1460         connected_sites.push_back(connected_site);
 1461     }
 1462 
 1463     for (int i = 0; i < m_pContextControl->GetTabCount(); ++i) {
 1464         CContextControl::_context_controls *controls =  m_pContextControl->GetControlsFromTabIndex(i);
 1465         if (!controls || !controls->pState) {
 1466             continue;
 1467         }
 1468 
 1469         Site site = controls->pState->GetSite();
 1470         if (!site) {
 1471             site = controls->pState->GetLastSite();
 1472         }
 1473 
 1474         std::wstring const& path = site.SitePath();
 1475         if (path.empty()) {
 1476             continue;
 1477         }
 1478 
 1479         auto it = std::find_if(connected_sites.cbegin(), connected_sites.cend(), [&](auto const& v) { return v.old_path == path; });
 1480         if (it != connected_sites.cend()) {
 1481             continue;
 1482         }
 1483 
 1484         CSiteManagerDialog::_connected_site connected_site;
 1485         connected_site.old_path = path;
 1486         connected_site.site = site;
 1487         connected_sites.push_back(connected_site);
 1488     }
 1489 
 1490     if (!dlg.Create(this, &connected_sites, site)) {
 1491         return;
 1492     }
 1493 
 1494     int res = dlg.ShowModal();
 1495     if (res == wxID_YES || res == wxID_OK) {
 1496         // Update bookmark paths
 1497         for (int i = 0; i < m_pContextControl->GetTabCount(); ++i) {
 1498             CContextControl::_context_controls *controls = m_pContextControl->GetControlsFromTabIndex(i);
 1499             if (!controls || !controls->pState) {
 1500                 continue;
 1501             }
 1502 
 1503             controls->pState->UpdateKnownSites(connected_sites);
 1504         }
 1505     }
 1506 
 1507     if (res == wxID_YES) {
 1508         Site data;
 1509         Bookmark bookmark;
 1510         if (!dlg.GetServer(data, bookmark)) {
 1511             return;
 1512         }
 1513 
 1514         ConnectToSite(data, bookmark);
 1515     }
 1516 }
 1517 
 1518 void CMainFrame::OnSiteManager(wxCommandEvent& e)
 1519 {
 1520 #ifdef __WXMAC__
 1521     if (wxGetKeyState(WXK_SHIFT) ||
 1522         wxGetKeyState(WXK_ALT) ||
 1523         wxGetKeyState(WXK_CONTROL))
 1524     {
 1525         OnSitemanagerDropdown(e);
 1526         return;
 1527     }
 1528 #endif
 1529     (void)e;
 1530     OpenSiteManager();
 1531 }
 1532 
 1533 void CMainFrame::UpdateActivityLed(int direction)
 1534 {
 1535     if (m_pActivityLed[direction]) {
 1536         m_pActivityLed[direction]->Ping();
 1537     }
 1538 }
 1539 
 1540 void CMainFrame::OnProcessQueue(wxCommandEvent& event)
 1541 {
 1542     if (m_pQueueView) {
 1543         m_pQueueView->SetActive(event.IsChecked());
 1544     }
 1545 }
 1546 
 1547 void CMainFrame::OnMenuEditSettings(wxCommandEvent&)
 1548 {
 1549     CSettingsDialog dlg(m_engineContext);
 1550     if (!dlg.Create(this)) {
 1551         return;
 1552     }
 1553 
 1554     COptions* pOptions = COptions::Get();
 1555 
 1556     wxString oldLang = pOptions->GetOption(OPTION_LANGUAGE);
 1557 
 1558     int oldShowDebugMenu = pOptions->GetOptionVal(OPTION_DEBUG_MENU) != 0;
 1559 
 1560     int res = dlg.ShowModal();
 1561     if (res != wxID_OK) {
 1562         return;
 1563     }
 1564 
 1565     wxString newLang = pOptions->GetOption(OPTION_LANGUAGE);
 1566 
 1567     if (oldLang != newLang ||
 1568         oldShowDebugMenu != pOptions->GetOptionVal(OPTION_DEBUG_MENU))
 1569     {
 1570         CreateMenus();
 1571     }
 1572     if (oldLang != newLang) {
 1573         wxMessageBoxEx(_("FileZilla needs to be restarted for the language change to take effect."), _("Language changed"), wxICON_INFORMATION, this);
 1574     }
 1575 
 1576     CheckChangedSettings();
 1577 }
 1578 
 1579 void CMainFrame::OnToggleLogView(wxCommandEvent&)
 1580 {
 1581     if (!m_pTopSplitter) {
 1582         return;
 1583     }
 1584 
 1585     bool shown;
 1586 
 1587     if (COptions::Get()->GetOptionVal(OPTION_MESSAGELOG_POSITION) == 1) {
 1588         if (!m_pQueueLogSplitter) {
 1589             return;
 1590         }
 1591         if (m_pQueueLogSplitter->IsSplit()) {
 1592             m_pQueueLogSplitter->Unsplit(m_pStatusView);
 1593             shown = false;
 1594         }
 1595         else if (m_pStatusView->IsShown()) {
 1596             m_pStatusView->Hide();
 1597             m_pBottomSplitter->Unsplit(m_pQueueLogSplitter);
 1598             shown = false;
 1599         }
 1600         else if (!m_pQueueLogSplitter->IsShown()) {
 1601             m_pQueueLogSplitter->Initialize(m_pStatusView);
 1602             m_pBottomSplitter->SplitHorizontally(m_pContextControl, m_pQueueLogSplitter);
 1603             shown = true;
 1604         }
 1605         else {
 1606             m_pQueueLogSplitter->SplitVertically(m_pQueuePane, m_pStatusView);
 1607             shown = true;
 1608         }
 1609     }
 1610     else {
 1611         if (m_pTopSplitter->IsSplit()) {
 1612             m_pTopSplitter->Unsplit(m_pStatusView);
 1613             shown = false;
 1614         }
 1615         else {
 1616             m_pTopSplitter->SplitHorizontally(m_pStatusView, m_pBottomSplitter);
 1617             shown = true;
 1618         }
 1619     }
 1620 
 1621     if (COptions::Get()->GetOptionVal(OPTION_MESSAGELOG_POSITION) != 2) {
 1622         COptions::Get()->SetOption(OPTION_SHOW_MESSAGELOG, shown);
 1623     }
 1624 }
 1625 
 1626 void CMainFrame::OnToggleDirectoryTreeView(wxCommandEvent& event)
 1627 {
 1628     if (!m_pContextControl) {
 1629         return;
 1630     }
 1631 
 1632     CContextControl::_context_controls* controls = m_pContextControl->GetCurrentControls();
 1633     if (!controls) {
 1634         return;
 1635     }
 1636 
 1637     bool const local = event.GetId() == XRCID("ID_TOOLBAR_LOCALTREEVIEW") || event.GetId() == XRCID("ID_VIEW_LOCALTREE");
 1638     CSplitterWindowEx* splitter = local ? controls->pLocalSplitter : controls->pRemoteSplitter;
 1639     bool show = !splitter->IsSplit();
 1640     ShowDirectoryTree(local, show);
 1641 }
 1642 
 1643 void CMainFrame::ShowDirectoryTree(bool local, bool show)
 1644 {
 1645     if (!m_pContextControl) {
 1646         return;
 1647     }
 1648 
 1649     const int layout = COptions::Get()->GetOptionVal(OPTION_FILEPANE_LAYOUT);
 1650     const int swap = COptions::Get()->GetOptionVal(OPTION_FILEPANE_SWAP);
 1651     for (int i = 0; i < m_pContextControl->GetTabCount(); ++i) {
 1652         CContextControl::_context_controls* controls = m_pContextControl->GetControlsFromTabIndex(i);
 1653         if (!controls) {
 1654             continue;
 1655         }
 1656 
 1657         CSplitterWindowEx* splitter = local ? controls->pLocalSplitter : controls->pRemoteSplitter;
 1658         CView* tree = local ? controls->pLocalTreeViewPanel : controls->pRemoteTreeViewPanel;
 1659         CView* list = local ? controls->pLocalListViewPanel : controls->pRemoteListViewPanel;
 1660 
 1661         if (show && !splitter->IsSplit()) {
 1662             tree->SetHeader(list->DetachHeader());
 1663 
 1664             if (layout == 3 && swap) {
 1665                 splitter->SplitVertically(list, tree);
 1666             }
 1667             else if (layout) {
 1668                 splitter->SplitVertically(tree, list);
 1669             }
 1670             else {
 1671                 splitter->SplitHorizontally(tree, list);
 1672             }
 1673         }
 1674         else if (!show && splitter->IsSplit()) {
 1675             list->SetHeader(tree->DetachHeader());
 1676             splitter->Unsplit(tree);
 1677         }
 1678     }
 1679 
 1680     COptions::Get()->SetOption(local ? OPTION_SHOW_TREE_LOCAL : OPTION_SHOW_TREE_REMOTE, show);
 1681 }
 1682 
 1683 void CMainFrame::OnToggleQueueView(wxCommandEvent&)
 1684 {
 1685     if (!m_pBottomSplitter) {
 1686         return;
 1687     }
 1688 
 1689     bool shown;
 1690     if (COptions::Get()->GetOptionVal(OPTION_MESSAGELOG_POSITION) == 1) {
 1691         if (!m_pQueueLogSplitter) {
 1692             return;
 1693         }
 1694         if (m_pQueueLogSplitter->IsSplit()) {
 1695             m_pQueueLogSplitter->Unsplit(m_pQueuePane);
 1696             shown = false;
 1697         }
 1698         else if (m_pQueuePane->IsShown()) {
 1699             m_pQueuePane->Hide();
 1700             m_pBottomSplitter->Unsplit(m_pQueueLogSplitter);
 1701             shown = false;
 1702         }
 1703         else if (!m_pQueueLogSplitter->IsShown()) {
 1704             m_pQueueLogSplitter->Initialize(m_pQueuePane);
 1705             m_pBottomSplitter->SplitHorizontally(m_pContextControl, m_pQueueLogSplitter);
 1706             shown = true;
 1707         }
 1708         else {
 1709             m_pQueueLogSplitter->SplitVertically(m_pQueuePane, m_pStatusView);
 1710             shown = true;
 1711         }
 1712     }
 1713     else {
 1714         if (m_pBottomSplitter->IsSplit()) {
 1715             m_pBottomSplitter->Unsplit(m_pQueueLogSplitter);
 1716         }
 1717         else {
 1718             m_pQueueLogSplitter->Initialize(m_pQueuePane);
 1719             m_pBottomSplitter->SplitHorizontally(m_pContextControl, m_pQueueLogSplitter);
 1720         }
 1721         shown = m_pBottomSplitter->IsSplit();
 1722     }
 1723 
 1724     COptions::Get()->SetOption(OPTION_SHOW_QUEUE, shown);
 1725 }
 1726 
 1727 void CMainFrame::OnMenuHelpAbout(wxCommandEvent&)
 1728 {
 1729     CAboutDialog dlg;
 1730     if (!dlg.Create(this)) {
 1731         return;
 1732     }
 1733 
 1734     dlg.ShowModal();
 1735 }
 1736 
 1737 void CMainFrame::OnFilter(wxCommandEvent& event)
 1738 {
 1739     if (wxGetKeyState(WXK_SHIFT)) {
 1740         OnFilterRightclicked(event);
 1741         return;
 1742     }
 1743 
 1744     bool const oldActive = CFilterManager::HasActiveFilters();
 1745 
 1746     CFilterDialog dlg;
 1747     dlg.Create(this);
 1748     dlg.ShowModal();
 1749 
 1750     if (oldActive == CFilterManager::HasActiveFilters() && m_pToolBar) {
 1751         // Restore state
 1752         m_pToolBar->ToggleTool(XRCID("ID_TOOLBAR_FILTER"), oldActive);
 1753     }
 1754 }
 1755 
 1756 #if FZ_MANUALUPDATECHECK
 1757 void CMainFrame::OnCheckForUpdates(wxCommandEvent& event)
 1758 {
 1759     if (!m_pUpdater) {
 1760         return;
 1761     }
 1762 
 1763     if (event.GetId() == XRCID("ID_CHECKFORUPDATES") || (!COptions::Get()->GetOptionVal(OPTION_DEFAULT_DISABLEUPDATECHECK) && COptions::Get()->GetOptionVal(OPTION_UPDATECHECK) != 0)) {
 1764         m_pUpdater->RunIfNeeded();
 1765     }
 1766 
 1767     update_dialog_timer_.Stop();
 1768     CUpdateDialog dlg(this, *m_pUpdater);
 1769     dlg.ShowModal();
 1770     update_dialog_timer_.Stop();
 1771 }
 1772 
 1773 void CMainFrame::UpdaterStateChanged(UpdaterState s, build const& v)
 1774 {
 1775 #if FZ_AUTOUPDATECHECK
 1776     if (!m_pMenuBar) {
 1777         return;
 1778     }
 1779 
 1780     if (s == UpdaterState::idle) {
 1781         wxMenu* m = 0;
 1782         wxMenuItem* pItem = m_pMenuBar->FindItem(GetAvailableUpdateMenuId(), &m);
 1783         if (pItem && m) {
 1784             for (size_t i = 0; i != m_pMenuBar->GetMenuCount(); ++i) {
 1785                 if( m_pMenuBar->GetMenu(i) == m ) {
 1786                     m_pMenuBar->Remove(i);
 1787                     delete m;
 1788                     break;
 1789                 }
 1790             }
 1791         }
 1792         return;
 1793     }
 1794     else if (s != UpdaterState::newversion && s != UpdaterState::newversion_ready && s != UpdaterState::newversion_stale) {
 1795         return;
 1796     }
 1797     
 1798     wxString const name = v.version_.empty() ? _("Unknown version") : wxString::Format(_("&Version %s"), v.version_);
 1799 
 1800     wxMenuItem* pItem = m_pMenuBar->FindItem(GetAvailableUpdateMenuId());
 1801     if (!pItem) {
 1802         wxMenu* pMenu = new wxMenu();
 1803         pMenu->Append(GetAvailableUpdateMenuId(), name);
 1804         m_pMenuBar->Append(pMenu, _("&New version available!"));
 1805 
 1806         if (!update_dialog_timer_.IsRunning()) {
 1807             update_dialog_timer_.Start(1, true);
 1808         }
 1809     }
 1810     else {
 1811         pItem->SetItemLabel(name);
 1812     }
 1813 #endif
 1814 }
 1815 
 1816 void CMainFrame::TriggerUpdateDialog()
 1817 {
 1818     if (m_bQuit || !m_pUpdater) {
 1819         return;
 1820     }
 1821 
 1822     if (CUpdateDialog::IsRunning()) {
 1823         return;
 1824     }
 1825 
 1826     if (!wxDialogEx::CanShowPopupDialog()) {
 1827         update_dialog_timer_.Start(1000, true);
 1828         return;
 1829     }
 1830 
 1831     CUpdateDialog dlg(this, *m_pUpdater);
 1832     dlg.ShowModal();
 1833 
 1834     // In case the timer was started while the dialog was up.
 1835     update_dialog_timer_.Stop();
 1836 }
 1837 #endif
 1838 
 1839 void CMainFrame::UpdateLayout()
 1840 {
 1841     int const layout = COptions::Get()->GetOptionVal(OPTION_FILEPANE_LAYOUT);
 1842     int const swap = COptions::Get()->GetOptionVal(OPTION_FILEPANE_SWAP);
 1843 
 1844     int const messagelog_position = COptions::Get()->GetOptionVal(OPTION_MESSAGELOG_POSITION);
 1845 
 1846     // First handle changes in message log position as it can make size of the other panes change
 1847     {
 1848         bool shown = m_pStatusView->IsShown();
 1849         wxWindow* parent = m_pStatusView->GetParent();
 1850 
 1851         bool changed;
 1852         if (parent == m_pTopSplitter && messagelog_position != 0) {
 1853             if (shown) {
 1854                 m_pTopSplitter->Unsplit(m_pStatusView);
 1855             }
 1856             changed = true;
 1857         }
 1858         else if (parent == m_pQueueLogSplitter && messagelog_position != 1) {
 1859             if (shown) {
 1860                 if (m_pQueueLogSplitter->IsSplit()) {
 1861                     m_pQueueLogSplitter->Unsplit(m_pStatusView);
 1862                 }
 1863                 else {
 1864                     m_pBottomSplitter->Unsplit(m_pQueueLogSplitter);
 1865                 }
 1866             }
 1867             changed = true;
 1868         }
 1869         else if (parent != m_pTopSplitter && parent != m_pQueueLogSplitter && messagelog_position != 2) {
 1870             m_pQueuePane->RemovePage(3);
 1871             changed = true;
 1872             shown = true;
 1873         }
 1874         else {
 1875             changed = false;
 1876         }
 1877 
 1878         if (changed) {
 1879             switch (messagelog_position) {
 1880             default:
 1881                 m_pStatusView->Reparent(m_pTopSplitter);
 1882                 if (shown) {
 1883                     m_pTopSplitter->SplitHorizontally(m_pStatusView, m_pBottomSplitter);
 1884                 }
 1885                 break;
 1886             case 1:
 1887                 m_pStatusView->Reparent(m_pQueueLogSplitter);
 1888                 if (shown) {
 1889                     if (m_pQueueLogSplitter->IsShown()) {
 1890                         m_pQueueLogSplitter->SplitVertically(m_pQueuePane, m_pStatusView);
 1891                     }
 1892                     else {
 1893                         m_pQueueLogSplitter->Initialize(m_pStatusView);
 1894                         m_pBottomSplitter->SplitHorizontally(m_pContextControl, m_pQueueLogSplitter);
 1895                     }
 1896                 }
 1897                 break;
 1898             case 2:
 1899                 m_pQueuePane->AddPage(m_pStatusView, _("Message log"));
 1900                 break;
 1901             }
 1902         }
 1903     }
 1904 
 1905     // Now the other panes
 1906     for (int i = 0; i < m_pContextControl->GetTabCount(); ++i) {
 1907         CContextControl::_context_controls *controls = m_pContextControl->GetControlsFromTabIndex(i);
 1908         if (!controls) {
 1909             continue;
 1910         }
 1911 
 1912         int mode;
 1913         if (!layout || layout == 2 || layout == 3) {
 1914             mode = wxSPLIT_VERTICAL;
 1915         }
 1916         else {
 1917             mode = wxSPLIT_HORIZONTAL;
 1918         }
 1919 
 1920         int isMode = controls->pViewSplitter->GetSplitMode();
 1921 
 1922         int isSwap = controls->pViewSplitter->GetWindow1() == controls->pRemoteSplitter ? 1 : 0;
 1923 
 1924         if (mode != isMode || swap != isSwap) {
 1925             controls->pViewSplitter->Unsplit();
 1926             if (mode == wxSPLIT_VERTICAL) {
 1927                 if (swap) {
 1928                     controls->pViewSplitter->SplitVertically(controls->pRemoteSplitter, controls->pLocalSplitter);
 1929                 }
 1930                 else {
 1931                     controls->pViewSplitter->SplitVertically(controls->pLocalSplitter, controls->pRemoteSplitter);
 1932                 }
 1933             }
 1934             else {
 1935                 if (swap) {
 1936                     controls->pViewSplitter->SplitHorizontally(controls->pRemoteSplitter, controls->pLocalSplitter);
 1937                 }
 1938                 else {
 1939                     controls->pViewSplitter->SplitHorizontally(controls->pLocalSplitter, controls->pRemoteSplitter);
 1940                 }
 1941             }
 1942         }
 1943 
 1944         if (controls->pLocalSplitter->IsSplit()) {
 1945             if (!layout) {
 1946                 mode = wxSPLIT_HORIZONTAL;
 1947             }
 1948             else {
 1949                 mode = wxSPLIT_VERTICAL;
 1950             }
 1951 
 1952             wxWindow* pFirst;
 1953             wxWindow* pSecond;
 1954             if (layout == 3 && swap) {
 1955                 pFirst = controls->pLocalListViewPanel;
 1956                 pSecond = controls->pLocalTreeViewPanel;
 1957             }
 1958             else {
 1959                 pFirst = controls->pLocalTreeViewPanel;
 1960                 pSecond = controls->pLocalListViewPanel;
 1961             }
 1962 
 1963             if (mode != controls->pLocalSplitter->GetSplitMode() || pFirst != controls->pLocalSplitter->GetWindow1()) {
 1964                 controls->pLocalSplitter->Unsplit();
 1965                 if (mode == wxSPLIT_VERTICAL) {
 1966                     controls->pLocalSplitter->SplitVertically(pFirst, pSecond);
 1967                 }
 1968                 else {
 1969                     controls->pLocalSplitter->SplitHorizontally(pFirst, pSecond);
 1970                 }
 1971             }
 1972         }
 1973 
 1974         if (controls->pRemoteSplitter->IsSplit()) {
 1975             if (!layout) {
 1976                 mode = wxSPLIT_HORIZONTAL;
 1977             }
 1978             else {
 1979                 mode = wxSPLIT_VERTICAL;
 1980             }
 1981 
 1982             wxWindow* pFirst;
 1983             wxWindow* pSecond;
 1984             if (layout == 3 && !swap) {
 1985                 pFirst = controls->pRemoteListViewPanel;
 1986                 pSecond = controls->pRemoteTreeViewPanel;
 1987             }
 1988             else {
 1989                 pFirst = controls->pRemoteTreeViewPanel;
 1990                 pSecond = controls->pRemoteListViewPanel;
 1991             }
 1992 
 1993             if (mode != controls->pRemoteSplitter->GetSplitMode() || pFirst != controls->pRemoteSplitter->GetWindow1()) {
 1994                 controls->pRemoteSplitter->Unsplit();
 1995                 if (mode == wxSPLIT_VERTICAL) {
 1996                     controls->pRemoteSplitter->SplitVertically(pFirst, pSecond);
 1997                 }
 1998                 else {
 1999                     controls->pRemoteSplitter->SplitHorizontally(pFirst, pSecond);
 2000                 }
 2001             }
 2002         }
 2003 
 2004         if (layout == 3) {
 2005             if (!swap) {
 2006                 controls->pRemoteSplitter->SetSashGravity(1.0);
 2007                 controls->pLocalSplitter->SetSashGravity(0.0);
 2008             }
 2009             else {
 2010                 controls->pLocalSplitter->SetSashGravity(1.0);
 2011                 controls->pRemoteSplitter->SetSashGravity(0.0);
 2012             }
 2013         }
 2014         else {
 2015             controls->pLocalSplitter->SetSashGravity(0.0);
 2016             controls->pRemoteSplitter->SetSashGravity(0.0);
 2017         }
 2018     }
 2019 }
 2020 
 2021 void CMainFrame::OnSitemanagerDropdown(wxCommandEvent& event)
 2022 {
 2023     if (!m_pToolBar) {
 2024         return;
 2025     }
 2026 
 2027     std::unique_ptr<wxMenu> pMenu = CSiteManager::GetSitesMenu();
 2028     if (pMenu) {
 2029         ShowDropdownMenu(pMenu.release(), m_pToolBar, event);
 2030     }
 2031 }
 2032 
 2033 bool CMainFrame::ConnectToSite(Site & data, Bookmark const& bookmark, CState* pState)
 2034 {
 2035     // First check if we need to ask user for a password
 2036     if (!CLoginManager::Get().GetPassword(data, false)) {
 2037         return false;
 2038     }
 2039 
 2040     // Check if current state is already connected and if needed ask whether to open in new tab
 2041     if (!pState) {
 2042         pState = CContextManager::Get()->GetCurrentContext();
 2043         if (!pState) {
 2044             return false;
 2045         }
 2046     }
 2047 
 2048     if (pState->IsRemoteConnected() || !pState->IsRemoteIdle()) {
 2049         int action = COptions::Get()->GetOptionVal(OPTION_ALREADYCONNECTED_CHOICE);
 2050         if (action < 2) {
 2051             wxDialogEx dlg;
 2052             if (!dlg.Load(this, _T("ID_ALREADYCONNECTED"))) {
 2053                 return false;
 2054             }
 2055 
 2056             if (action != 0) {
 2057                 XRCCTRL(dlg, "ID_OLDTAB", wxRadioButton)->SetValue(true);
 2058             }
 2059             else {
 2060                 XRCCTRL(dlg, "ID_NEWTAB", wxRadioButton)->SetValue(true);
 2061             }
 2062 
 2063             if (dlg.ShowModal() != wxID_OK) {
 2064                 return false;
 2065             }
 2066 
 2067             if (XRCCTRL(dlg, "ID_NEWTAB", wxRadioButton)->GetValue()) {
 2068                 action = 0;
 2069             }
 2070             else {
 2071                 action = 1;
 2072             }
 2073 
 2074             if (XRCCTRL(dlg, "ID_REMEMBER", wxCheckBox)->IsChecked()) {
 2075                 action |= 2;
 2076             }
 2077             COptions::Get()->SetOption(OPTION_ALREADYCONNECTED_CHOICE, action);
 2078         }
 2079 
 2080         if (!(action & 1)) {
 2081             if (!m_pContextControl->CreateTab()) {
 2082                 return false;
 2083             }
 2084             pState = CContextManager::Get()->GetCurrentContext();
 2085         }
 2086     }
 2087 
 2088     // Next tell the state to connect
 2089     if (!pState->Connect(data, bookmark.m_remoteDir, bookmark.m_comparison)) {
 2090         return false;
 2091     }
 2092 
 2093     // Apply comparison and sync browsing options
 2094     // FIXME: Move to state?
 2095     if (!bookmark.m_localDir.empty()) {
 2096         bool set = pState->SetLocalDir(bookmark.m_localDir, 0, false);
 2097 
 2098         if (set && bookmark.m_sync) {
 2099             wxASSERT(!bookmark.m_remoteDir.empty());
 2100             pState->SetSyncBrowse(true, bookmark.m_remoteDir);
 2101         }
 2102     }
 2103 
 2104     if (bookmark.m_comparison && pState->GetComparisonManager()) {
 2105         pState->GetComparisonManager()->CompareListings();
 2106     }
 2107 
 2108     return true;
 2109 }
 2110 
 2111 void CMainFrame::CheckChangedSettings()
 2112 {
 2113     m_pAsyncRequestQueue->RecheckDefaults();
 2114 
 2115     CAutoAsciiFiles::SettingsChanged();
 2116 
 2117 #if FZ_MANUALUPDATECHECK
 2118     if (m_pUpdater) {
 2119         m_pUpdater->Init();
 2120     }
 2121 #endif
 2122 }
 2123 
 2124 void CMainFrame::ConnectNavigationHandler(wxEvtHandler* handler)
 2125 {
 2126     if (!handler) {
 2127         return;
 2128     }
 2129 
 2130     handler->Connect(wxEVT_NAVIGATION_KEY, wxNavigationKeyEventHandler(CMainFrame::OnNavigationKeyEvent), 0, this);
 2131 }
 2132 
 2133 void CMainFrame::OnNavigationKeyEvent(wxNavigationKeyEvent& event)
 2134 {
 2135     if (wxGetKeyState(WXK_CONTROL) && event.IsFromTab()) {
 2136         if (m_pContextControl) {
 2137             m_pContextControl->AdvanceTab(event.GetDirection());
 2138         }
 2139         return;
 2140     }
 2141 
 2142     event.Skip();
 2143 }
 2144 
 2145 void CMainFrame::OnChar(wxKeyEvent& event)
 2146 {
 2147     if (event.GetKeyCode() == WXK_F7) {
 2148         auto * controls = m_pContextControl->GetCurrentControls();
 2149         if (controls) {
 2150             controls->SwitchFocusedSide();
 2151         }
 2152         return;
 2153     }
 2154     else if (event.GetKeyCode() != WXK_F6) {
 2155         event.Skip();
 2156         return;
 2157     }
 2158 
 2159     // Jump between quickconnect bar and view headers
 2160 
 2161     std::list<wxWindow*> windowOrder;
 2162     if (m_pQuickconnectBar) {
 2163         windowOrder.push_back(m_pQuickconnectBar);
 2164     }
 2165     CContextControl::_context_controls* controls = m_pContextControl->GetCurrentControls();
 2166     if (controls) {
 2167         windowOrder.push_back(controls->pLocalViewHeader);
 2168         windowOrder.push_back(controls->pRemoteViewHeader);
 2169     }
 2170 
 2171     wxWindow* focused = FindFocus();
 2172 
 2173     bool skipFirst = false;
 2174     std::list<wxWindow*>::iterator iter;
 2175     if (!focused) {
 2176         iter = windowOrder.begin();
 2177         skipFirst = false;
 2178     }
 2179     else {
 2180         wxWindow *parent = focused->GetParent();
 2181         for (iter = windowOrder.begin(); iter != windowOrder.end(); ++iter) {
 2182             if (*iter == focused || *iter == parent) {
 2183                 skipFirst = true;
 2184                 break;
 2185             }
 2186         }
 2187         if (iter == windowOrder.end()) {
 2188             iter = windowOrder.begin();
 2189             skipFirst = false;
 2190         }
 2191     }
 2192 
 2193     FocusNextEnabled(windowOrder, iter, skipFirst, !event.ShiftDown());
 2194 }
 2195 
 2196 void CMainFrame::FocusNextEnabled(std::list<wxWindow*>& windowOrder, std::list<wxWindow*>::iterator iter, bool skipFirst, bool forward)
 2197 {
 2198     std::list<wxWindow*>::iterator start = iter;
 2199 
 2200     while (skipFirst || !(*iter)->IsShownOnScreen() || !(*iter)->IsEnabled()) {
 2201         skipFirst = false;
 2202         if (forward) {
 2203             ++iter;
 2204             if (iter == windowOrder.end()) {
 2205                 iter = windowOrder.begin();
 2206             }
 2207         }
 2208         else {
 2209             if (iter == windowOrder.begin()) {
 2210                 iter = windowOrder.end();
 2211             }
 2212             --iter;
 2213         }
 2214 
 2215         if (iter == start) {
 2216             wxBell();
 2217             return;
 2218         }
 2219     }
 2220 
 2221     (*iter)->SetFocus();
 2222 }
 2223 
 2224 void CMainFrame::RememberSplitterPositions()
 2225 {
 2226     CContextControl::_context_controls* controls = m_pContextControl ? m_pContextControl->GetCurrentControls() : 0;
 2227     if (!controls) {
 2228         return;
 2229     }
 2230 
 2231     auto const positions = controls->GetSplitterPositions();
 2232     std::wstring const posString = fz::sprintf(
 2233         L"%d %d %d %d %d %d",
 2234         // top_pos
 2235         m_pTopSplitter->GetSashPosition(),
 2236 
 2237         // bottom_height
 2238         m_pBottomSplitter->GetSashPosition(),
 2239 
 2240         // view_pos
 2241         // Note that we cannot use %f, it is locale-dependent
 2242         static_cast<int>(std::get<0>(positions) * 1000000000),
 2243 
 2244         // local_pos
 2245         std::get<1>(positions),
 2246 
 2247         // remote_pos
 2248         std::get<2>(positions),
 2249 
 2250         // queuelog splitter
 2251         static_cast<int>(m_pQueueLogSplitter->GetRelativeSashPosition() * 1000000000)
 2252     );
 2253 
 2254     COptions::Get()->SetOption(OPTION_MAINWINDOW_SPLITTER_POSITION, posString);
 2255 }
 2256 
 2257 bool CMainFrame::RestoreSplitterPositions()
 2258 {
 2259     if (wxGetKeyState(WXK_SHIFT) && wxGetKeyState(WXK_ALT) && wxGetKeyState(WXK_CONTROL)) {
 2260         return false;
 2261     }
 2262 
 2263     // top_pos bottom_height view_pos view_height_width local_pos remote_pos
 2264     std::wstring const positions = COptions::Get()->GetOption(OPTION_MAINWINDOW_SPLITTER_POSITION);
 2265     auto tokens = fz::strtok_view(positions, L" ");
 2266     if (tokens.size() < 6) {
 2267         return false;
 2268     }
 2269 
 2270     int values[6];
 2271     for (size_t i = 0; i < 6; ++i) {
 2272         values[i] = fz::to_integral(tokens[i], std::numeric_limits<int>::min());
 2273         if (values[i] == std::numeric_limits<int>::min()) {
 2274             return false;
 2275         }
 2276     }
 2277 
 2278     if (m_pTopSplitter) {
 2279         m_pTopSplitter->SetSashPosition(values[0]);
 2280     }
 2281     if (m_pBottomSplitter) {
 2282         m_pBottomSplitter->SetSashPosition(values[1]);
 2283     }
 2284 
 2285     if (m_pContextControl) {
 2286         double pos = static_cast<double>(values[2]) / 1000000000;
 2287         for (int i = 0; i < m_pContextControl->GetTabCount(); ++i) {
 2288             auto controls = m_pContextControl->GetControlsFromTabIndex(i);
 2289             if (!controls) {
 2290                 continue;
 2291             }
 2292             controls->SetSplitterPositions(std::make_tuple(pos, values[3], values[4]));
 2293         }
 2294     }
 2295 
 2296     double pos = static_cast<double>(values[5]) / 1000000000;
 2297     if (pos >= 0 && pos <= 1 && m_pQueueLogSplitter) {
 2298         m_pQueueLogSplitter->SetRelativeSashPosition(pos);
 2299     }
 2300 
 2301     return true;
 2302 }
 2303 
 2304 void CMainFrame::SetDefaultSplitterPositions()
 2305 {
 2306     if (m_pTopSplitter) {
 2307         m_pTopSplitter->SetSashPosition(97);
 2308     }
 2309 
 2310     wxSize size = m_pBottomSplitter->GetClientSize();
 2311     int h = size.GetHeight() - 135;
 2312     if (h < 50) {
 2313         h = 50;
 2314     }
 2315     if (m_pBottomSplitter) {
 2316         m_pBottomSplitter->SetSashPosition(h);
 2317     }
 2318 
 2319     if (m_pQueueLogSplitter) {
 2320         m_pQueueLogSplitter->SetSashPosition(0);
 2321     }
 2322 
 2323     if (m_pContextControl) {
 2324         for (int i = 0; i < m_pContextControl->GetTabCount(); ++i) {
 2325             CContextControl::_context_controls* controls = m_pContextControl->GetControlsFromTabIndex(i);
 2326             if (!controls) {
 2327                 continue;
 2328             }
 2329             if (controls->pViewSplitter) {
 2330                 controls->pViewSplitter->SetRelativeSashPosition(0.5);
 2331             }
 2332             if (controls->pLocalSplitter) {
 2333                 controls->pLocalSplitter->SetRelativeSashPosition(0.4);
 2334             }
 2335             if (controls->pRemoteSplitter) {
 2336                 controls->pRemoteSplitter->SetRelativeSashPosition(0.4);
 2337             }
 2338         }
 2339     }
 2340 }
 2341 
 2342 void CMainFrame::OnActivate(wxActivateEvent& event)
 2343 {
 2344     // According to the wx docs we should do this
 2345     event.Skip();
 2346 
 2347     if (!event.GetActive()) {
 2348         return;
 2349     }
 2350 
 2351 #if defined(__WXMAC__) && !wxCHECK_VERSION(3, 1, 0)
 2352     // wxMac looses focus information if the window becomes inactive.
 2353     // Restore focus to the previously focused child, otherwise focus ends up
 2354     // in the quickconnect bar.
 2355     // Go via ID of the last focused child to avoid issues with window lifetime.
 2356     if (m_lastFocusedChild != -1) {
 2357         m_winLastFocused = FindWindow(m_lastFocusedChild);
 2358     }
 2359 #endif
 2360 
 2361     CEditHandler* pEditHandler = CEditHandler::Get();
 2362     if (pEditHandler) {
 2363         pEditHandler->CheckForModifications(true);
 2364     }
 2365 
 2366     if (m_pAsyncRequestQueue) {
 2367         m_pAsyncRequestQueue->TriggerProcessing();
 2368     }
 2369 }
 2370 
 2371 void CMainFrame::OnToolbarComparison(wxCommandEvent&)
 2372 {
 2373     CState* pState = CContextManager::Get()->GetCurrentContext();
 2374     if (!pState) {
 2375         return;
 2376     }
 2377 
 2378     CComparisonManager* pComparisonManager = pState->GetComparisonManager();
 2379     if (pComparisonManager->IsComparing()) {
 2380         pComparisonManager->ExitComparisonMode();
 2381         return;
 2382     }
 2383 
 2384     if (!COptions::Get()->GetOptionVal(OPTION_FILEPANE_LAYOUT)) {
 2385         CContextControl::_context_controls* controls = m_pContextControl->GetCurrentControls();
 2386         if (!controls) {
 2387             return;
 2388         }
 2389 
 2390         if ((controls->pLocalListSearchPanel && controls->pLocalListSearchPanel->IsShown()) || (controls->pRemoteListSearchPanel && controls->pRemoteListSearchPanel->IsShown())) {
 2391             CConditionalDialog dlg(this, CConditionalDialog::quick_search, CConditionalDialog::yesno);
 2392             dlg.SetTitle(_("Directory comparison"));
 2393             dlg.AddText(_("To compare directories quick search must be closed."));
 2394             dlg.AddText(_("Close quick search and continue comparing?"));
 2395             if (!dlg.Run()) {
 2396                 return;
 2397             }
 2398 
 2399             if (controls->pLocalListSearchPanel && controls->pLocalListSearchPanel->IsShown()) {
 2400                 controls->pLocalListSearchPanel->Close();
 2401             }
 2402 
 2403             if (controls->pRemoteListSearchPanel && controls->pRemoteListSearchPanel->IsShown()) {
 2404                 controls->pRemoteListSearchPanel->Close();
 2405             }
 2406         }
 2407 
 2408         if ((controls->pLocalSplitter->IsSplit() && !controls->pRemoteSplitter->IsSplit()) ||
 2409             (!controls->pLocalSplitter->IsSplit() && controls->pRemoteSplitter->IsSplit()))
 2410         {
 2411             CConditionalDialog dlg(this, CConditionalDialog::compare_treeviewmismatch, CConditionalDialog::yesno);
 2412             dlg.SetTitle(_("Directory comparison"));
 2413             dlg.AddText(_("To compare directories, both file lists have to be aligned."));
 2414             dlg.AddText(_("To do this, the directory trees need to be both shown or both hidden."));
 2415             dlg.AddText(_("Show both directory trees and continue comparing?"));
 2416             if (!dlg.Run()) {
 2417                 // Needed to restore non-toggle state of button
 2418                 pState->NotifyHandlers(STATECHANGE_COMPARISON);
 2419                 return;
 2420             }
 2421 
 2422             ShowDirectoryTree(true, true);
 2423             ShowDirectoryTree(false, true);
 2424         }
 2425 
 2426         int pos = (controls->pLocalSplitter->GetSashPosition() + controls->pRemoteSplitter->GetSashPosition()) / 2;
 2427         controls->pLocalSplitter->SetSashPosition(pos);
 2428         controls->pRemoteSplitter->SetSashPosition(pos);
 2429     }
 2430 
 2431     pComparisonManager->CompareListings();
 2432 }
 2433 
 2434 void CMainFrame::OnToolbarComparisonDropdown(wxCommandEvent& event)
 2435 {
 2436     if (!m_pToolBar) {
 2437         return;
 2438     }
 2439 
 2440     auto menu = new wxMenu;
 2441     menu->Append(XRCID("ID_TOOLBAR_COMPARISON"), _("&Enable"), wxString(), wxITEM_CHECK);
 2442 
 2443     menu->AppendSeparator();
 2444     menu->Append(XRCID("ID_COMPARE_SIZE"), _("Compare file&size"), wxString(), wxITEM_RADIO);
 2445     menu->Append(XRCID("ID_COMPARE_DATE"), _("Compare &modification time"), wxString(), wxITEM_RADIO);
 2446 
 2447     menu->AppendSeparator();
 2448     menu->Append(XRCID("ID_COMPARE_HIDEIDENTICAL"), _("&Hide identical files"), wxString(), wxITEM_CHECK);
 2449 
 2450     CState* pState = CContextManager::Get()->GetCurrentContext();
 2451     if (!pState) {
 2452         return;
 2453     }
 2454 
 2455     CComparisonManager* pComparisonManager = pState->GetComparisonManager();
 2456     menu->FindItem(XRCID("ID_TOOLBAR_COMPARISON"))->Check(pComparisonManager->IsComparing());
 2457 
 2458     const int mode = COptions::Get()->GetOptionVal(OPTION_COMPARISONMODE);
 2459     if (mode == 0) {
 2460         menu->FindItem(XRCID("ID_COMPARE_SIZE"))->Check();
 2461     }
 2462     else {
 2463         menu->FindItem(XRCID("ID_COMPARE_DATE"))->Check();
 2464     }
 2465 
 2466     menu->Check(XRCID("ID_COMPARE_HIDEIDENTICAL"), COptions::Get()->GetOptionVal(OPTION_COMPARE_HIDEIDENTICAL) != 0);
 2467 
 2468     ShowDropdownMenu(menu, m_pToolBar, event);
 2469 }
 2470 
 2471 void CMainFrame::ShowDropdownMenu(wxMenu* pMenu, wxToolBar* pToolBar, wxCommandEvent& event)
 2472 {
 2473 #ifdef EVT_TOOL_DROPDOWN
 2474     if (event.GetEventType() == wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED) {
 2475         pToolBar->SetDropdownMenu(event.GetId(), pMenu);
 2476         event.Skip();
 2477     }
 2478     else
 2479 #endif
 2480     {
 2481 #ifdef __WXMSW__
 2482         RECT r;
 2483         if (::SendMessage((HWND)pToolBar->GetHandle(), TB_GETITEMRECT, pToolBar->GetToolPos(event.GetId()), (LPARAM)&r)) {
 2484             pToolBar->PopupMenu(pMenu, r.left, r.bottom);
 2485         }
 2486         else
 2487 #endif
 2488             pToolBar->PopupMenu(pMenu);
 2489 
 2490         delete pMenu;
 2491     }
 2492 }
 2493 
 2494 void CMainFrame::OnDropdownComparisonMode(wxCommandEvent& event)
 2495 {
 2496     CState* pState = CContextManager::Get()->GetCurrentContext();
 2497     if (!pState) {
 2498         return;
 2499     }
 2500 
 2501     int old_mode = COptions::Get()->GetOptionVal(OPTION_COMPARISONMODE);
 2502     int new_mode = (event.GetId() == XRCID("ID_COMPARE_SIZE")) ? 0 : 1;
 2503     COptions::Get()->SetOption(OPTION_COMPARISONMODE, new_mode);
 2504 
 2505     CComparisonManager* pComparisonManager = pState->GetComparisonManager();
 2506     if (old_mode != new_mode && pComparisonManager) {
 2507         pComparisonManager->SetComparisonMode(new_mode);
 2508         if (pComparisonManager->IsComparing()) {
 2509             pComparisonManager->CompareListings();
 2510         }
 2511     }
 2512 }
 2513 
 2514 void CMainFrame::OnDropdownComparisonHide(wxCommandEvent&)
 2515 {
 2516     CState* pState = CContextManager::Get()->GetCurrentContext();
 2517     if (!pState) {
 2518         return;
 2519     }
 2520 
 2521     bool old_mode = COptions::Get()->GetOptionVal(OPTION_COMPARE_HIDEIDENTICAL) != 0;
 2522     COptions::Get()->SetOption(OPTION_COMPARE_HIDEIDENTICAL, old_mode ? 0 : 1);
 2523 
 2524     CComparisonManager* pComparisonManager = pState->GetComparisonManager();
 2525     if (pComparisonManager) {
 2526         pComparisonManager->SetHideIdentical(old_mode ? 0 : 1);
 2527         if (pComparisonManager->IsComparing()) {
 2528             pComparisonManager->CompareListings();
 2529         }
 2530     }
 2531 }
 2532 
 2533 void CMainFrame::ProcessCommandLine()
 2534 {
 2535     CCommandLine const* pCommandLine = wxGetApp().GetCommandLine();
 2536     if (!pCommandLine) {
 2537         return;
 2538     }
 2539 
 2540     std::wstring local;
 2541     if (!(local = pCommandLine->GetOption(CCommandLine::local)).empty()) {
 2542 
 2543         if (!wxDir::Exists(local)) {
 2544             wxString str = _("Path not found:");
 2545             str += _T("\n") + local;
 2546             wxMessageBoxEx(str, _("Syntax error in command line"));
 2547             return;
 2548         }
 2549 
 2550         CState *pState = CContextManager::Get()->GetCurrentContext();
 2551         if (pState) {
 2552             pState->SetLocalDir(local);
 2553         }
 2554     }
 2555 
 2556     std::wstring site;
 2557     if (pCommandLine->HasSwitch(CCommandLine::sitemanager)) {
 2558         if (COptions::Get()->GetOptionVal(OPTION_STARTUP_ACTION) != 1) {
 2559             OpenSiteManager();
 2560         }
 2561     }
 2562     else if (!(site = pCommandLine->GetOption(CCommandLine::site)).empty()) {
 2563         auto const data = CSiteManager::GetSiteByPath(site);
 2564 
 2565         if (data.first) {
 2566             ConnectToSite(*data.first, data.second);
 2567         }
 2568     }
 2569 
 2570     std::wstring param = pCommandLine->GetParameter();
 2571     if (!param.empty()) {
 2572         std::wstring error;
 2573 
 2574         Site site;
 2575 
 2576         wxString logontype = pCommandLine->GetOption(CCommandLine::logontype);
 2577         if (logontype == _T("ask")) {
 2578             site.SetLogonType(LogonType::ask);
 2579         }
 2580         else if (logontype == _T("interactive")) {
 2581             site.SetLogonType(LogonType::interactive);
 2582         }
 2583 
 2584         CServerPath path;
 2585         if (!site.ParseUrl(param, 0, std::wstring(), std::wstring(), error, path)) {
 2586             wxString str = _("Parameter not a valid URL");
 2587             str += _T("\n") + error;
 2588             wxMessageBoxEx(error, _("Syntax error in command line"));
 2589         }
 2590 
 2591         if (COptions::Get()->GetOptionVal(OPTION_DEFAULT_KIOSKMODE) && site.credentials.logonType_ == LogonType::normal) {
 2592             site.SetLogonType(LogonType::ask);
 2593             CLoginManager::Get().RememberPassword(site);
 2594         }
 2595 
 2596         Bookmark bm;
 2597         bm.m_remoteDir = path;
 2598         ConnectToSite(site, bm);
 2599     }
 2600 }
 2601 
 2602 void CMainFrame::OnFilterRightclicked(wxCommandEvent&)
 2603 {
 2604     const bool active = CFilterManager::HasActiveFilters();
 2605 
 2606     CFilterManager::ToggleFilters();
 2607 
 2608     if (active == CFilterManager::HasActiveFilters()) {
 2609         return;
 2610     }
 2611 
 2612     CContextManager::Get()->NotifyAllHandlers(STATECHANGE_APPLYFILTER);
 2613 }
 2614 
 2615 #ifdef __WXMSW__
 2616 WXLRESULT CMainFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
 2617 {
 2618     if (nMsg == WM_DEVICECHANGE) {
 2619         // Let tree control handle device change message
 2620         // They get sent by Windows on adding or removing drive
 2621         // letters
 2622 
 2623         if (!m_pContextControl) {
 2624             return 0;
 2625         }
 2626 
 2627         for (int i = 0; i < m_pContextControl->GetTabCount(); ++i) {
 2628             CContextControl::_context_controls* controls = m_pContextControl->GetControlsFromTabIndex(i);
 2629             if (controls && controls->pLocalTreeView) {
 2630                 controls->pLocalTreeView->OnDevicechange(wParam, lParam);
 2631             }
 2632         }
 2633         return 0;
 2634     }
 2635     else if (nMsg == WM_DISPLAYCHANGE) {
 2636         // wxDisplay caches the display configuration and does not
 2637         // reset it if the display configuration changes.
 2638         // wxDisplay uses this strange factory design pattern and uses a wxModule
 2639         // to delete the factory on program shutdown.
 2640         //
 2641         // To reset the factory manually in response to WM_DISPLAYCHANGE,
 2642         // create another instance of the module and call its Exit() member.
 2643         // After that, the next call to a wxDisplay will create a new factory and
 2644         // get the new display layout from Windows.
 2645         //
 2646         // Note: Both the factory pattern as well as the dynamic object system
 2647         //     are perfect example of bad design.
 2648         //
 2649         wxModule* pDisplayModule = (wxModule*)wxCreateDynamicObject(_T("wxDisplayModule"));
 2650         if (pDisplayModule) {
 2651             pDisplayModule->Exit();
 2652             delete pDisplayModule;
 2653         }
 2654     }
 2655     return wxFrame::MSWWindowProc(nMsg, wParam, lParam);
 2656 }
 2657 #endif
 2658 
 2659 void CMainFrame::OnSyncBrowse(wxCommandEvent&)
 2660 {
 2661     CState* pState = CContextManager::Get()->GetCurrentContext();
 2662     if (!pState) {
 2663         return;
 2664     }
 2665 
 2666     pState->SetSyncBrowse(!pState->GetSyncBrowse());
 2667 }
 2668 
 2669 #ifndef __WXMAC__
 2670 void CMainFrame::OnIconize(wxIconizeEvent& event)
 2671 {
 2672 #ifdef __WXGTK__
 2673     if (m_taskbar_is_uniconizing) {
 2674         return;
 2675     }
 2676     if (m_taskBarIcon && m_taskBarIcon->IsIconInstalled()) { // Only way to uniconize is via the taskbar icon.
 2677         return;
 2678     }
 2679 #endif
 2680     if (!event.IsIconized()) {
 2681         if (m_taskBarIcon) {
 2682             m_taskBarIcon->RemoveIcon();
 2683         }
 2684         Show(true);
 2685 
 2686         if (m_pAsyncRequestQueue) {
 2687             m_pAsyncRequestQueue->TriggerProcessing();
 2688         }
 2689 
 2690         return;
 2691     }
 2692 
 2693     if (!COptions::Get()->GetOptionVal(OPTION_MINIMIZE_TRAY)) {
 2694         return;
 2695     }
 2696 
 2697     if (!m_taskBarIcon) {
 2698         m_taskBarIcon = new wxTaskBarIcon();
 2699         m_taskBarIcon->Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxTaskBarIconEventHandler(CMainFrame::OnTaskBarClick), 0, this);
 2700         m_taskBarIcon->Connect(wxEVT_TASKBAR_LEFT_UP, wxTaskBarIconEventHandler(CMainFrame::OnTaskBarClick), 0, this);
 2701         m_taskBarIcon->Connect(wxEVT_TASKBAR_RIGHT_UP, wxTaskBarIconEventHandler(CMainFrame::OnTaskBarClick), 0, this);
 2702     }
 2703 
 2704     bool installed;
 2705     if (!m_taskBarIcon->IsIconInstalled()) {
 2706         installed = m_taskBarIcon->SetIcon(CThemeProvider::GetIcon(_T("ART_FILEZILLA")), GetTitle());
 2707     }
 2708     else {
 2709         installed = true;
 2710     }
 2711 
 2712     if (installed) {
 2713         Show(false);
 2714     }
 2715 }
 2716 
 2717 void CMainFrame::OnTaskBarClick(wxTaskBarIconEvent&)
 2718 {
 2719 #ifdef __WXGTK__
 2720     if (m_taskbar_is_uniconizing)
 2721         return;
 2722     m_taskbar_is_uniconizing = true;
 2723 #endif
 2724 
 2725     if (m_taskBarIcon)
 2726         m_taskBarIcon->RemoveIcon();
 2727 
 2728     Show(true);
 2729     Iconize(false);
 2730 
 2731     if (m_pAsyncRequestQueue)
 2732         m_pAsyncRequestQueue->TriggerProcessing();
 2733 
 2734 #ifdef __WXGTK__
 2735     QueueEvent(new wxCommandEvent(fzEVT_TASKBAR_CLICK_DELAYED));
 2736 #endif
 2737 }
 2738 
 2739 #ifdef __WXGTK__
 2740 void CMainFrame::OnTaskBarClick_Delayed(wxCommandEvent&)
 2741 {
 2742     m_taskbar_is_uniconizing = false;
 2743 }
 2744 #endif
 2745 
 2746 #endif
 2747 
 2748 void CMainFrame::OnSearch(wxCommandEvent&)
 2749 {
 2750     CState* pState = CContextManager::Get()->GetCurrentContext();
 2751     if (!pState) {
 2752         return;
 2753     }
 2754 
 2755     CSearchDialog dlg(this, *pState, m_pQueueView);
 2756     if (!dlg.Load()) {
 2757         return;
 2758     }
 2759 
 2760     dlg.Run();
 2761 }
 2762 
 2763 void CMainFrame::PostInitialize()
 2764 {
 2765 #ifdef __WXMAC__
 2766     // Focus first control
 2767     NavigateIn(wxNavigationKeyEvent::IsForward);
 2768 #endif
 2769 
 2770 #if FZ_MANUALUPDATECHECK
 2771     // Need to do this after welcome screen to avoid simultaneous display of multiple dialogs
 2772     if (!m_pUpdater) {
 2773         update_dialog_timer_.SetOwner(this);
 2774         m_pUpdater = new CUpdater(*this, m_engineContext,
 2775             [this](CActiveNotification const& notification) {
 2776             UpdateActivityLed(notification.GetDirection());
 2777         }
 2778         );
 2779         m_pUpdater->Init();
 2780     }
 2781 #endif
 2782 
 2783     int const startupAction = COptions::Get()->GetOptionVal(OPTION_STARTUP_ACTION);
 2784     bool startupReconnect = startupAction == 2;
 2785 
 2786     if (startupAction == 1) {
 2787         OpenSiteManager();
 2788         startupReconnect = false;
 2789     }
 2790 
 2791     CCommandLine const* pCommandLine = wxGetApp().GetCommandLine();
 2792     if (pCommandLine && pCommandLine->BlocksReconnectAtStartup()) {
 2793         startupReconnect = false;
 2794     }
 2795 
 2796     if (m_pContextControl && startupReconnect) {
 2797         auto xml = COptions::Get()->GetOptionXml(OPTION_TAB_DATA);
 2798         pugi::xml_node tabs = xml.child("Tabs");
 2799         int i = 0;
 2800         for (auto tab = tabs.child("Tab"); tab; tab = tab.next_sibling("Tab")) {
 2801             if (tab.attribute("connected").as_int()) {
 2802                 auto controls = m_pContextControl->GetControlsFromTabIndex(i);
 2803 
 2804                 if (controls && controls->pState) {
 2805                     CState* pState = controls->pState;
 2806                     if (pState->IsRemoteConnected() || !pState->IsRemoteIdle()) {
 2807                         continue;
 2808                     }
 2809 
 2810                     Site site = pState->GetLastSite();
 2811                     CServerPath path = pState->GetLastServerPath();
 2812                     Bookmark bm;
 2813                     bm.m_remoteDir = path;
 2814                     if (!ConnectToSite(site, bm, pState)) {
 2815                         break;
 2816                     }
 2817                 }
 2818             }
 2819 
 2820             ++i;
 2821         }
 2822     }
 2823 }
 2824 
 2825 void CMainFrame::OnMenuNewTab(wxCommandEvent&)
 2826 {
 2827     if (m_pContextControl) {
 2828         m_pContextControl->CreateTab();
 2829     }
 2830 }
 2831 
 2832 void CMainFrame::OnMenuCloseTab(wxCommandEvent&)
 2833 {
 2834     if (!m_pContextControl) {
 2835         return;
 2836     }
 2837 
 2838     m_pContextControl->CloseTab(m_pContextControl->GetCurrentTab());
 2839 }
 2840 
 2841 void CMainFrame::OnToggleToolBar(wxCommandEvent& event)
 2842 {
 2843     COptions::Get()->SetOption(OPTION_TOOLBAR_HIDDEN, event.IsChecked() ? 0 : 1);
 2844 #ifdef __WXMAC__
 2845     if (m_pToolBar) {
 2846         m_pToolBar->Show(event.IsChecked());
 2847     }
 2848 #else
 2849     CreateMainToolBar();
 2850     if (m_pToolBar) {
 2851         m_pToolBar->UpdateToolbarState();
 2852     }
 2853     HandleResize();
 2854 #endif
 2855 }
 2856 
 2857 void CMainFrame::FixTabOrder()
 2858 {
 2859     if (m_pQuickconnectBar && m_pTopSplitter) {
 2860         m_pQuickconnectBar->MoveBeforeInTabOrder(m_pTopSplitter);
 2861     }
 2862 }
 2863 
 2864 #ifdef __WXMAC__
 2865 void CMainFrame::OnChildFocused(wxChildFocusEvent& event)
 2866 {
 2867     m_lastFocusedChild = event.GetWindow()->GetId();
 2868 }
 2869 #endif
 2870 
 2871 void CMainFrame::SetupKeyboardAccelerators()
 2872 {
 2873     std::vector<wxAcceleratorEntry> entries;
 2874     for (int i = 0; i < 10; i++) {
 2875         tab_hotkey_ids[i] = wxNewId();
 2876         entries.emplace_back(wxACCEL_CMD, (int)'0' + i, tab_hotkey_ids[i]);
 2877         entries.emplace_back(wxACCEL_ALT, (int)'0' + i, tab_hotkey_ids[i]);
 2878     }
 2879     entries.emplace_back(wxACCEL_CMD | wxACCEL_SHIFT, 'O', m_comparisonToggleAcceleratorId);
 2880     entries.emplace_back(wxACCEL_CMD | wxACCEL_SHIFT, 'I', XRCID("ID_MENU_VIEW_FILTERS"));
 2881     entries.emplace_back(wxACCEL_CMD, WXK_F5, XRCID("ID_REFRESH"));
 2882 #ifdef __WXMAC__
 2883     entries.emplace_back(wxACCEL_CMD, ',', XRCID("wxID_PREFERENCES"));
 2884 
 2885     keyboardCommands[wxNewId()] = std::make_pair([](wxTextEntry* e) { e->Cut(); }, 'X');
 2886     keyboardCommands[wxNewId()] = std::make_pair([](wxTextEntry* e) { e->Copy(); }, 'C');
 2887     keyboardCommands[wxNewId()] = std::make_pair([](wxTextEntry* e) { e->Paste(); }, 'V');
 2888     keyboardCommands[wxNewId()] = std::make_pair([](wxTextEntry* e) { e->SelectAll(); }, 'A');
 2889 
 2890     for (auto const& command : keyboardCommands) {
 2891         entries.emplace_back(wxACCEL_CMD, command.second.second, command.first);
 2892     }
 2893 
 2894     // Ctrl+(Shift+)Tab to switch between tabs
 2895     int id = wxNewId();
 2896     entries.emplace_back(wxACCEL_RAW_CTRL, '\t', id);
 2897     Bind(wxEVT_MENU, [this](wxEvent&) { if (m_pContextControl) { m_pContextControl->AdvanceTab(true); } }, id);
 2898     id = wxNewId();
 2899     entries.emplace_back(wxACCEL_RAW_CTRL | wxACCEL_SHIFT, '\t', id);
 2900     Bind(wxEVT_MENU, [this](wxEvent&) { if (m_pContextControl) { m_pContextControl->AdvanceTab(false); } }, id);
 2901 #endif
 2902     wxAcceleratorTable accel(entries.size(), &entries[0]);
 2903     SetAcceleratorTable(accel);
 2904 }
 2905 
 2906 void CMainFrame::OnOptionsChanged(changed_options_t const& options)
 2907 {
 2908     if (options.test(OPTION_FILEPANE_LAYOUT) || options.test(OPTION_FILEPANE_SWAP) || options.test(OPTION_MESSAGELOG_POSITION)) {
 2909         UpdateLayout();
 2910     }
 2911 
 2912     if (options.test(OPTION_ICONS_THEME) || options.test(OPTION_ICONS_SCALE)) {
 2913         CreateMainToolBar();
 2914         if (m_pToolBar) {
 2915             m_pToolBar->UpdateToolbarState();
 2916         }
 2917     }
 2918 }