//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// copyright            : (C) 2008 by Eran Ifrah
// file name            : frame.cpp
//
// -------------------------------------------------------------------------
// A
//              _____           _      _     _ _
//             /  __ \         | |    | |   (_) |
//             | /  \/ ___   __| | ___| |    _| |_ ___
//             | |    / _ \ / _  |/ _ \ |   | | __/ _ )
//             | \__/\ (_) | (_| |  __/ |___| | ||  __/
//              \____/\___/ \__,_|\___\_____/_|\__\___|
//
//                                                  F i l e
//
//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

#include "frame.h"

#include "BreakpointsView.hpp"
#include "ColoursAndFontsManager.h"
#include "CompilersDetectorManager.h"
#include "CompilersFoundDlg.h"
#include "Cxx/cpptoken.h"
#include "Debugger/DebuggerToolBar.h"
#include "Debugger/debuggermanager.h"
#include "FileSystemWorkspace/clFileSystemWorkspace.hpp"
#include "GCCMetadata.hpp"
#include "Notebook.h"
#include "NotebookNavigationDlg.h"
#include "SideBar.hpp"
#include "StdToWX.h"
#include "SwitchToWorkspaceDlg.h"
#include "WelcomePage.h"
#include "acceltabledlg.h"
#include "advanced_settings.h"
#include "app.h"
#include "async_executable_cmd.h"
#include "autoversion.h"
#include "batchbuilddlg.h"
#include "bitmap_loader.h"
#include "bookmark_manager.h"
#include "build_custom_targets_menu_manager.h"
#include "build_settings_config.h"
#include "buildtabsettingsdata.h"
#include "clAboutDialog.h"
#include "clBootstrapWizard.h"
#include "clCustomiseToolBarDlg.h"
#include "clEditorBar.h"
#include "clGotoAnythingManager.h"
#include "clInfoBar.h"
#include "clLocaleManager.hpp"
#include "clMainFrameHelper.h"
#include "clSTCHelper.hpp"
#include "clSingleChoiceDialog.h"
#include "clThemedTreeCtrl.h"
#include "clToolBarButtonBase.h"
#include "clWorkspaceManager.h"
#include "cl_aui_dock_art.h"
#include "cl_aui_tb_are.h"
#include "cl_aui_tool_stickness.h"
#include "cl_command_event.h"
#include "cl_config.h"
#include "cl_defs.h"
#include "cl_standard_paths.h"
#include "cl_unredo.h"
#include "code_completion_manager.h"
#include "configuration_manager_dlg.h"
#include "context_cpp.h"
#include "cpp_symbol_tree.h"
#include "debugcoredump.h"
#include "debuggerconfigtool.h"
#include "debuggerpane.h"
#include "debuggersettingsdlg.h"
#include "detachedpanesinfo.h"
#include "dirsaver.h"
#include "dockablepane.h"
#include "dockablepanemenumanager.h"
#include "drawingutils.h"
#include "editor_config.h"
#include "environmentconfig.h"
#include "event_notifier.h"
#include "exelocator.h"
#include "file_logger.h"
#include "filedroptarget.h"
#include "fileexplorer.h"
#include "fileutils.h"
#include "fileview.h"
#include "findresultstab.h"
#include "findusagetab.h"
#include "generalinfo.h"
#include "globals.h"
#include "imanager.h"
#include "language.h"
#include "localstable.h"
#include "macros.h"
#include "manager.h"
#include "menumanager.h"
#include "navigationmanager.h"
#include "newworkspacedlg.h"
#include "open_resource_dialog.h" // New open resource
#include "openwindowspanel.h"
#include "options_dlg2.h"
#include "plugin.h"
#include "pluginmanager.h"
#include "pluginmgrdlg.h"
#include "precompiled_header.h"
#include "project.h"
#include "quickdebugdlg.h"
#include "quickfindbar.h"
#include "renamesymboldlg.h"
#include "replaceinfilespanel.h"
#include "search_thread.h"
#include "sessionmanager.h"
#include "singleinstancethreadjob.h"
#include "symbol_tree.h"
#include "syntaxhighlightdlg.h"
#include "tabgroupdlg.h"
#include "tabgroupmanager.h"
#include "tabgroupspane.h"
#include "tags_parser_search_path_dlg.h"
#include "webupdatethread.h"
#include "workspacetab.h"
#include "wxCodeCompletionBoxManager.h"
#include "wxCustomControls.hpp"
#include "wxCustomStatusBar.h"

#include <algorithm>
#include <array>
#include <wx/bookctrl.h>
#include <wx/busyinfo.h>
#include <wx/dcbuffer.h>
#include <wx/msgdlg.h>
#include <wx/richmsgdlg.h>
#include <wx/settings.h>
#include <wx/splash.h>
#include <wx/stc/stc.h>
#include <wx/wupdlock.h>

#ifdef __WXGTK__
#include <gtk/gtk.h>
#endif

//////////////////////////////////////////////////

namespace
{
int FrameTimerId = wxNewId();
// return the wxBORDER_SIMPLE that matches the current application theme
wxBorder get_border_simple_theme_aware_bit()
{
#if defined(__WXMSW__)
    if (clSystemSettings::GetAppearance().IsDark()) {
        return wxBORDER_SIMPLE;
    } else {
        return wxBORDER_THEME;
    }
#elif defined(__WXMAC__)
    return wxBORDER_NONE;
#else
    return wxBORDER_DEFAULT;
#endif
} // get_border_simple_theme_aware_bit
} // namespace

const wxEventType wxEVT_LOAD_PERSPECTIVE = XRCID("load_perspective");
const wxEventType wxEVT_REFRESH_PERSPECTIVE_MENU = XRCID("refresh_perspective_menu");
const wxEventType wxEVT_ACTIVATE_EDITOR = XRCID("activate_editor");
const wxEventType wxEVT_LOAD_SESSION = ::wxNewEventType();

#define CHECK_SHUTDOWN()                                \
    {                                                   \
        if (ManagerST::Get()->IsShutdownInProgress()) { \
            return;                                     \
        }                                               \
    }
#ifdef __WXGTK__
#define FACTOR_1 0.0
#define FACTOR_2 0.0
#else
#define FACTOR_1 2.0
#define FACTOR_2 2.0
#endif

#define CODELITE_SET_BEST_FOCUS()                          \
    if (clGetManager()->GetActiveEditor()) {               \
        clGetManager()->GetActiveEditor()->SetActive();    \
    } else if (m_sidebar->IsShown()) {                     \
        m_sidebar->GrabFocus();                            \
    } else if (GetMainBook()->GetWelcomePage(false)) {     \
        GetMainBook()->GetWelcomePage(false)->GrabFocus(); \
    }

/**
 * @brief is the debugger running?
 */
namespace
{
bool IsDebuggerRunning()
{
    clDebugEvent eventIsRunning(wxEVT_DBG_IS_RUNNING);
    EventNotifier::Get()->ProcessEvent(eventIsRunning);
    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    return (dbgr && dbgr->IsRunning()) || eventIsRunning.IsAnswer();
}

bool codelite_initialised = false;
bool codelite_active = false;
} // namespace

//----------------------------------------------------------------
// Our main frame
//----------------------------------------------------------------
BEGIN_EVENT_TABLE(clMainFrame, wxFrame)
// ATTN: Any item that appears in more than one menu is
//       only listed once here, the first time it occurs

//---------------------------------------------------
// System events
//---------------------------------------------------
EVT_IDLE(clMainFrame::OnIdle)
EVT_CLOSE(clMainFrame::OnClose)
EVT_TIMER(FrameTimerId, clMainFrame::OnTimer)

//---------------------------------------------------
// File menu
//---------------------------------------------------
EVT_MENU(XRCID("new_file"), clMainFrame::OnFileNew)
EVT_MENU(XRCID("open_file"), clMainFrame::OnFileOpen)
EVT_MENU(XRCID("open_folder"), clMainFrame::OnFileOpenFolder)
EVT_MENU(XRCID("refresh_file"), clMainFrame::OnFileReload)
EVT_MENU(XRCID("load_tab_group"), clMainFrame::OnFileLoadTabGroup)
EVT_MENU(XRCID("save_file"), clMainFrame::OnSave)
EVT_MENU(XRCID("duplicate_tab"), clMainFrame::OnDuplicateTab)
EVT_MENU(XRCID("save_file_as"), clMainFrame::OnSaveAs)
EVT_MENU(XRCID("save_all"), clMainFrame::OnFileSaveAll)
EVT_MENU(XRCID("save_tab_group"), clMainFrame::OnFileSaveTabGroup)
EVT_MENU(XRCID("close_file"), clMainFrame::OnFileClose)
EVT_MENU(wxID_CLOSE_ALL, clMainFrame::OnFileCloseAll)
EVT_MENU_RANGE(RecentFilesSubMenuID, RecentFilesSubMenuID + 10, clMainFrame::OnRecentFile)
EVT_MENU_RANGE(RecentWorkspaceSubMenuID, RecentWorkspaceSubMenuID + 10, clMainFrame::OnRecentWorkspace)
EVT_MENU_RANGE(ID_MENU_CUSTOM_TARGET_FIRST, ID_MENU_CUSTOM_TARGET_MAX, clMainFrame::OnBuildCustomTarget)
EVT_MENU(wxID_EXIT, clMainFrame::OnQuit)
// print
EVT_MENU(wxID_PRINT, clMainFrame::OnPrint)
EVT_UPDATE_UI(wxID_PRINT, clMainFrame::OnFileExistUpdateUI)
EVT_MENU(wxID_PAGE_SETUP, clMainFrame::OnPageSetup)

EVT_UPDATE_UI(XRCID("refresh_file"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("save_file"), clMainFrame::OnFileSaveUI)
EVT_UPDATE_UI(XRCID("duplicate_tab"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("save_file_as"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("save_all"), clMainFrame::OnFileSaveAllUI)
EVT_UPDATE_UI(XRCID("save_tab_group"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("close_file"), clMainFrame::OnFileCloseUI)
EVT_UPDATE_UI(XRCID("recent_workspaces"), clMainFrame::OnRecentWorkspaceUI)

//--------------------------------------------------
// Edit menu
//--------------------------------------------------
EVT_MENU(wxID_UNDO, clMainFrame::DispatchCommandEvent)
EVT_MENU(wxID_REDO, clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("label_current_state"), clMainFrame::DispatchCommandEvent)
EVT_MENU(wxID_DUPLICATE, clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("delete_line"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("delete_line_end"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("delete_line_start"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("copy_line"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("cut_line"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("transpose_lines"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("trim_trailing"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("to_upper"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("to_lower"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("match_brace"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("select_to_brace"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("complete_word"), clMainFrame::OnCodeComplete)
EVT_MENU(XRCID("simple_word_completion"), clMainFrame::OnWordComplete)
EVT_MENU(XRCID("complete_word_refresh_list"), clMainFrame::OnCompleteWordRefreshList)
EVT_MENU(XRCID("function_call_tip"), clMainFrame::OnFunctionCalltip)
EVT_MENU(XRCID("convert_eol_win"), clMainFrame::OnConvertEol)
EVT_MENU(XRCID("convert_eol_unix"), clMainFrame::OnConvertEol)
EVT_MENU(XRCID("convert_eol_mac"), clMainFrame::OnConvertEol)
EVT_MENU(XRCID("move_line_down"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("move_line_up"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("center_line"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("center_line_roll"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("selection_to_multi_caret"), clMainFrame::OnSplitSelection)
EVT_MENU(XRCID("convert_indent_to_tabs"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("convert_indent_to_spaces"), clMainFrame::DispatchCommandEvent)
EVT_UPDATE_UI(XRCID("selection_to_multi_caret"), clMainFrame::OnSplitSelectionUI)

EVT_UPDATE_UI(wxID_UNDO, clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(wxID_REDO, clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(wxID_DUPLICATE, clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("delete_line"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("delete_line_end"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("delete_line_start"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("copy_line"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("cut_line"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("transpose_lines"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("trim_trailing"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("to_upper"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("to_lower"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("match_brace"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("select_to_brace"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("complete_word"), clMainFrame::OnCompleteWordUpdateUI)
EVT_UPDATE_UI(XRCID("simple_word_completion"), clMainFrame::OnCompleteWordUpdateUI)
EVT_UPDATE_UI(XRCID("function_call_tip"), clMainFrame::OnFunctionCalltipUI)
EVT_UPDATE_UI(XRCID("convert_eol_win"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("convert_eol_unix"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("convert_eol_mac"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("move_line_down"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("move_line_up"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("center_line_roll"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("convert_indent_to_tabs"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("convert_indent_to_spaces"), clMainFrame::OnFileExistUpdateUI)

//-------------------------------------------------------
// View menu
//-------------------------------------------------------
EVT_MENU(XRCID("restore_layout"), clMainFrame::OnRestoreDefaultLayout)
EVT_MENU(XRCID("word_wrap"), clMainFrame::OnViewWordWrap)
EVT_MENU(XRCID("toggle_fold"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("fold_all"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("fold_all_in_selection"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("fold_topmost_in_selection"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("display_eol"), clMainFrame::OnViewDisplayEOL)
EVT_MENU(XRCID("whitepsace_invisible"), clMainFrame::OnShowWhitespace)
EVT_MENU(XRCID("whitepsace_always"), clMainFrame::OnShowWhitespace)
EVT_MENU(XRCID("whitespace_visiable_after_indent"), clMainFrame::OnShowWhitespace)
EVT_MENU(XRCID("whitespace_indent_only"), clMainFrame::OnShowWhitespace)
EVT_MENU(XRCID("next_tab"), clMainFrame::OnNextTab)
EVT_MENU(XRCID("prev_tab"), clMainFrame::OnPrevTab)
EVT_MENU(XRCID("full_screen"), clMainFrame::OnShowFullScreen)
EVT_MENU(XRCID("show_terminal_pane"), clMainFrame::OnShowBuiltInTerminal)
EVT_MENU(XRCID("show_nav_toolbar"), clMainFrame::OnShowNavBar)
EVT_MENU(XRCID("toogle_main_toolbars"), clMainFrame::OnToggleMainTBars)
EVT_MENU(XRCID("toogle_plugin_toolbars"), clMainFrame::OnTogglePluginTBars)
EVT_MENU(XRCID("toggle_panes"), clMainFrame::OnTogglePanes)
EVT_MENU(XRCID("distraction_free_mode"), clMainFrame::OnToggleMinimalView)
EVT_UPDATE_UI(XRCID("distraction_free_mode"), clMainFrame::OnToggleMinimalViewUI)
EVT_MENU(XRCID("hide_status_bar"), clMainFrame::OnShowStatusBar)
EVT_UPDATE_UI(XRCID("hide_status_bar"), clMainFrame::OnShowStatusBarUI)
EVT_MENU(XRCID("hide_tool_bar"), clMainFrame::OnShowToolbar)
EVT_UPDATE_UI(XRCID("hide_tool_bar"), clMainFrame::OnShowToolbarUI)
EVT_MENU(XRCID("show_menu_bar"), clMainFrame::OnShowMenuBar)
EVT_UPDATE_UI(XRCID("show_menu_bar"), clMainFrame::OnShowMenuBarUI)
EVT_MENU(XRCID("show_tab_bar"), clMainFrame::OnShowTabBar)
EVT_UPDATE_UI(XRCID("show_tab_bar"), clMainFrame::OnShowTabBarUI)
EVT_MENU_RANGE(viewAsMenuItemID, viewAsMenuItemMaxID, clMainFrame::DispatchCommandEvent)
EVT_MENU(wxID_ZOOM_FIT, clMainFrame::DispatchCommandEvent)
EVT_MENU(wxID_ZOOM_IN, clMainFrame::DispatchCommandEvent)
EVT_MENU(wxID_ZOOM_OUT, clMainFrame::DispatchCommandEvent)
EVT_UPDATE_UI(wxID_ZOOM_OUT, clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(wxID_ZOOM_IN, clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(wxID_ZOOM_OUT, clMainFrame::OnFileExistUpdateUI)

EVT_UPDATE_UI(XRCID("word_wrap"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("toggle_fold"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("fold_all"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("fold_all_in_selection"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("fold_topmost_in_selection"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("display_eol"), clMainFrame::OnViewDisplayEOL_UI)
EVT_UPDATE_UI(XRCID("next_tab"), clMainFrame::OnNextPrevTab_UI)
EVT_UPDATE_UI(XRCID("prev_tab"), clMainFrame::OnNextPrevTab_UI)
EVT_UPDATE_UI(XRCID("whitepsace_invisible"), clMainFrame::OnShowWhitespaceUI)
EVT_UPDATE_UI(XRCID("whitepsace_always"), clMainFrame::OnShowWhitespaceUI)
EVT_UPDATE_UI(XRCID("whitespace_visiable_after_indent"), clMainFrame::OnShowWhitespaceUI)
EVT_UPDATE_UI(XRCID("whitespace_indent_only"), clMainFrame::OnShowWhitespaceUI)
EVT_UPDATE_UI(XRCID("show_nav_toolbar"), clMainFrame::OnShowNavBarUI)
EVT_UPDATE_UI(viewAsSubMenuID, clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI_RANGE(viewAsMenuItemID, viewAsMenuItemMaxID, clMainFrame::DispatchUpdateUIEvent)

//-------------------------------------------------------
// Search menu
//-------------------------------------------------------
EVT_MENU(XRCID("id_find"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("id_replace"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("find_resource"), clMainFrame::OnFindResourceXXX)
EVT_MENU(XRCID("incremental_search"), clMainFrame::OnIncrementalSearch)
EVT_MENU(XRCID("incremental_replace"), clMainFrame::OnIncrementalReplace)
EVT_MENU(XRCID("find_symbol"), clMainFrame::OnQuickOutline)
EVT_MENU(XRCID("goto_definition"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("goto_previous_definition"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("goto_linenumber"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("toggle_bookmark"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("next_bookmark"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("previous_bookmark"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("removeall_current_bookmarks"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("removeall_bookmarks"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("next_fif_match"), clMainFrame::OnNextFiFMatch)
EVT_MENU(XRCID("previous_fif_match"), clMainFrame::OnPreviousFiFMatch)
EVT_MENU(XRCID("grep_current_file"), clMainFrame::OnGrepWord)
EVT_MENU(XRCID("grep_current_workspace"), clMainFrame::OnGrepWord)
EVT_MENU(XRCID("web_search_selection"), clMainFrame::OnWebSearchSelection)
EVT_MENU(XRCID("ID_QUICK_ADD_NEXT"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("ID_QUICK_FIND_ALL"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("ID_GOTO_ANYTHING"), clMainFrame::OnGotoAnything)
EVT_MENU(XRCID("wxEVT_BOOK_NAV_NEXT"), clMainFrame::OnMainBookNavigating)
EVT_MENU(XRCID("wxEVT_BOOK_NAV_PREV"), clMainFrame::OnMainBookNavigating)
EVT_MENU(XRCID("wxEVT_BOOK_MOVE_TAB_LEFT"), clMainFrame::OnMainBookMovePage)
EVT_MENU(XRCID("wxEVT_BOOK_MOVE_TAB_RIGHT"), clMainFrame::OnMainBookMovePage)
EVT_UPDATE_UI(XRCID("ID_QUICK_ADD_NEXT"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("ID_QUICK_FIND_ALL"), clMainFrame::OnFileExistUpdateUI)

EVT_UPDATE_UI(XRCID("id_find"), clMainFrame::OnIncrementalSearchUI)
EVT_UPDATE_UI(XRCID("id_replace"), clMainFrame::OnIncrementalSearchUI)
EVT_UPDATE_UI(XRCID("select_previous"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("select_next"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("find_next"), clMainFrame::OnFileExistUpdateUI)
EVT_MENU(XRCID("find_next"), clMainFrame::OnFindNext)
EVT_UPDATE_UI(XRCID("find_previous"), clMainFrame::OnFileExistUpdateUI)
EVT_MENU(XRCID("find_previous"), clMainFrame::OnFindPrevious)
EVT_UPDATE_UI(XRCID("find_next_at_caret"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("find_previous_at_caret"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("incremental_search"), clMainFrame::OnIncrementalSearchUI)
EVT_UPDATE_UI(XRCID("find_resource"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("find_type"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("find_function"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("find_macro"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("find_typedef"), clMainFrame::OnWorkspaceOpen)

EVT_UPDATE_UI(XRCID("find_symbol"), clMainFrame::OnCompleteWordUpdateUI)
EVT_UPDATE_UI(XRCID("goto_definition"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("goto_previous_definition"), clMainFrame::DispatchUpdateUIEvent)
EVT_UPDATE_UI(XRCID("goto_linenumber"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("toggle_bookmark"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("next_bookmark"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("previous_bookmark"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("removeall_bookmarks"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("next_fif_match"), clMainFrame::OnNextFiFMatchUI)
EVT_UPDATE_UI(XRCID("previous_fif_match"), clMainFrame::OnPreviousFiFMatchUI)
EVT_UPDATE_UI(XRCID("grep_current_file"), clMainFrame::OnGrepWordUI)
EVT_UPDATE_UI(XRCID("grep_current_workspace"), clMainFrame::OnGrepWordUI)
EVT_UPDATE_UI(XRCID("web_search_selection"), clMainFrame::OnWebSearchSelectionUI)

//-------------------------------------------------------
// Project menu
//-------------------------------------------------------
EVT_MENU(XRCID("local_workspace_prefs"), clMainFrame::OnWorkspaceEditorPreferences)
EVT_MENU(XRCID("local_workspace_settings"), clMainFrame::OnWorkspaceSettings)
EVT_MENU(XRCID("new_workspace"), clMainFrame::OnProjectNewWorkspace)
EVT_MENU(XRCID("file_new_workspace"), clMainFrame::OnProjectNewWorkspace)
EVT_UPDATE_UI(XRCID("file_new_workspace"), clMainFrame::OnNewWorkspaceUI)
EVT_UPDATE_UI(XRCID("new_workspace"), clMainFrame::OnNewWorkspaceUI)
EVT_UPDATE_UI(XRCID("import_from_msvs"), clMainFrame::OnNewWorkspaceUI)
EVT_MENU(XRCID("switch_to_workspace"), clMainFrame::OnSwitchWorkspace)
EVT_MENU(XRCID("file_switch_to_workspace"), clMainFrame::OnSwitchWorkspace)
EVT_UPDATE_UI(XRCID("switch_to_workspace"), clMainFrame::OnSwitchWorkspaceUI)
EVT_UPDATE_UI(XRCID("file_switch_to_workspace"), clMainFrame::OnSwitchWorkspaceUI)
EVT_MENU(XRCID("close_workspace"), clMainFrame::OnCloseWorkspace)
EVT_MENU(XRCID("file_close_workspace"), clMainFrame::OnCloseWorkspace)
EVT_MENU(XRCID("reload_workspace"), clMainFrame::OnReloadWorkspace)
EVT_MENU(XRCID("import_from_msvs"), clMainFrame::OnImportMSVS)
EVT_MENU(XRCID("new_project"), clMainFrame::OnProjectNewProject)
EVT_MENU(XRCID("file_new_project"), clMainFrame::OnProjectNewProject)
EVT_MENU(XRCID("add_project"), clMainFrame::OnProjectAddProject)
EVT_MENU(XRCID("reconcile_project"), clMainFrame::OnReconcileProject)
EVT_MENU(XRCID("retag_workspace"), clMainFrame::OnRetagWorkspace)
EVT_MENU(XRCID("full_retag_workspace"), clMainFrame::OnRetagWorkspace)
EVT_MENU(XRCID("project_properties"), clMainFrame::OnShowActiveProjectSettings)
EVT_MENU(XRCID("set_active_project"), clMainFrame::OnSetActivePoject)

EVT_UPDATE_UI(XRCID("local_workspace_prefs"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("local_workspace_settings"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("close_workspace"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("file_close_workspace"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("reload_workspace"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("add_project"), clMainFrame::OnWorkspaceMenuUI)
EVT_UPDATE_UI(XRCID("file_new_project"), clMainFrame::OnWorkspaceOpen)
EVT_UPDATE_UI(XRCID("new_project"), clMainFrame::OnNewProjectUI)
EVT_UPDATE_UI(XRCID("reconcile_project"), clMainFrame::OnShowActiveProjectSettingsUI)
EVT_UPDATE_UI(XRCID("retag_workspace"), clMainFrame::OnRetagWorkspaceUI)
EVT_UPDATE_UI(XRCID("full_retag_workspace"), clMainFrame::OnRetagWorkspaceUI)
EVT_UPDATE_UI(XRCID("project_properties"), clMainFrame::OnShowActiveProjectSettingsUI)
EVT_UPDATE_UI(XRCID("set_active_project"), clMainFrame::OnSetActivePojectUI)

//-------------------------------------------------------
// Build menu
//-------------------------------------------------------
EVT_MENU(XRCID("execute_no_debug"), clMainFrame::OnExecuteNoDebug)
EVT_MENU(XRCID("stop_executed_program"), clMainFrame::OnStopExecutedProgram)
EVT_MENU(XRCID("build_active_project"), clMainFrame::OnBuildProject)
EVT_MENU(XRCID("build_active_project_only"), clMainFrame::OnBuildProjectOnly)
EVT_MENU(XRCID("compile_active_file"), clMainFrame::OnCompileFile)
EVT_MENU(XRCID("compile_active_file_project"), clMainFrame::OnCompileFileProject)
EVT_MENU(XRCID("clean_active_project"), clMainFrame::OnCleanProject)
EVT_MENU(XRCID("clean_active_project_only"), clMainFrame::OnCleanProjectOnly)
EVT_MENU(XRCID("stop_active_project_build"), clMainFrame::OnStopBuild)
EVT_MENU(XRCID("rebuild_active_project"), clMainFrame::OnRebuildProject)
EVT_MENU(XRCID("build_n_run_active_project"), clMainFrame::OnBuildAndRunProject)
EVT_MENU(XRCID("build_workspace"), clMainFrame::OnBuildWorkspace)
EVT_MENU(XRCID("clean_workspace"), clMainFrame::OnCleanWorkspace)
EVT_MENU(XRCID("rebuild_workspace"), clMainFrame::OnReBuildWorkspace)
EVT_MENU(XRCID("batch_build"), clMainFrame::OnBatchBuild)

EVT_UPDATE_UI(XRCID("execute_no_debug"), clMainFrame::OnExecuteNoDebugUI)
EVT_UPDATE_UI(XRCID("stop_executed_program"), clMainFrame::OnStopExecutedProgramUI)
EVT_UPDATE_UI(XRCID("build_active_project"), clMainFrame::OnBuildProjectUI)
EVT_UPDATE_UI(XRCID("compile_active_file"), clMainFrame::OnCompileFileUI)
EVT_UPDATE_UI(XRCID("compile_active_file_project"), clMainFrame::OnCompileFileUI)
EVT_UPDATE_UI(XRCID("clean_active_project"), clMainFrame::OnCleanProjectUI)
EVT_UPDATE_UI(XRCID("stop_active_project_build"), clMainFrame::OnStopBuildUI)
EVT_UPDATE_UI(XRCID("rebuild_active_project"), clMainFrame::OnBuildProjectUI)
EVT_UPDATE_UI(XRCID("build_n_run_active_project"), clMainFrame::OnBuildProjectUI)
EVT_UPDATE_UI(XRCID("build_workspace"), clMainFrame::OnBuildWorkspaceUI)
EVT_UPDATE_UI(XRCID("clean_workspace"), clMainFrame::OnCleanWorkspaceUI)
EVT_UPDATE_UI(XRCID("rebuild_workspace"), clMainFrame::OnReBuildWorkspaceUI)
EVT_UPDATE_UI(XRCID("batch_build"), clMainFrame::OnBatchBuildUI)

//-------------------------------------------------------
// Debug menu
//-------------------------------------------------------
EVT_MENU(XRCID("debugger_win_locals"), clMainFrame::OnShowDebuggerWindow)
EVT_MENU(XRCID("debugger_win_watches"), clMainFrame::OnShowDebuggerWindow)
EVT_MENU(XRCID("debugger_win_output"), clMainFrame::OnShowDebuggerWindow)
EVT_MENU(XRCID("debugger_win_threads"), clMainFrame::OnShowDebuggerWindow)
EVT_MENU(XRCID("debugger_win_callstack"), clMainFrame::OnShowDebuggerWindow)
EVT_MENU(XRCID("debugger_win_memory"), clMainFrame::OnShowDebuggerWindow)
EVT_MENU(XRCID("debugger_win_breakpoints"), clMainFrame::OnShowDebuggerWindow)
EVT_MENU(XRCID("debugger_win_asciiview"), clMainFrame::OnShowDebuggerWindow)
EVT_MENU(XRCID("debugger_win_disassemble"), clMainFrame::OnShowDebuggerWindow)
EVT_UPDATE_UI(XRCID("debugger_win_locals"), clMainFrame::OnShowDebuggerWindowUI)
EVT_UPDATE_UI(XRCID("debugger_win_watches"), clMainFrame::OnShowDebuggerWindowUI)
EVT_UPDATE_UI(XRCID("debugger_win_output"), clMainFrame::OnShowDebuggerWindowUI)
EVT_UPDATE_UI(XRCID("debugger_win_threads"), clMainFrame::OnShowDebuggerWindowUI)
EVT_UPDATE_UI(XRCID("debugger_win_callstack"), clMainFrame::OnShowDebuggerWindowUI)
EVT_UPDATE_UI(XRCID("debugger_win_memory"), clMainFrame::OnShowDebuggerWindowUI)
EVT_UPDATE_UI(XRCID("debugger_win_breakpoints"), clMainFrame::OnShowDebuggerWindowUI)
EVT_UPDATE_UI(XRCID("debugger_win_asciiview"), clMainFrame::OnShowDebuggerWindowUI)
EVT_UPDATE_UI(XRCID("debugger_win_disassemble"), clMainFrame::OnShowDebuggerWindowUI)
EVT_MENU(XRCID("start_debugger"), clMainFrame::OnDebug)
EVT_MENU(XRCID("restart_debugger"), clMainFrame::OnDebugRestart)
EVT_MENU(XRCID("attach_debugger"), clMainFrame::OnDebugAttach)
EVT_MENU(XRCID("pause_debugger"), clMainFrame::OnDebugCmd)
EVT_MENU(XRCID("stop_debugger"), clMainFrame::OnDebugStop)
EVT_MENU(XRCID("dbg_run_to_cursor"), clMainFrame::OnDebugRunToCursor)
EVT_MENU(XRCID("dbg_jump_cursor"), clMainFrame::OnDebugJumpToCursor)
EVT_MENU(XRCID("dbg_stepin"), clMainFrame::OnDebugCmd)
EVT_MENU(XRCID("dbg_stepi"), clMainFrame::OnDebugCmd)
EVT_MENU(XRCID("dbg_stepout"), clMainFrame::OnDebugCmd)
EVT_MENU(XRCID("dbg_enable_reverse_debug"), clMainFrame::OnToggleReverseDebugging)
EVT_MENU(XRCID("dbg_start_recording"), clMainFrame::OnToggleReverseDebuggingRecording)
EVT_MENU(XRCID("dbg_next"), clMainFrame::OnDebugCmd)
EVT_MENU(XRCID("dbg_nexti"), clMainFrame::OnDebugCmd)
EVT_MENU(XRCID("show_cursor"), clMainFrame::OnDebugCmd)
EVT_MENU(XRCID("add_breakpoint"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("insert_breakpoint"), clMainFrame::DispatchCommandEvent) // Toggles
EVT_MENU(XRCID("disable_all_breakpoints"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("enable_all_breakpoints"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("delete_all_breakpoints"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("insert_temp_breakpoint"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("insert_disabled_breakpoint"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("insert_cond_breakpoint"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("edit_breakpoint"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("show_breakpoint_dlg"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("insert_watchpoint"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("toggle_breakpoint_enabled_status"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("ignore_breakpoint"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("delete_breakpoint"), clMainFrame::DispatchCommandEvent)
EVT_MENU(XRCID("quick_debug"), clMainFrame::OnQuickDebug)
EVT_MENU(XRCID("debug_core_dump"), clMainFrame::OnDebugCoreDump)

EVT_UPDATE_UI(XRCID("start_debugger"), clMainFrame::OnDebugUI)
EVT_UPDATE_UI(XRCID("restart_debugger"), clMainFrame::OnDebugRestartUI)
EVT_UPDATE_UI(XRCID("stop_debugger"), clMainFrame::OnDebugStopUI)
EVT_UPDATE_UI(XRCID("dbg_stepin"), clMainFrame::OnDebugCmdUI)
EVT_UPDATE_UI(XRCID("dbg_stepi"), clMainFrame::OnDebugCmdUI)
EVT_UPDATE_UI(XRCID("dbg_stepout"), clMainFrame::OnDebugCmdUI)
EVT_UPDATE_UI(XRCID("dbg_next"), clMainFrame::OnDebugCmdUI)

EVT_UPDATE_UI(XRCID("dbg_enable_reverse_debug"), clMainFrame::OnToggleReverseDebuggingUI)
EVT_UPDATE_UI(XRCID("dbg_start_recording"), clMainFrame::OnToggleReverseDebuggingRecordingUI)
EVT_UPDATE_UI(XRCID("pause_debugger"), clMainFrame::OnDebugInterruptUI)
EVT_UPDATE_UI(XRCID("dbg_nexti"), clMainFrame::OnDebugStepInstUI)
EVT_UPDATE_UI(XRCID("show_cursor"), clMainFrame::OnDebugShowCursorUI)
EVT_UPDATE_UI(XRCID("dbg_run_to_cursor"), clMainFrame::OnDebugRunToCursorUI)
EVT_UPDATE_UI(XRCID("dbg_jump_cursor"), clMainFrame::OnDebugJumpToCursorUI)

EVT_UPDATE_UI(XRCID("insert_breakpoint"), clMainFrame::OnDebugManageBreakpointsUI)
EVT_UPDATE_UI(XRCID("disable_all_breakpoints"), clMainFrame::OnDebugManageBreakpointsUI)
EVT_UPDATE_UI(XRCID("enable_all_breakpoints"), clMainFrame::OnDebugManageBreakpointsUI)
EVT_UPDATE_UI(XRCID("delete_all_breakpoints"), clMainFrame::OnDebugManageBreakpointsUI)
EVT_UPDATE_UI(XRCID("quick_debug"), clMainFrame::OnQuickDebugUI)
EVT_UPDATE_UI(XRCID("debug_core_dump"), clMainFrame::OnQuickDebugUI)

//-------------------------------------------------------
// Plugins menu
//-------------------------------------------------------
EVT_MENU(XRCID("manage_plugins"), clMainFrame::OnManagePlugins)

//-------------------------------------------------------
// Settings menu
//-------------------------------------------------------
EVT_MENU(wxID_PREFERENCES, clMainFrame::OnViewOptions)
EVT_MENU(XRCID("syntax_highlight"), clMainFrame::OnSyntaxHighlight)
EVT_MENU(XRCID("configure_accelerators"), clMainFrame::OnConfigureAccelerators)
EVT_MENU(XRCID("add_envvar"), clMainFrame::OnAddEnvironmentVariable)
EVT_MENU(XRCID("advance_settings"), clMainFrame::OnAdvanceSettings)
EVT_MENU(XRCID("debuger_settings"), clMainFrame::OnDebuggerSettings)
EVT_MENU(XRCID("tags_options"), clMainFrame::OnCtagsOptions)

//-------------------------------------------------------
// Help menu
//-------------------------------------------------------
EVT_MENU(wxID_ABOUT, clMainFrame::OnAbout)
EVT_MENU(XRCID("wxID_REPORT_BUG"), clMainFrame::OnReportIssue)
EVT_MENU(XRCID("check_for_update"), clMainFrame::OnCheckForUpdate)
EVT_MENU(XRCID("run_setup_wizard"), clMainFrame::OnRunSetupWizard)

//-----------------------------------------------------------------
// Toolbar
//-----------------------------------------------------------------
EVT_MENU(XRCID("id_forward"), clMainFrame::OnBackwardForward)
EVT_MENU(XRCID("id_backward"), clMainFrame::OnBackwardForward)
EVT_MENU(XRCID("highlight_word"), clMainFrame::OnHighlightWord)
EVT_UPDATE_UI(XRCID("highlight_word"), clMainFrame::OnHighlightWordUI)

EVT_UPDATE_UI(XRCID("id_forward"), clMainFrame::OnBackwardForwardUI)
EVT_UPDATE_UI(XRCID("id_backward"), clMainFrame::OnBackwardForwardUI)

//-------------------------------------------------------
// Workspace Pane tab context menu
//-------------------------------------------------------
EVT_MENU(XRCID("detach_wv_tab"), clMainFrame::OnDetachWorkspaceViewTab)
EVT_MENU(XRCID("hide_wv_tab"), clMainFrame::OnHideWorkspaceViewTab)
EVT_MENU(XRCID("hide_ov_tab"), clMainFrame::OnHideOutputViewTab)

//-------------------------------------------------------
// Debugger Pane tab context menu
//-------------------------------------------------------
EVT_MENU(XRCID("detach_debugger_tab"), clMainFrame::OnDetachDebuggerViewTab)

//-------------------------------------------------------
// Editor tab context menu
//-------------------------------------------------------
EVT_MENU(XRCID("close_other_tabs"), clMainFrame::OnCloseAllButThis)
EVT_MENU(XRCID("close_tabs_to_the_right"), clMainFrame::OnCloseTabsToTheRight)
EVT_MENU(XRCID("copy_file_relative_path_to_workspace"), clMainFrame::OnCopyFilePathRelativeToWorkspace)
EVT_MENU(XRCID("copy_file_name"), clMainFrame::OnCopyFilePath)
EVT_MENU(XRCID("copy_file_path"), clMainFrame::OnCopyFilePathOnly)
EVT_MENU(XRCID("copy_file_name_only"), clMainFrame::OnCopyFileName)
EVT_MENU(XRCID("open_shell_from_filepath"), clMainFrame::OnOpenShellFromFilePath)
EVT_MENU(XRCID("open_file_explorer"), clMainFrame::OnOpenFileExplorerFromFilePath)
EVT_MENU(XRCID("ID_DETACH_EDITOR"), clMainFrame::OnDetachEditor)
EVT_MENU(XRCID("mark_readonly"), clMainFrame::OnMarkEditorReadonly)

EVT_UPDATE_UI(XRCID("mark_readonly"), clMainFrame::OnMarkEditorReadonlyUI)
EVT_UPDATE_UI(XRCID("copy_file_relative_path_to_workspace"), clMainFrame::OnCopyFilePathRelativeToWorkspaceUI)
EVT_UPDATE_UI(XRCID("copy_file_name"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("copy_file_path"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("open_shell_from_filepath"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("open_file_explorer"), clMainFrame::OnFileExistUpdateUI)
EVT_UPDATE_UI(XRCID("ID_DETACH_EDITOR"), clMainFrame::OnDetachEditorUI)

//-----------------------------------------------------------------
// Default editor context menu
//-----------------------------------------------------------------
EVT_MENU(wxID_DELETE, clMainFrame::DispatchCommandEvent)
EVT_UPDATE_UI(wxID_DELETE, clMainFrame::DispatchUpdateUIEvent)

//-----------------------------------------------------------------
// C++ editor context menu
//-----------------------------------------------------------------
EVT_MENU(XRCID("open_include_file"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("add_include_file"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("add_forward_decl"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("swap_files"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("find_decl"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("find_impl"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("go_to_function_start"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("go_to_next_function"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("insert_doxy_comment"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("setters_getters"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("move_impl"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("add_impl"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("add_multi_impl"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("find_references"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("comment_selection"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("comment_line"), clMainFrame::OnCppContextMenu)
EVT_MENU(XRCID("retag_file"), clMainFrame::OnCppContextMenu)

//-----------------------------------------------------------------
// Hyperlinks
//-----------------------------------------------------------------
EVT_HTML_LINK_CLICKED(wxID_ANY, clMainFrame::OnLinkClicked)
EVT_MENU(XRCID("link_action"), clMainFrame::OnStartPageEvent)

EVT_COMMAND(wxID_ANY, wxEVT_ACTIVATE_EDITOR, clMainFrame::OnActivateEditor)
EVT_MENU(XRCID("goto_codelite_download_url"), clMainFrame::OnGotoCodeLiteDownloadPage)

EVT_COMMAND(wxID_ANY, wxEVT_CMD_NEW_VERSION_AVAILABLE, clMainFrame::OnNewVersionAvailable)
EVT_COMMAND(wxID_ANY, wxEVT_CMD_VERSION_UPTODATE, clMainFrame::OnNewVersionAvailable)
EVT_COMMAND(wxID_ANY, wxEVT_CMD_VERSION_CHECK_ERROR, clMainFrame::OnVersionCheckError)

EVT_COMMAND(wxID_ANY, wxEVT_CMD_NEW_DOCKPANE, clMainFrame::OnNewDetachedPane)
EVT_COMMAND(wxID_ANY, wxEVT_CMD_DELETE_DOCKPANE, clMainFrame::OnDestroyDetachedPane)
END_EVENT_TABLE()

namespace
{
/// keep the initial startup background colour
/// we use this to detect any theme changes done to the system
/// the checks are done in the OnAppAcitvated event
wxColour startupBackgroundColour;

wxWindow* GetWindowFromEvent(MainBook* book, wxEvent& event)
{
    // Check if the menu contains the window to close, if not close the active tab
    auto menu = dynamic_cast<wxMenu*>(event.GetEventObject());
    while (menu && menu->GetParent()) {
        menu = menu->GetParent();
    }
    auto win = book->GetCurrentPage();
    if (menu && menu->GetClientData()) {
        win = reinterpret_cast<wxWindow*>(menu->GetClientData());
    }
    return win;
}

clEditor* GetEditorFromEvent(MainBook* book, wxEvent& event)
{
    // Check if the menu contains the window to close, if not close the active tab
    auto win = GetWindowFromEvent(book, event);
    return dynamic_cast<clEditor*>(win);
}

IEditor* GetIEditorFromEvent(MainBook* book, wxEvent& event)
{
    // Check if the menu contains the window to close, if not close the active tab
    auto win = GetWindowFromEvent(book, event);
    auto cl_editor = dynamic_cast<clEditor*>(win);
    if (!cl_editor) {
        return nullptr;
    }
    return dynamic_cast<IEditor*>(cl_editor);
}
} // namespace

clMainFrame* clMainFrame::m_theFrame = NULL;
bool clMainFrame::m_initCompleted = false;

clMainFrame::clMainFrame(
    wxWindow* pParent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style)
    : m_postBuildEndAction(ePostBuildEndAction::kNone)
    , m_cppMenu(NULL)
    , m_highlightWord(false)
    , m_workspaceRetagIsRequired(false)
    , m_bookmarksDropDownMenu(NULL)
    , m_singleInstanceThread(NULL)
#ifdef __WXGTK__
    , m_isWaylandSession(false)
#endif
    , m_webUpdate(NULL)
    , m_mainToolbar(NULL)
    , m_pluginsToolbar(NULL)
{
    GeneralInfo inf;
    EditorConfigST::Get()->ReadObject("GeneralInfo", &inf);

    // Ensure that the position is within the display coords
    if (inf.GetFramePosition().x < 0 || inf.GetFramePosition().y < 0) {
        inf.SetFramePosition({ 100, 100 });
    }

    wxPoint create_point = inf.GetFramePosition();
#ifdef __WXMSW__
    // on Windows, create the frame outside of the display
    create_point = wxPoint{ -20000, -20000 };
#endif

    if (!wxFrame::Create(pParent, id, title, create_point, inf.GetFrameSize(), style)) {
        return;
    }

    // constuct the UI
    m_frameGeneralInfo = inf;
    Construct();
}

clMainFrame::~clMainFrame(void)
{
    wxDELETE(m_singleInstanceThread);
    wxDELETE(m_webUpdate);

#ifndef __WXMSW__ // show the main panel
    m_mainPanel->Show();
    m_zombieReaper.Stop();
#endif

    // Free the code completion manager
    CodeCompletionManager::Release();

    m_infoBar->Unbind(wxEVT_BUTTON, &clMainFrame::OnInfobarButton, this);
    wxTheApp->Unbind(wxEVT_ACTIVATE_APP, &clMainFrame::OnAppActivated, this);
    wxTheApp->Disconnect(
        wxID_COPY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(clMainFrame::DispatchCommandEvent), NULL, this);
    wxTheApp->Disconnect(
        wxID_PASTE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(clMainFrame::DispatchCommandEvent), NULL, this);
    wxTheApp->Disconnect(wxID_SELECTALL,
                         wxEVT_COMMAND_MENU_SELECTED,
                         wxCommandEventHandler(clMainFrame::DispatchCommandEvent),
                         NULL,
                         this);
    wxTheApp->Disconnect(
        wxID_CUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(clMainFrame::DispatchCommandEvent), NULL, this);
    wxTheApp->Disconnect(
        wxID_COPY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::DispatchUpdateUIEvent), NULL, this);
    wxTheApp->Disconnect(
        wxID_PASTE, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::DispatchUpdateUIEvent), NULL, this);
    wxTheApp->Disconnect(
        wxID_SELECTALL, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::DispatchUpdateUIEvent), NULL, this);
    wxTheApp->Disconnect(
        wxID_CUT, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::DispatchUpdateUIEvent), NULL, this);
    EventNotifier::Get()->Unbind(
        wxEVT_ENVIRONMENT_VARIABLES_MODIFIED, &clMainFrame::OnEnvironmentVariablesModified, this);
    EventNotifier::Get()->Unbind(wxEVT_BUILD_PROCESS_ENDED, &clMainFrame::OnBuildEnded, this);
    EventNotifier::Get()->Disconnect(wxEVT_LOAD_SESSION, wxCommandEventHandler(clMainFrame::OnLoadSession), NULL, this);
    EventNotifier::Get()->Unbind(wxEVT_WORKSPACE_LOADED, &clMainFrame::OnWorkspaceLoaded, this);
    EventNotifier::Get()->Unbind(wxEVT_WORKSPACE_CLOSED, &clMainFrame::OnWorkspaceClosed, this);
    EventNotifier::Get()->Disconnect(
        wxEVT_CL_THEME_CHANGED, wxCommandEventHandler(clMainFrame::OnThemeChanged), NULL, this);
    EventNotifier::Get()->Disconnect(
        wxEVT_ACTIVE_EDITOR_CHANGED, wxCommandEventHandler(clMainFrame::OnActiveEditorChanged), NULL, this);
    EventNotifier::Get()->Unbind(
        wxEVT_EDITOR_SETTINGS_CHANGED, wxCommandEventHandler(clMainFrame::OnSettingsChanged), this);
    EventNotifier::Get()->Unbind(wxEVT_CMD_RELOAD_EXTERNALLY_MODIFIED_NOPROMPT,
                                 wxCommandEventHandler(clMainFrame::OnReloadExternallModifiedNoPrompt),
                                 this);
    EventNotifier::Get()->Unbind(
        wxEVT_CMD_SINGLE_INSTANCE_THREAD_OPEN_FILES, &clMainFrame::OnSingleInstanceOpenFiles, this);
    EventNotifier::Get()->Unbind(wxEVT_CMD_SINGLE_INSTANCE_THREAD_RAISE_APP, &clMainFrame::OnSingleInstanceRaise, this);

    EventNotifier::Get()->Unbind(
        wxEVT_CMD_RELOAD_EXTERNALLY_MODIFIED, wxCommandEventHandler(clMainFrame::OnReloadExternallModified), this);

    if (m_mainToolbar) {
        m_mainToolbar->Unbind(wxEVT_TOOL, &clMainFrame::OnTBUnRedo, this, wxID_UNDO);
        m_mainToolbar->Unbind(wxEVT_TOOL, &clMainFrame::OnTBUnRedo, this, wxID_REDO);
        m_mainToolbar->Unbind(wxEVT_TOOL_DROPDOWN, &clMainFrame::OnTBUnRedoMenu, this, wxID_UNDO);
        m_mainToolbar->Unbind(wxEVT_TOOL_DROPDOWN, &clMainFrame::OnTBUnRedoMenu, this, wxID_REDO);
    }
    EventNotifier::Get()->Disconnect(
        wxEVT_PROJ_RENAMED, clCommandEventHandler(clMainFrame::OnProjectRenamed), NULL, this);
    wxDELETE(m_timer);
    EventNotifier::Get()->Unbind(wxEVT_SYS_COLOURS_CHANGED, &clMainFrame::OnSysColoursChanged, this);

    EventNotifier::Get()->Unbind(wxEVT_DEBUG_STARTED, &clMainFrame::OnDebugStarted, this);
    EventNotifier::Get()->Unbind(wxEVT_DEBUG_ENDED, &clMainFrame::OnDebugEnded, this);
    EventNotifier::Get()->Unbind(wxEVT_QUICK_DEBUG, &clMainFrame::OnStartQuickDebug, this);

    Unbind(wxEVT_MENU, &clMainFrame::OnMainToolBarPlaceTop, this, XRCID("toolbar_top"));
    Unbind(wxEVT_MENU, &clMainFrame::OnMainToolBarPlaceLeft, this, XRCID("toolbar_left"));
    Unbind(wxEVT_MENU, &clMainFrame::OnMainToolBarPlaceBottom, this, XRCID("toolbar_bottom"));
    Unbind(wxEVT_MENU, &clMainFrame::OnMainToolBarPlaceRight, this, XRCID("toolbar_right"));
    Unbind(wxEVT_MENU, &clMainFrame::OnMainToolBarHide, this, XRCID("toolbar_hidden"));

    Unbind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarHideUI, this, XRCID("toolbar_hidden"));
    Unbind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarPlaceTopUI, this, XRCID("toolbar_top"));
    Unbind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarPlaceLeftUI, this, XRCID("toolbar_left"));
    Unbind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarPlaceBottomUI, this, XRCID("toolbar_bottom"));
    Unbind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarPlaceRightUI, this, XRCID("toolbar_right"));

    // GetPerspectiveManager().DisconnectEvents() assumes that m_mgr is still alive (and it should be as it is allocated
    // on the stack of clMainFrame)
    ManagerST::Get()->GetPerspectiveManager().DisconnectEvents();

    ManagerST::Free();
    delete m_DPmenuMgr;

    // uninitialize AUI manager
    m_mgr.UnInit();

    // Remove the temporary folder and its content
    clStandardPaths::Get().RemoveTempDir();

    //  Finalize the shutdown
    CodeLiteApp::FinalizeShutdown();

    // Exit
    wxTheApp->ExitMainLoop();
}

void clMainFrame::Construct()
{
    // set the revision number in the frame title
    wxString title(_("CodeLite "));
    title << CODELITE_VERSION_STRING;

    // initialize the environment variable configuration manager
    EnvironmentConfig::Instance()->Load();

    /// keep the initial background colour
    startupBackgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);

    // Pass the docking manager to the plugin-manager
    PluginManager::Get()->SetDockingManager(&m_mgr);

    long value = EditorConfigST::Get()->GetInteger("highlight_word", 1);
    m_highlightWord = (bool)value;

    // Initialize the frame helper
    m_frameHelper = std::make_unique<clMainFrameHelper>(this, &m_mgr);
    CreateGUIControls();

    ManagerST::Get(); // Dummy call

    // allow the main frame to receive files by drag and drop
    SetDropTarget(new FileDropTarget());

    // Start the search thread
    SearchThreadST::Get()->SetNotifyWindow(EventNotifier::Get());
    SearchThreadST::Get()->Start(WXTHREAD_MIN_PRIORITY);

    // Create the single instance thread
    m_singleInstanceThread = new clSingleInstanceThread();
    m_singleInstanceThread->Start();

    // start the editor creator thread
    m_timer = new wxTimer(this, FrameTimerId);

    // connect common edit events
    wxTheApp->Connect(
        wxID_COPY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(clMainFrame::DispatchCommandEvent), NULL, this);
    wxTheApp->Connect(
        wxID_PASTE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(clMainFrame::DispatchCommandEvent), NULL, this);
    wxTheApp->Connect(wxID_SELECTALL,
                      wxEVT_COMMAND_MENU_SELECTED,
                      wxCommandEventHandler(clMainFrame::DispatchCommandEvent),
                      NULL,
                      this);
    wxTheApp->Connect(
        wxID_CUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(clMainFrame::DispatchCommandEvent), NULL, this);

    wxTheApp->Connect(
        wxID_COPY, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::DispatchUpdateUIEvent), NULL, this);
    wxTheApp->Connect(
        wxID_PASTE, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::DispatchUpdateUIEvent), NULL, this);
    wxTheApp->Connect(
        wxID_SELECTALL, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::DispatchUpdateUIEvent), NULL, this);
    wxTheApp->Connect(
        wxID_CUT, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::DispatchUpdateUIEvent), NULL, this);
    wxTheApp->Bind(wxEVT_ACTIVATE_APP, &clMainFrame::OnAppActivated, this);
    EventNotifier::Get()->Bind(
        wxEVT_ENVIRONMENT_VARIABLES_MODIFIED, &clMainFrame::OnEnvironmentVariablesModified, this);
    EventNotifier::Get()->Connect(wxEVT_LOAD_SESSION, wxCommandEventHandler(clMainFrame::OnLoadSession), NULL, this);
    EventNotifier::Get()->Bind(wxEVT_BUILD_PROCESS_ENDED, &clMainFrame::OnBuildEnded, this);
    EventNotifier::Get()->Bind(wxEVT_WORKSPACE_LOADED, &clMainFrame::OnWorkspaceLoaded, this);
    EventNotifier::Get()->Bind(wxEVT_WORKSPACE_CLOSED, &clMainFrame::OnWorkspaceClosed, this);
    EventNotifier::Get()->Connect(
        wxEVT_CL_THEME_CHANGED, wxCommandEventHandler(clMainFrame::OnThemeChanged), NULL, this);
    EventNotifier::Get()->Connect(
        wxEVT_ACTIVE_EDITOR_CHANGED, wxCommandEventHandler(clMainFrame::OnActiveEditorChanged), NULL, this);
    EventNotifier::Get()->Bind(
        wxEVT_EDITOR_SETTINGS_CHANGED, wxCommandEventHandler(clMainFrame::OnSettingsChanged), this);
    EventNotifier::Get()->Bind(wxEVT_CMD_RELOAD_EXTERNALLY_MODIFIED_NOPROMPT,
                               wxCommandEventHandler(clMainFrame::OnReloadExternallModifiedNoPrompt),
                               this);
    EventNotifier::Get()->Bind(
        wxEVT_CMD_RELOAD_EXTERNALLY_MODIFIED, wxCommandEventHandler(clMainFrame::OnReloadExternallModified), this);
    EventNotifier::Get()->Bind(
        wxEVT_CMD_SINGLE_INSTANCE_THREAD_OPEN_FILES, &clMainFrame::OnSingleInstanceOpenFiles, this);
    EventNotifier::Get()->Bind(wxEVT_CMD_SINGLE_INSTANCE_THREAD_RAISE_APP, &clMainFrame::OnSingleInstanceRaise, this);
    if (m_mainToolbar) {
        m_mainToolbar->Bind(wxEVT_TOOL, &clMainFrame::OnTBUnRedo, this, wxID_UNDO);
        m_mainToolbar->Bind(wxEVT_TOOL, &clMainFrame::OnTBUnRedo, this, wxID_REDO);
        m_mainToolbar->Bind(wxEVT_TOOL_DROPDOWN, &clMainFrame::OnTBUnRedoMenu, this, wxID_UNDO);
        m_mainToolbar->Bind(wxEVT_TOOL_DROPDOWN, &clMainFrame::OnTBUnRedoMenu, this, wxID_REDO);
    }

    EventNotifier::Get()->Connect(wxEVT_PROJ_RENAMED, clCommandEventHandler(clMainFrame::OnProjectRenamed), NULL, this);

    EventNotifier::Get()->Bind(wxEVT_DEBUG_STARTED, &clMainFrame::OnDebugStarted, this);
    EventNotifier::Get()->Bind(wxEVT_DEBUG_ENDED, &clMainFrame::OnDebugEnded, this);
    m_infoBar->Bind(wxEVT_BUTTON, &clMainFrame::OnInfobarButton, this);
    EventNotifier::Get()->Bind(wxEVT_QUICK_DEBUG, &clMainFrame::OnStartQuickDebug, this);
    EventNotifier::Get()->Bind(wxEVT_SYS_COLOURS_CHANGED, &clMainFrame::OnSysColoursChanged, this);

    Bind(wxEVT_MENU, &clMainFrame::OnMainToolBarHide, this, XRCID("toolbar_hidden"));
    Bind(wxEVT_MENU, &clMainFrame::OnMainToolBarPlaceTop, this, XRCID("toolbar_top"));
    Bind(wxEVT_MENU, &clMainFrame::OnMainToolBarPlaceLeft, this, XRCID("toolbar_left"));
    Bind(wxEVT_MENU, &clMainFrame::OnMainToolBarPlaceBottom, this, XRCID("toolbar_bottom"));
    Bind(wxEVT_MENU, &clMainFrame::OnMainToolBarPlaceRight, this, XRCID("toolbar_right"));

    Bind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarHideUI, this, XRCID("toolbar_hidden"));
    Bind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarPlaceTopUI, this, XRCID("toolbar_top"));
    Bind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarPlaceLeftUI, this, XRCID("toolbar_left"));
    Bind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarPlaceBottomUI, this, XRCID("toolbar_bottom"));
    Bind(wxEVT_UPDATE_UI, &clMainFrame::OnMainToolBarPlaceRightUI, this, XRCID("toolbar_right"));

    // Start the code completion manager, we do this by calling it once
    CodeCompletionManager::Get();

    // Register keyboard shortcuts
    AddKeyboardAccelerators();

#ifdef __WXGTK__
    // Try to detect if this is a Wayland session; we have some Wayland-workaround code
    m_isWaylandSession = clIsWaylandSession();
#endif
    PostConstruct();
}

void clMainFrame::PostConstruct()
{
    CreateWelcomePage();

#if !wxUSE_NATIVE_MENUBAR
    GetSizer()->Insert(0, m_mainMenuBar, 0, wxEXPAND);
#endif

    m_pluginsToolbar->Realize();
    GetSizer()->Insert(0, m_pluginsToolbar, 0, wxEXPAND);

    GetMainBook()->Show();
    CallAfter(&clMainFrame::CompleteInitialization);

    // Post wxEVT_SYS_COLOURS_CHANGED event to make sure that all of our controls are aligned with the colour
    clCommandEvent evtColoursChanged(wxEVT_SYS_COLOURS_CHANGED);
    EventNotifier::Get()->AddPendingEvent(evtColoursChanged);
}

void clMainFrame::Initialize(bool loadLastSession)
{
    m_theFrame = new clMainFrame(NULL,
                                 wxID_ANY,
                                 "CodeLite",
                                 wxDefaultPosition,
                                 wxDefaultSize,
                                 wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
    m_theFrame->m_loadLastSession = loadLastSession;
}

void clMainFrame::AddKeyboardAccelerators()
{
    clKeyboardManager* mgr = clKeyboardManager::Get();
    mgr->AddAccelerator(_("Build"),
                        { { "configuration_manager", _("Configuration Manager...") },
                          { "batch_build", _("Batch Build...") },
                          { "execute_no_debug", _("Run"), "Ctrl-F5" },
                          { "stop_executed_program", _("Stop") },
                          { "build_active_project", _("Build Project"), "F7" },
                          { "build_workspace", _("Build Workspace"), "Ctrl-Shift-B" },
                          { "rebuild_workspace", _("Rebuild Workspace") },
                          { "clean_workspace", _("Clean Workspace") },
                          { "compile_active_file", _("Compile Current File"), "Ctrl-F7" },
                          { "compile_active_file_project", _("Compile Current File's Project"), "Ctrl-Shift-F7" },
                          { "clean_active_project", _("Clean Project") },
                          { "stop_active_project_build", _("Stop Build") },
                          { "rebuild_active_project", _("Rebuild Project") },
                          { "build_n_run_active_project", _("Build and Run Project"), "Ctrl-F9" },
                          { "next_build_error", _("Next Build Error"), "F4" } });
    mgr->AddAccelerator(_("C++"),
                        { { "swap_files", _("Swap Header/Implementation file"), "F12" },
                          { "find_decl", _("Goto Declaration") },
                          { "find_impl", _("Goto Implementation") },
                          { "open_include_file", _("Open Include File") } });
    mgr->AddAccelerator(_("C++ | Code Generation / Refactoring"),
                        { { "setters_getters", _("Generate Setters/Getters...") },
                          { "move_impl", _("Move Function Implementation to...") },
                          { "add_impl", _("Add Function Implementation...") },
                          { "add_multi_impl", _("Implement all Un-implemented Functions...") } });
    mgr->AddAccelerator(_("Debugger"),
                        { { "start_debugger", _("Start/Continue Debugger"), "F5" },
                          { "restart_debugger", _("Restart Debugger"), "Ctrl-Shift-F5" },
                          { "attach_debugger", _("Attach to process...") },
                          { "pause_debugger", _("Pause debugger") },
                          { "stop_debugger", _("Stop debugger"), "Shift-F5" },
                          { "dbg_stepin", _("Step Into"), "F11" },
                          { "dbg_stepout", _("Step Out"), "Shift-F11" },
                          { "dbg_stepi", _("Step Into Instruction") },
                          { "dbg_next", _("Next"), "F10" },
                          { "dbg_nexti", _("Next Instruction"), "Ctrl-F10" },
                          { "show_cursor", _("Show Cursor") },
                          { "dbg_run_to_cursor", _("Run to Caret Line") },
                          { "dbg_jump_cursor", _("Jump to Caret Line") },
                          { "insert_breakpoint", _("Toggle Breakpoint"), "F9" },
                          { "disable_all_breakpoints", _("Disable All Breakpoints") },
                          { "enable_all_breakpoints", _("Enable All Breakpoints") },
                          { "delete_all_breakpoints", _("Delete All Breakpoints") },
                          { "quick_debug", _("Quick Debug...") } });
    mgr->AddAccelerator(
        _("Edit"),
        { { "wxID_UNDO", _("Undo"), "Ctrl-Z" },
          { "wxID_REDO", _("Redo"), "Ctrl-Y" },
          { "wxID_CUT", _("Cut"), "Ctrl-X" },
          { "wxID_COPY", _("Copy"), "Ctrl-C" },
          { "wxID_PASTE", _("Paste"), "Ctrl-V" },
          { "wxID_DUPLICATE", _("Duplicate Selection / Line"), "Ctrl-D" },
          { "delete_line_end", _("Delete to Line End") },
          { "delete_line_start", _("Delete to Line Start") },
          { "delete_line", _("Delete Line"), "Ctrl-L" },
          { "copy_line", _("Copy Line") },
          { "cut_line", _("Cut Line") },
          { "selection_to_multi_caret", _("Split selection into lines"), "Ctrl-Shift-L" },
          { "to_lower", _("Make Lowercase"), "Ctrl-U" },
          { "to_upper", _("Make Uppercase"), "Ctrl-Shift-U" },
          { "insert_doxy_comment", _("Insert Comment Block"), "Ctrl-Shift-D" },
          { "comment_line", _("Comment Line"), "Ctrl-/" },
          { "comment_selection", _("Comment Selection"), "Ctrl-Shift-/" },
          { "transpose_lines", _("Transpose Lines"), "Ctrl-T" },
          { "move_line_up", _("Move Line Up"), "Ctrl-Shift-UP" },
          { "move_line_down", _("Move Line Down"), "Ctrl-Shift-DOWN" },
          { "convert_indent_to_tabs", _("Convert Indentation to Tabs") },
          { "convert_indent_to_spaces", _("Convert Indentation to Spaces") },
          { "center_line_roll", _("Center Line in Editor") },
          { "wxID_SELECTALL", _("Select All"), "Ctrl-A" },
          { "match_brace", _("Match Brace"), "Ctrl-]" },
          { "select_to_brace", _("Select to Brace"), "Ctrl-Shift-E" },
          { "complete_word", _("Code Complete"), "Ctrl-SPACE" },
          { "simple_word_completion", _("Complete Word"), "Ctrl-ENTER" },
          { "function_call_tip", _("Display Function Calltip"), "Ctrl-Shift-SPACE" },
          { "convert_eol_win", _("Convert to Windows Format") },
          { "convert_eol_unix", _("Convert to Unix Format") },
          { "trim_trailing", _("Trim Trailing Spaces") },
          { "copy_file_relative_path_to_workspace", _("Copy Path Relative to Workspace"), "Ctrl-Alt-Shift-C" },
          { "copy_file_name", _("Copy Path to Clipboard") },
          { "copy_file_path", _("Copy Full Path to Clipboard") },
          { "copy_file_name_only", _("Copy File Name to Clipboard") } });
    mgr->AddAccelerator(_("File"),
                        { { "new_file", _("New File"), "Ctrl-N" },
                          { "open_file", _("Open File..."), "Ctrl-O" },
                          { "refresh_file", _("Reload File"), "Ctrl-R" },
                          { "save_file", _("Save File"), "Ctrl-S" },
                          { "duplicate_tab", _("Duplicate Tab") },
                          { "save_file_as", _("Save As..."), "Ctrl-Shift-S" },
                          { "save_all", _("Save all files") },
                          { "close_file", _("Close"), "Ctrl-W" },
                          { "wxID_CLOSE_ALL", _("Close All") },
                          { "wxID_PRINT", _("Print..."), "Ctrl-P" },
                          { "wxID_EXIT", _("Exit"), "Alt-X" } });
    mgr->AddAccelerator(_("Help"), { { "wxID_ABOUT", _("About...") } });
    mgr->AddAccelerator(_("Plugins"), { { "manage_plugins", _("Manage Plugins...") } });
    mgr->AddAccelerator(_("Search"),
                        { { "grep_current_file", _("Grep Selection in the Current File"), "Ctrl-Shift-G" },
                          { "grep_current_workspace", _("Grep Selection in the Workspace") },
                          { "web_search_selection", _("Search for Selection with default browser"), "Ctrl-Shift-W" },
                          { "ID_GOTO_ANYTHING", _("Goto Anything"), "Ctrl-Shift-P" },
                          { "find_previous", _("Find Previous"), "Shift-F3" },
                          { "find_resource", _("Find Resource..."), "Ctrl-Shift-R" },
                          { "find_symbol", _("Quick Outline..."), "Ctrl-Shift-O" },
                          { "goto_definition", _("Find Symbol"), "Alt-G" } });
    mgr->AddAccelerator(_("Search | Bookmarks"),
                        { { "toggle_bookmark", _("Toggle Bookmark"), "Ctrl-B" },
                          { "next_bookmark", _("Next Bookmark"), "F2" },
                          { "previous_bookmark", _("Previous Bookmark"), "Shift-F2" },
                          { "removeall_bookmarks", _("Remove All Bookmarks") },
                          { "removeall_current_bookmarks", _("Remove All Currently-Active Bookmarks") },
                          { "open_shell_from_filepath", _("Open Shell From File Path"), "Ctrl-Shift-T" },
                          { "open_file_explorer", _("Open Containing Folder"), "Ctrl-Alt-Shift-T" } });
    mgr->AddAccelerator(_("Search | Find In Files"),
                        { { "find_in_files", _("Find In Files..."), "Ctrl-Shift-F" },
                          { "next_fif_match", _("Go to Next 'Find In File' Match"), "F8" },
                          { "previous_fif_match", _("Go to Previous 'Find In File' Match"), "Ctrl-F8" } });
    mgr->AddAccelerator(_("Search | Find and Replace"),
                        { { "id_find", _("Find..."), "Ctrl-F" },
                          { "ID_QUICK_ADD_NEXT", _("Quick Add Next"), "Ctrl-K" },
                          { "ID_QUICK_FIND_ALL", _("Quick Find All"), "Ctrl-Shift-K" },
                          { "id_replace", _("Replace..."), "Ctrl-H" },
                          { "find_next", _("Find Next"), "F3" },
                          { "find_next_at_caret", _("Find Word At Caret") },
                          { "find_previous_at_caret", _("Find Word At Caret Backward") } });
    mgr->AddAccelerator(_("Search | Go To"),
                        { { "id_backward", _("Go To Previous Location"), "Ctrl-," },
                          { "id_forward", _("Go To Forward Location"), "Ctrl-." },
                          { "goto_linenumber", _("Go To Line..."), "Ctrl-G" } });
    mgr->AddAccelerator(_("Settings"),
                        { { "wxID_PREFERENCES", _("Preferences"), "Alt-O" },
                          { "syntax_highlight", _("Colours and Fonts...") },
                          { "configure_accelerators", _("Keyboard shortcuts...") },
                          { "add_envvar", _("Environment Variables..."), "Ctrl-Shift-V" },
                          { "advance_settings", _("Build Settings...") },
                          { "debuger_settings", _("GDB Settings...") },
                          { "tags_options", _("Code Completion...") } });
    mgr->AddAccelerator(_("Tab"),
                        { { "wxEVT_BOOK_NAV_PREV", _("Show Recent Tabs Dialog"), "RawCtrl-TAB" },
                          { "wxEVT_BOOK_MOVE_TAB_LEFT", _("Move Tab Left"), "RawCtrl-Shift-PGUP" },
                          { "wxEVT_BOOK_MOVE_TAB_RIGHT", _("Move Tab Right"), "RawCtrl-Shift-PGDN" } });

    mgr->AddAccelerator(_("View"),
                        { { "word_wrap", _("Word Wrap") },
                          { "toggle_fold", _("Toggle Current Fold"), "Alt-RIGHT" },
                          { "fold_all", _("Toggle All Folds") },
                          { "fold_topmost_in_selection", _("Toggle All Topmost Folds in Selection") },
                          { "fold_all_in_selection", _("Toggle Every Fold in Selection") },
                          { "display_eol", _("Display EOL") },
                          { "next_tab", _("Next tab"), "Ctrl-Alt-RIGHT" },
                          { "prev_tab", _("Previous tab"), "Ctrl-Alt-LEFT" },
                          { "full_screen", _("Full Screen..."), "Alt-M" },
                          { "output_pane", _("Output Pane"), "Ctrl-`" },
                          { "show_terminal_pane", _("Show Builtin Terminal"), "F6" },
                          { "workspace_pane", _("Workspace Pane"), "Ctrl-Alt-W" },
                          { "debugger_pane", _("Debugger Pane"), "Ctrl-Alt-D" },
                          { "show_nav_toolbar", _("Navigation Bar"), "Ctrl-Alt-N" },
                          { "toggle_panes", _("Toggle All Panes"), "Ctrl-M" },
                          { "distraction_free_mode", _("Toggle Minimal View"), "Ctrl-F11" },
#ifdef __WXGTK__
                          { "show_menu_bar", _("Show Menu Bar"), "Alt-`" },
#endif
                          { "hide_status_bar", _("Show Status Bar") },
                          { "hide_tool_bar", _("Show Tool Bar"), "F1" } });
    mgr->AddAccelerator(_("View | Show Whitespace"),
                        { { "whitepsace_invisible", _("Invisible"), "Alt-F1" },
                          { "whitepsace_always", _("Show Always"), "Alt-F2" },
                          { "whitespace_visiable_after_indent", _("Visible After First Indent"), "Alt-F3" },
                          { "whitespace_indent_only", _("Indentation Only") } });
    mgr->AddAccelerator(_("View | Zoom"),
                        { { "wxID_ZOOM_IN", _("Zoom In") },
                          { "wxID_ZOOM_OUT", _("Zoom Out") },
                          { "wxID_ZOOM_FIT", _("Reset Zoom"), "Ctrl-0" } });
    mgr->AddAccelerator(_("Workspace"),
                        { { "new_workspace", _("New Workspace...") },
                          { "switch_to_workspace", _("Open Workspace...") },
                          { "close_workspace", _("Close Workspace") },
                          { "reload_workspace", _("Reload Workspace") },
                          { "local_workspace_settings", _("Workspace Settings...") },
                          { "local_workspace_prefs", _("Workspace Editor Preferences...") },
                          { "import_from_msvs", _("Import other IDEs solution/workspace files...") },
                          { "project_properties", _("Open Active Project Settings..."), "Alt-F7" },
                          { "new_project", _("Create New Project") },
                          { "add_project", _("Add an Existing Project") },
                          { "full_retag_workspace", _("Parse Workspace") } });
}

clMainFrame* clMainFrame::Get() { return m_theFrame; }

namespace
{
int GetBestXButtonSize(wxWindow* win)
{
    wxUnusedVar(win);
    static bool once = true;
    static int buttonSize = 14;
    if (once) {
        once = false;
        wxBitmap bmp(1, 1);
        wxMemoryDC dc(bmp);
        wxGCDC gcdc(dc);
        gcdc.SetFont(DrawingUtils::GetDefaultGuiFont());
        wxSize sz = gcdc.GetTextExtent("T");
        buttonSize = wxMax(sz.x, sz.y);
    }
    return buttonSize;
}
} // namespace

void clMainFrame::CreateGUIControls()
{
    SetSizer(new wxBoxSizer(wxVERTICAL));
    m_mainPanel = new wxPanel(this);
    InitializeLogo();

#if defined(__WXOSX__) && wxCHECK_VERSION(3, 1, 0)
    EnableFullScreenView();
#endif

    // Instantiate the workspace manager
    // By calling its "Get" method
    clWorkspaceManager::Get();

    // tell wxAuiManager to manage this frame
    m_mgr.SetManagedWindow(m_mainPanel);

    m_mgr.SetArtProvider(new clAuiDockArt(PluginManager::Get()));
    SetAUIManagerFlags();

    m_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_GRADIENT_TYPE, wxAUI_GRADIENT_NONE);
    // Get the best caption size
#ifndef __WXMAC__
    int captionSize = GetBestXButtonSize(this);
    captionSize += 4; // 2 pixles space for bottom and top
#else
    auto font = DrawingUtils::GetDefaultGuiFont();
    wxClientDC client_dc{ this };
    client_dc.SetFont(font);
    double height = client_dc.GetTextExtent("Tp").GetHeight();
    int captionSize = (int)(height * 1.5);
#endif

    m_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_CAPTION_SIZE, captionSize);
    m_mgr.GetArtProvider()->SetColor(wxAUI_DOCKART_SASH_COLOUR, DrawingUtils::GetPanelBgColour());

#ifdef __WXMSW__
    m_mgr.GetArtProvider()->SetColor(wxAUI_DOCKART_ACTIVE_CAPTION_COLOUR,
                                     clSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
    m_mgr.GetArtProvider()->SetColor(wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR,
                                     clSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
    m_mgr.GetArtProvider()->SetColor(wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR,
                                     clSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
#endif

    m_mgr.GetArtProvider()->SetColor(wxAUI_DOCKART_BACKGROUND_COLOUR, DrawingUtils::GetPanelBgColour());

    // initialize debugger configuration tool
    DebuggerConfigTool::Get()->Load("config/debuggers.xml", "5.4");
    clCxxWorkspaceST::Get()->SetStartupDir(ManagerST::Get()->GetStartupDirectory());

    m_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE, 0);
    m_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_PANE_BUTTON_SIZE, GetBestXButtonSize(this));
    m_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_SASH_SIZE, 6);

    // add menu bar
    m_mainMenuBar = wxXmlResource::Get()->LoadMenuBar("main_menu");

#ifdef __WXMAC__
    // remove the "Show menu bar"
    wxMenu* view = NULL;
    wxMenuItem* item = m_mainMenuBar->FindItem(XRCID("show_menu_bar"), &view);
    if (item && view) {
        view->Remove(item);
    }
    SetMenuBar(m_mainMenuBar);
#else
    bool showMenuBar = clConfig::Get().Read(kConfigShowMenuBar, true);
    if (showMenuBar) {
        SetMenuBar(m_mainMenuBar);
    } else {
#if defined(__WXGTK__)
        // we can set it
        SetMenuBar(m_mainMenuBar);
        PostSizeEvent();
#endif
        m_mainMenuBar->Hide();
    }
#endif

    // Layout the main panel ASAP
    m_mgr.Update();

    // Create the status bar
    m_statusBar = new clStatusBar(this, PluginManager::Get());
    SetStatusBar(m_statusBar);

    // Set up dynamic parts of menu.
    CreateRecentlyOpenedWorkspacesMenu();

    // Connect to Edit menu, so that its labelled-state submenu can be added on the fly when necessary
    wxMenu* editmenu = NULL;
    wxMenuItem* menuitem = GetMainMenuBar()->FindItem(wxID_UNDO, &editmenu);
    if (menuitem && editmenu) {
        editmenu->Bind(wxEVT_MENU_OPEN, wxMenuEventHandler(clMainFrame::OnEditMenuOpened), this);
    }

    m_DPmenuMgr = new DockablePaneMenuManager(&m_mgr);

    //---------------------------------------------
    // Add docking windows
    //---------------------------------------------

    // I'm not localising the captions of these Views atm. That's because wxAui uses name+caption to ID a pane
    // It also serialises the caption. That means that changing locale will break the layout stored in codelite.layout
    // If it's decided to do this in the future, change only the 'Caption("Output View")' bits below
    // However I'm creating unused strings here, so that the translations remain in the catalogue
    const wxString unusedOV(_("Output View"));
    const wxString unusedWV(_("Workspace View"));
    const wxString unusedCR(
        _("wxCrafter")); // One that would otherwise be untranslated; OT here, but it's a convenient place to put it

    // Add the workspace pane
    m_sidebar =
        new SideBar(m_mainPanel, "Workspace View", &m_mgr, wxTAB_TRAVERSAL | get_border_simple_theme_aware_bit());
    RegisterDockWindow(XRCID("workspace_pane"), "Workspace View");

    m_secondary_sidebar = new SecondarySideBar(m_mainPanel, wxTAB_TRAVERSAL | get_border_simple_theme_aware_bit());
    RegisterDockWindow(XRCID("secondary_side_bar"), "Secondary Sidebar");

    // link between the side bars
    m_sidebar->SetSecondarySideBar(m_secondary_sidebar);
    m_secondary_sidebar->SetSideBar(m_sidebar);

    // add the debugger locals tree, make it hidden by default
    m_debuggerPane =
        new DebuggerPane(m_mainPanel, "Debugger", &m_mgr, wxTAB_TRAVERSAL | get_border_simple_theme_aware_bit());
    RegisterDockWindow(XRCID("debugger_pane"), "Debugger");

    // Wrap the mainbook with a wxPanel
    // We do this so we can place the find bar under the main book
    long container_style = wxBORDER_NONE | wxTAB_TRAVERSAL;
    wxPanel* container = new wxPanel(m_mainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, container_style);

    EventNotifier::Get()->Bind(wxEVT_SYS_COLOURS_CHANGED, [container](clCommandEvent& e) {
        e.Skip();
#ifndef __WXMAC__
        container->SetBackgroundColour(clSystemSettings::GetDefaultPanelColour());
#endif
    });

    container->SetSizer(new wxBoxSizer(wxVERTICAL));
    clEditorBar* navbar = new clEditorBar(container);
    navbar->Hide();

    container->GetSizer()->Add(navbar, 0, wxEXPAND);

    // Add the debugger toolbar
    m_debuggerToolbar = new DebuggerToolBar(container);
    container->GetSizer()->Add(m_debuggerToolbar, 0, wxALIGN_LEFT);

    m_mainBook = new MainBook(container);

    m_infoBar = new clInfoBar(container);
    container->GetSizer()->Add(m_mainBook, 1, wxEXPAND);
    container->GetSizer()->Add(m_infoBar, 0, wxEXPAND);
    QuickFindBar* findbar = new QuickFindBar(this);
    m_mainBook->SetFindBar(findbar);
    m_mainBook->SetEditorBar(navbar);

    m_mgr.AddPane(container, wxAuiPaneInfo().Name("Editor").CenterPane().PaneBorder(true));
    CreateRecentlyOpenedFilesMenu();

    m_outputPane = new OutputPane(m_mainPanel, "Output View", wxTAB_TRAVERSAL | get_border_simple_theme_aware_bit());
    RegisterDockWindow(XRCID("output_pane"), "Output View");

    long show_nav = EditorConfigST::Get()->GetInteger("ShowNavBar", 0);
    m_mainBook->ShowNavBar(show_nav ? true : false);

    if (!BuildSettingsConfigST::Get()->Load("2.1")) {
        clERROR() << "Could not locate build configuration! CodeLite installation is broken this might cause unwanted "
                     "behaviour!"
                  << endl;
    }
    clConfig ccConfig("code-completion.conf");
    ccConfig.ReadItem(&m_tagsOptionsData);

    // If the cc options value has changed, construct a new instance
    // with default values and call the "Merge" method
    TagsOptionsData tmp;
    m_tagsOptionsData.Merge(tmp);
    ccConfig.WriteItem(&m_tagsOptionsData);

    // update ctags options
    TagsManagerST::Get()->SetCtagsOptions(m_tagsOptionsData);

    // We must do Layout() before loading the toolbars, otherwise they're broken in >=wxGTK-2.9
    Layout();

    // Create the toolbars
    // If we requested to create a single toolbar, create a native toolbar
    // otherwise, we create a multiple toolbars using wxAUI toolbar if possible
    OptionsConfigPtr options = EditorConfigST::Get()->GetOptions();
    if (options) {
        DoCreateToolBar(options->GetIconsSize());
        Bind(wxEVT_TOOL_DROPDOWN, &clMainFrame::OnNativeTBUnRedoDropdown, this, wxID_UNDO, wxID_REDO);

    } else {
        DoCreateToolBar(16);
    }

    // Connect the custom build target events range: !USE_AUI_TOOLBAR only
    if (m_mainToolbar) {
        m_mainToolbar->Connect(ID_MENU_CUSTOM_TARGET_FIRST,
                               ID_MENU_CUSTOM_TARGET_MAX,
                               wxEVT_COMMAND_MENU_SELECTED,
                               wxCommandEventHandler(clMainFrame::OnBuildCustomTarget),
                               NULL,
                               this);
    }

    Bind(wxEVT_COMMAND_MENU_SELECTED,
         wxCommandEventHandler(clMainFrame::OnChangeActiveBookmarkType),
         this,
         XRCID("BookmarkTypes[start]"),
         XRCID("BookmarkTypes[end]"));

    GetDebuggerPane()->GetNotebook()->SetMenu(wxXmlResource::Get()->LoadMenu("debugger_view_rmenu"));
    GetOutputPane()->GetNotebook()->SetMenu(wxXmlResource::Get()->LoadMenu("outputview_view_rmenu"));

    DoSysColoursChanged();
    SetAutoLayout(true);

    // add the managed panel to the AUI manager
    GetSizer()->Add(m_mainPanel, 1, wxEXPAND);

    ::clSetTLWindowBestSizeAndPosition(this);

    // This is needed in >=wxGTK-2.9, otherwise the auinotebook doesn't fully expand at first
    SendSizeEvent(wxSEND_EVENT_POST);

    // Ditto the workspace pane auinotebook
    GetWorkspacePane()->SendSizeEvent(wxSEND_EVENT_POST);
}

void clMainFrame::DoShowToolbars(bool show, bool update)
{
    wxUnusedVar(update);
    m_pluginsToolbar->Show(show);
    Layout();
}

void clMainFrame::OnEditMenuOpened(wxMenuEvent& event)
{
    event.Skip();
    clEditor* editor = GetMainBook()->GetActiveEditor();
    wxMenuItem* labelCurrentState = event.GetMenu()->FindChildItem(XRCID("label_current_state"));
    if (labelCurrentState) { // Here seems to be the only reliable place to do 'updateui' for this; a real UpdateUI
                             // handler is only hit when there's no editor :/
        labelCurrentState->Enable(editor != NULL);
    } else {
        // In wx3.1 Bind()ing wxEVT_MENU_OPEN for the Edit menu catches its submenu opens too, so we arrive here
        // multiple times
        return;
    }

    if (editor) {
        editor->GetCommandsProcessor().PrepareLabelledStatesMenu(event.GetMenu());

    } else {
        // There's no active editor, so remove any stale submenu; otherwise it'll display but the contents won't work
        wxMenuItem* menuitem = event.GetMenu()->FindChildItem(XRCID("goto_labelled_state"));
        if (menuitem) {
            event.GetMenu()->Delete(menuitem);
        }
    }
}

void clMainFrame::OnNativeTBUnRedoDropdown(wxCommandEvent& event)
{
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor && m_mainToolbar) {
        bool undoing = event.GetId() == wxID_UNDO;
        wxMenu* menu = new wxMenu;
        editor->GetCommandsProcessor().DoPopulateUnRedoMenu(*menu, undoing);
        if (!menu->GetMenuItemCount()) {
            delete menu;
            return;
        }

        if (undoing) {
            menu->Bind(wxEVT_COMMAND_MENU_SELECTED,
                       wxCommandEventHandler(CommandProcessorBase::OnUndoDropdownItem),
                       &editor->GetCommandsProcessor());
        } else {
            menu->Bind(wxEVT_COMMAND_MENU_SELECTED,
                       wxCommandEventHandler(CommandProcessorBase::OnRedoDropdownItem),
                       &editor->GetCommandsProcessor());
        }
        m_mainToolbar->SetDropdownMenu(event.GetId(), menu);
        event.Skip();
    }
    // Don't skip if there's no active editor/toolbar, otherwise a stale menu will show
}

namespace
{
wxBitmap load_bitmap_from_bundle(const wxString& name, int toolsize, wxWindow* win)
{
    wxFileName svg_path{ clStandardPaths::Get().GetDataDir(), wxEmptyString };
    svg_path.AppendDir("svgs");
    svg_path.AppendDir(clSystemSettings::IsDark() ? "dark-theme" : "light-theme");
    svg_path.SetFullName(name + ".svg");
    auto bundle = wxBitmapBundle::FromSVGFile(svg_path.GetFullPath(), wxSize(toolsize, toolsize));
    return bundle.GetBitmapFor(win);
}

void add_main_toolbar_item(
    wxToolBar* tb, const wxString& xrcstr, const wxString& label, const wxString& bmpname, int toolsize)
{
    // load the bitmap using the bundle
    wxBitmap bmp = load_bitmap_from_bundle(bmpname, toolsize, tb);
    wxBitmap disabled_bmp = DrawingUtils::CreateDisabledBitmap(bmp);

    int toolid = wxXmlResource::GetXRCID(xrcstr);
    tb->AddTool(toolid, label, bmp, disabled_bmp, wxITEM_NORMAL, label, label);
}

bool is_main_toolbar_hidden(int style) { return (style & (wxTB_TOP | wxTB_BOTTOM | wxTB_LEFT | wxTB_RIGHT)) == 0; }
} // namespace

#define TB_POS_ALL (wxTB_TOP | wxTB_BOTTOM | wxTB_RIGHT | wxTB_LEFT)

void clMainFrame::DoCreateToolBar(int toolSize)
{
    //----------------------------------------------
    // create the toolbars
    //----------------------------------------------
    SetToolBar(nullptr);

    // plugins toolbar
    // Create the plugins toolbar, empty by default
    m_pluginsToolbar =
        new clToolBarGeneric(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_TOP | wxTB_NODIVIDER | wxTB_FLAT);
    m_pluginsToolbar->EnableCustomisation(true);
    m_pluginsToolbar->Bind(wxEVT_TOOLBAR_CUSTOMISE, &clMainFrame::OnCustomiseToolbar, this);
    m_mainToolbarStyle = clConfig::Get().Read("MainToolBarStyle", m_mainToolbarStyle);

    if (is_main_toolbar_hidden(m_mainToolbarStyle)) {
        return;
    }

    // the main tool
    const int ID_TOOLBAR = 500;
    toolSize = 16;
#if defined(__WXMAC__)
    if (m_mainToolbarStyle & wxTB_TOP) {
        toolSize = 48;
    }
#endif

    SetToolBar(nullptr);
    m_mainToolbar = CreateToolBar(m_mainToolbarStyle, ID_TOOLBAR);

#if defined(__WXMAC__)
    m_mainToolbar->SetToolBitmapSize(wxSize(toolSize, toolSize));
#endif

    add_main_toolbar_item(m_mainToolbar, "new_file", _("New file"), "file_new", toolSize);
    add_main_toolbar_item(m_mainToolbar, "open_file", _("Open file"), "file_open", toolSize);
    add_main_toolbar_item(m_mainToolbar, "refresh_file", _("Reload File"), "file_reload", toolSize);
    add_main_toolbar_item(m_mainToolbar, "close_file", _("Close File"), "file_close", toolSize);
    m_mainToolbar->AddSeparator();

    add_main_toolbar_item(m_mainToolbar, "save_file", _("Save"), "file_save", toolSize);
    add_main_toolbar_item(m_mainToolbar, "save_all", _("Save All"), "file_save_all", toolSize);
    m_mainToolbar->AddSeparator();

    add_main_toolbar_item(m_mainToolbar, "wxID_CUT", _("Cut"), "cut", toolSize);
    add_main_toolbar_item(m_mainToolbar, "wxID_COPY", _("Copy"), "copy", toolSize);
    add_main_toolbar_item(m_mainToolbar, "wxID_PASTE", _("Paste"), "paste", toolSize);
    add_main_toolbar_item(m_mainToolbar, "wxID_UNDO", _("Undo"), "undo", toolSize);
    add_main_toolbar_item(m_mainToolbar, "wxID_REDO", _("Redo"), "redo", toolSize);
    m_mainToolbar->AddSeparator();

    add_main_toolbar_item(m_mainToolbar, "to_upper", _("Make Uppercase"), "up", toolSize);
    add_main_toolbar_item(m_mainToolbar, "to_lower", _("Make Lowercase"), "down", toolSize);
    m_mainToolbar->AddSeparator();

    add_main_toolbar_item(m_mainToolbar, "id_backward", _("Backward"), "back", toolSize);
    add_main_toolbar_item(m_mainToolbar, "id_forward", _("Forward"), "forward", toolSize);
    m_mainToolbar->AddSeparator();

    //----------------------------------------------
    // create the bookmark toolbar
    //----------------------------------------------
    add_main_toolbar_item(m_mainToolbar, "toggle_bookmark", _("Toggle Bookmark"), "bookmark", toolSize);
    // m_mainToolbar->Bind(wxEVT_TOOL, BookmarkManager::Get().CreateBookmarksSubmenu(NULL), this,
    // XRCID("toggle_bookmark"));
    add_main_toolbar_item(m_mainToolbar, "previous_bookmark", _("Previous Bookmark"), "back", toolSize);
    add_main_toolbar_item(m_mainToolbar, "next_bookmark", _("Next Bookmark"), "forward", toolSize);
    add_main_toolbar_item(m_mainToolbar, "removeall_bookmarks", _("Remove All Bookmarks"), "clear", toolSize);
    m_mainToolbar->AddSeparator();

    //----------------------------------------------
    // create the search toolbar
    //----------------------------------------------
    add_main_toolbar_item(m_mainToolbar, "id_find", _("Find"), "find", toolSize);
    add_main_toolbar_item(m_mainToolbar, "id_replace", _("Replace"), "find_and_replace", toolSize);
    add_main_toolbar_item(m_mainToolbar, "find_in_files", _("Find In Files"), "find_in_files", toolSize);
    add_main_toolbar_item(m_mainToolbar, "find_resource", _("Find Resource In Workspace"), "open_resource", toolSize);
    m_mainToolbar->AddSeparator();

    //----------------------------------------------
    // create the build toolbar
    //----------------------------------------------
    add_main_toolbar_item(m_mainToolbar, "build_active_project_menu", _("Build Active Project"), "build", toolSize);
    m_mainToolbar->Bind(wxEVT_TOOL, &clMainFrame::OnShowBuildMenu, this, XRCID("build_active_project_menu"));

    add_main_toolbar_item(m_mainToolbar, "stop_active_project_build", _("Stop Current Build"), "stop", toolSize);
    add_main_toolbar_item(m_mainToolbar, "clean_active_project", _("Clean Active Project"), "clean", toolSize);
    m_mainToolbar->AddSeparator();

    // debugger
    add_main_toolbar_item(m_mainToolbar, "execute_no_debug", _("Run Active Project"), "run", toolSize);
    add_main_toolbar_item(m_mainToolbar, "stop_executed_program", _("Stop Running Program"), "execute_stop", toolSize);
    add_main_toolbar_item(m_mainToolbar, "start_debugger", _("Start or Continue debugger"), "start-debugger", toolSize);
    m_mainToolbar->Realize();
}

bool clMainFrame::StartSetupWizard(bool firstTime)
{
    clBootstrapWizard wiz(this, firstTime);
    if (wiz.RunWizard(wiz.GetFirstPage())) {
        {
            wxString message;

            if (wiz.IsRestartRequired()) {
                message << _("Applying your choices and restarting CodeLite");
            } else {
                message << _("Applying your choices, this may take a few seconds");
            }

            wxBusyInfo bi(message);

            clBootstrapData data = wiz.GetData();

            // update the compilers if not empty
            if (!data.compilers.empty()) {
                BuildSettingsConfigST::Get()->SetCompilers(data.compilers);
                CallAfter(&clMainFrame::UpdateParserSearchPathsFromDefaultCompiler);
            }
            OptionsConfigPtr options = EditorConfigST::Get()->GetOptions();
            options->SetIndentUsesTabs(data.useTabs);
            options->SetShowWhitspaces(data.whitespaceVisibility);
            EditorConfigST::Get()->SetOptions(options);
            clConfig::Get().Write("CodeLiteAppearance", data.forceDarkAppearance ? 1 : 0 /* system */);

            // Update the theme
            ColoursAndFontsManager::Get().SetTheme(data.selectedTheme);
            ColoursAndFontsManager::Get().Save();
        }

        if (wiz.IsRestartRequired()) {
            // Force a CodeLite restart
            clCommandEvent restartEvent(wxEVT_FORCE_RESTART_CODELITE);
            ManagerST::Get()->AddPendingEvent(restartEvent);
            return true;
        }
    }
    return false;
}

void clMainFrame::Bootstrap()
{
    if (!clConfig::Get().Read(kConfigBootstrapCompleted, false)) {
        clConfig::Get().Write(kConfigBootstrapCompleted, true);
        if (StartSetupWizard(true)) {
            EventNotifier::Get()->PostCommandEvent(wxEVT_INIT_DONE, NULL);
            return;
        }
    }

    // Load the session manager
    wxString sessConfFile;
    sessConfFile << clStandardPaths::Get().GetUserDataDir() << "/config/sessions.xml";
    SessionManager::Get().Load(sessConfFile);

    // restore last session if needed
    if (clConfig::Get().Read(kConfigRestoreLastSession, true) && m_loadLastSession) {
        wxCommandEvent loadSessionEvent(wxEVT_LOAD_SESSION);
        EventNotifier::Get()->AddPendingEvent(loadSessionEvent);
    }
    EventNotifier::Get()->PostCommandEvent(wxEVT_INIT_DONE, NULL);

    // and finally, find the best window to give focus to
    codelite_initialised = true;
}

void clMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) { Close(); }

void clMainFrame::OnTBUnRedoMenu(wxCommandEvent& event)
{
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor) {
        editor->GetCommandsProcessor().ProcessEvent(event);

    } else if (GetMainBook()->GetCurrentPage()) {
        event.StopPropagation(); // Otherwise we'll infinitely loop if the active page doesn't handle this event
        GetMainBook()->GetCurrentPage()->GetEventHandler()->ProcessEvent(event); // Let the active plugin have a go
    }
}

void clMainFrame::OnTBUnRedo(wxCommandEvent& event)
{
    DispatchCommandEvent(event); // Revert to standard processing
}

//----------------------------------------------------
// Helper method for the event handling
//----------------------------------------------------

bool clMainFrame::IsEditorEvent(wxEvent& event)
{
    // If the event came from the toolbar, return true

#ifdef __WXGTK__
    MainBook* mainBook = GetMainBook();
    if (!mainBook || !mainBook->GetActiveEditor()) {
        if (event.GetId() == XRCID("id_find")) {
            return true;
        } else {
            return false;
        }
    }

    switch (event.GetId()) {
    case wxID_CUT:
    case wxID_SELECTALL:
    case wxID_COPY:
    case wxID_PASTE:
    case wxID_UNDO:
    case wxID_REDO: {
        bool isFocused;
        clEditor* editor = dynamic_cast<clEditor*>(event.GetEventObject());
        if (editor) {
            isFocused = true;

        } else {
            isFocused = mainBook->GetActiveEditor()->IsFocused();
        }
        return isFocused;
    }
    default:
        break;
    }

#else
    // Handle common edit events
    // if the focused window is *not* clEditor,
    // and the focused windows is of type
    // wxTextCtrl or wxScintilla, let the focused
    // Window handle the event
    wxWindow* focusWin = wxWindow::FindFocus();
    if (focusWin) {
        switch (event.GetId()) {
        case wxID_CUT:
        case wxID_SELECTALL:
        case wxID_COPY:
        case wxID_PASTE:
        case wxID_UNDO:
        case wxID_REDO: {
            clEditor* ed = dynamic_cast<clEditor*>(focusWin);
            if (!ed) {
                // let other controls handle it
                return false;
            }
            break;
        }
        default:
            break;
        }
    }
#endif
    return true;
}

namespace
{
wxStyledTextCtrl* CurrentSTC(clMainFrame* self)
{
    auto page = self->GetMainBook()->GetCurrentPage();
    CHECK_PTR_RET_NULL(page);

    return dynamic_cast<wxStyledTextCtrl*>(page);
}
} // namespace
void clMainFrame::DispatchCommandEvent(wxCommandEvent& event)
{
    if (!IsEditorEvent(event)) {
        event.Skip();
        return;
    }

    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (event.GetId() == XRCID("id_find")) {
        wxStyledTextCtrl* stc = dynamic_cast<wxStyledTextCtrl*>(wxWindow::FindFocus());
        if (stc) {
            GetMainBook()->GetFindBar()->SetEditor(stc);
            GetMainBook()->ShowQuickBar(::clGetVisibleSelection(stc), true);
        }
    } else if (editor) {
        editor->OnMenuCommand(event);
    } else if (event.GetId() == XRCID("goto_linenumber")) {
        auto stc = CurrentSTC(this);
        CHECK_PTR_RET(stc);

        wxString msg = wxString::Format(_("Go to line number (1 - %i):"), stc->GetLineCount());
        wxNumberEntryDialog dlg(stc, msg, wxEmptyString, _("Go To Line"), 0, 1, stc->GetLineCount());
        if (dlg.ShowModal() == wxID_OK) {
            long line = dlg.GetValue();
            stc->GotoLine(line - 1);
            clSTCHelper::CenterLine(stc, line - 1);
            stc->SetSTCFocus(true);
        }
    }
}

void clMainFrame::DispatchUpdateUIEvent(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();

    if (!IsEditorEvent(event)) {
        event.Skip();
        return;
    }

    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (!editor) {
        event.Enable(false);
        return;
    }

    if (event.GetId() >= viewAsMenuItemID && event.GetId() <= viewAsMenuItemMaxID) {
        // keep the old id as int and override the value set in the event object
        // to trick the event system
        event.SetInt(event.GetId());
        event.SetId(viewAsMenuItemID);
    }
    editor->OnUpdateUI(event);
}

void clMainFrame::OnFileExistUpdateUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    auto editor = GetEditorFromEvent(GetMainBook(), event);
    event.Enable(editor != nullptr);
}

void clMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
#ifdef __WXGTK__
    // Build the about info
    wxAboutDialogInfo info;

    // Add the developers list
    info.AddDeveloper(_("Eran Ifrah (Project admin)"));
    info.AddDeveloper(_("David G. Hart"));
    info.AddDeveloper(_("Frank Lichtner"));
    info.AddDeveloper(_("Jacek Kucharski"));
    info.AddDeveloper(_("Marrianne Gagnon"));
    info.AddDeveloper(_("Scott Dolim"));

    // Misc
    info.SetWebSite("https://codelite.org", _("CodeLite Home"));
    info.SetVersion(CODELITE_VERSION_STRING);
    info.SetCopyright("Eran Ifrah 2007-2025");

    // Load the license file
    wxFileName license(clStandardPaths::Get().GetDataDir(), "LICENSE");
    wxString fileContent;
    FileUtils::ReadFileContent(license, fileContent);
    info.SetLicence(fileContent);
    info.SetName(_("CodeLite IDE"));
    info.SetDescription(_("CodeLite, a free, open source, C/C++/Rust/Python/PHP and JavaScript IDE"));

    wxBitmap iconBmp = clGetManager()->GetStdIcons()->LoadBitmap("codelite-logo", 64);
    if (iconBmp.IsOk()) {
        wxIcon icn;
        icn.CopyFromBitmap(iconBmp);
        info.SetIcon(icn);
    }
    wxAboutBox(info, this);
#else
    clAboutDialog dialog(this, CODELITE_VERSION_STRING);
    dialog.ShowModal();
#endif
}

void clMainFrame::OnClose(wxCloseEvent& event)
{
    if (!SaveLayoutAndSession()) {
        event.Veto();
        event.Skip(false);
        return;
    }

    SaveGeneralSettings();
    event.Skip();

    wxString msg;
    ManagerST::Get()->SetShutdownInProgress(true);

    // Notify the plugins that we are going down
    clCommandEvent eventGoingDown(wxEVT_GOING_DOWN);
    EventNotifier::Get()->ProcessEvent(eventGoingDown);

    // Stop the search thread
    ManagerST::Get()->KillProgram();
    SearchThreadST::Get()->StopSearch();

    // Stop any debugging session if any
    IDebugger* debugger = DebuggerMgr::Get().GetActiveDebugger();
    if (debugger && debugger->IsRunning()) {
        ManagerST::Get()->DbgStop();
    }

    // In case we got some data in the clipboard, flush it so it will be available
    // after our process exits
    wxTheClipboard->Flush();
}

void clMainFrame::LoadSession(const wxString& sessionName)
{
    SessionEntry session;
    if (SessionManager::Get().GetSession(sessionName, session)) {
        wxString wspFile = session.GetWorkspaceName();
        if (wspFile.IsEmpty() == false && wspFile != "Default") {

            // Load the workspace only if it exists
            wxFileName fnWorkspace(wspFile);

            if (fnWorkspace.Exists()) {
                wxCommandEvent eventOpenWorkspace;
                eventOpenWorkspace.SetString(fnWorkspace.GetFullPath());
                OnSwitchWorkspace(eventOpenWorkspace);
            }

        } else {
            // no workspace to open, so just restore any previously open editors
            GetMainBook()->RestoreSession(session);
        }
    }

    if (GetMainBook()->GetActiveEditor()) {
        GetMainBook()->GetActiveEditor()->SetActive();
    }
}

void clMainFrame::OnSave(wxCommandEvent& event)
{
    clEditor* editor = GetEditorFromEvent(GetMainBook(), event);
    if (editor) {
        editor->SaveFile();

    } else {

        // delegate it to the plugins
        wxCommandEvent saveEvent(XRCID("save_file"));
        EventNotifier::Get()->ProcessEvent(saveEvent);
    }
}

void clMainFrame::OnSaveAs(wxCommandEvent& WXUNUSED(event))
{
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor) {
        editor->SaveFileAs();
    }
}

void clMainFrame::OnFileLoadTabGroup(wxCommandEvent& WXUNUSED(event))
{
    wxArrayString previousgroups;
    EditorConfigST::Get()->GetRecentItems(previousgroups, "RecentTabgroups");

    // Check the previous items still exist
    for (int n = (int)previousgroups.GetCount() - 1; n >= 0; --n) {
        if (!wxFileName::FileExists(previousgroups.Item(n))) {
            previousgroups.RemoveAt(n);
        }
    }
    EditorConfigST::Get()->SetRecentItems(previousgroups, "RecentTabgroups"); // In case any were deleted

    wxString path = ManagerST::Get()->IsWorkspaceOpen() ? clCxxWorkspaceST::Get()->GetWorkspaceFileName().GetPath()
                                                        : wxGetHomeDir();
    LoadTabGroupDlg dlg(this, path, previousgroups);

    // Disable the 'Replace' checkbox if there aren't any editors to replace
    const auto editors = GetMainBook()->GetAllEditors();
    dlg.EnableReplaceCheck(editors.size());

    if (dlg.ShowModal() != wxID_OK) {
        return;
    }

    wxString filepath = dlg.GetListBox()->GetStringSelection();
    wxString sessionFilepath = filepath.BeforeLast('.');

    clWindowUpdateLocker locker(this);
    TabGroupEntry session;
    if (SessionManager::Get().GetSession(sessionFilepath, session, "tabgroup", tabgroupTag)) {
        // We've 'loaded' the requested tabs. If required, delete any current ones
        if (dlg.GetReplaceCheck()) {
            GetMainBook()->CloseAll(true);
        }
        GetMainBook()->RestoreSession(session);

        // Remove any previous instance of this group from the history, then prepend it and save
        int index = previousgroups.Index(filepath);
        if (index != wxNOT_FOUND) {
            previousgroups.RemoveAt(index);
        }
        previousgroups.Insert(filepath, 0);
        EditorConfigST::Get()->SetRecentItems(previousgroups, "RecentTabgroups");
    }
}

void clMainFrame::OnFileReload(wxCommandEvent& event)
{
    wxUnusedVar(event);
    clEditor* editor = GetEditorFromEvent(GetMainBook(), event);
    CHECK_PTR_RET(editor);

    if (editor->GetModify()) {
        // Ask user if he really wants to lose all changes
        wxString msg;
        msg << _("File '") << editor->GetFileName().GetFullName() << _("' is modified\nContinue with reload?");
        if (::wxMessageBox(msg, _("Reload File"), wxICON_WARNING | wxYES_NO | wxCANCEL | wxCANCEL_DEFAULT) != wxYES) {
            return;
        }
    }
    editor->ReloadFromDisk(true);
}

void clMainFrame::OnCloseWorkspace(wxCommandEvent& event)
{
    wxUnusedVar(event);

    wxBusyCursor bc;
    // let the plugins close any custom workspace
    clCommandEvent e(wxEVT_CMD_CLOSE_WORKSPACE, GetId());
    e.SetEventObject(this);
    EventNotifier::Get()->ProcessEvent(e);

    // In any case, make sure that we dont have a workspace opened
    if (ManagerST::Get()->IsWorkspaceOpen()) {
        ManagerST::Get()->CloseWorkspace();
    }
}

void clMainFrame::OnSwitchWorkspace(wxCommandEvent& event)
{
    // Notify plugins
    clCommandEvent switchingToWorkspce(wxEVT_SWITCHING_TO_WORKSPACE);
    if (event.GetString().IsEmpty()) {
        if (EventNotifier::Get()->ProcessEvent(switchingToWorkspce)) {
            // plugin called event.Skip(false)
            return;
        }
    }

    // To restore the default behavior, a plugin could set the file name in the event so we can skip the
    // SwitchToWorkspaceDlg process
    if (!switchingToWorkspce.GetFileName().empty()) {
        event.SetString(switchingToWorkspce.GetFileName());
    }

    wxBusyCursor bc;
    wxString wspFile;
    const wxString WSP_EXT = "workspace";

    // if the event does not contain the workspace filename, prompt the user
    if (event.GetString().IsEmpty()) {
        SwitchToWorkspaceDlg dlg(this);
        if (dlg.ShowModal() != wxID_OK) {
            return;
        }
        wspFile = dlg.GetPath();
    } else {
        wspFile = event.GetString();
    }

    if (wspFile.IsEmpty()) {
        return;
    }

    // Check if a workspace is opened and close it if needed
    if (clWorkspaceManager::Get().IsWorkspaceOpened()) {
        // Close the workspace first
        wxCommandEvent evtClose(wxEVT_MENU, XRCID("close_workspace"));
        evtClose.SetEventObject(this);
        GetEventHandler()->ProcessEvent(evtClose);
    }

    // Let the plugins a chance of handling this workspace first
    clCommandEvent e(wxEVT_CMD_OPEN_WORKSPACE, GetId());
    e.SetEventObject(this);
    e.SetFileName(wspFile);
    if (EventNotifier::Get()->ProcessEvent(e)) {
        // a plugin processed it by itself
        return;
    }

    // Make sure that the 'Workspace' tab is visible
    {
        wxCommandEvent showTabEvent(wxEVT_COMMAND_MENU_SELECTED, XRCID("show_workspace_tab"));
        showTabEvent.SetEventObject(this);
        showTabEvent.SetInt(1);
        GetEventHandler()->ProcessEvent(showTabEvent);
    }

    // Open the workspace
    ManagerST::Get()->OpenWorkspace(wspFile);
}

void clMainFrame::OnCompleteWordRefreshList(wxCommandEvent& event) { wxUnusedVar(event); }

void clMainFrame::OnCodeComplete(wxCommandEvent& event)
{
    wxUnusedVar(event);
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor) {
        editor->CompleteWord(LSP::CompletionItem::kTriggerUser);
    }
}

void clMainFrame::OnFunctionCalltip(wxCommandEvent& event)
{
    wxUnusedVar(event);
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor) {
        editor->ShowFunctionTipFromCurrentPos();
    }
}

// Open new file
void clMainFrame::OnFileNew(wxCommandEvent& event)
{
    wxUnusedVar(event);
    GetMainBook()->NewEditor();
}

void clMainFrame::OnFileOpen(wxCommandEvent& WXUNUSED(event))
{
    const wxString ALL("All Files (*)|*");

    // Open a file using the current editor's path
    clEditor* editor = GetMainBook()->GetActiveEditor();
    wxString open_path;

    if (editor) {
        open_path = editor->GetFileName().GetPath();
    } else {
        // Could not locate the active editor, use the project
        ProjectPtr project = ManagerST::Get()->GetProject(ManagerST::Get()->GetActiveProjectName());
        if (project) {
            open_path = project->GetFileName().GetPath();
        }
    }

    wxFileDialog* dlg = new wxFileDialog(this,
                                         _("Open File"),
                                         open_path,
                                         wxEmptyString,
                                         ALL,
                                         wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE,
                                         wxDefaultPosition);
    if (dlg->ShowModal() == wxID_OK) {
        wxArrayString paths;
        dlg->GetPaths(paths);
        for (size_t i = 0; i < paths.GetCount(); i++) {
            GetMainBook()->OpenFile(paths.Item(i));
        }
    }
    dlg->Destroy();
}

void clMainFrame::OnFileClose(wxCommandEvent& event)
{
    auto winToClose = GetWindowFromEvent(GetMainBook(), event);
    if (winToClose) {
        GetMainBook()->CallAfter(&MainBook::ClosePageVoid, winToClose);
    }
}

void clMainFrame::OnFileSaveAll(wxCommandEvent& event)
{
    wxUnusedVar(event);
    GetMainBook()->SaveAll(false, true);
}

void clMainFrame::OnFileSaveTabGroup(wxCommandEvent& WXUNUSED(event))
{
    wxArrayString previousgroups;
    EditorConfigST::Get()->GetRecentItems(previousgroups, "RecentTabgroups");

    SaveTabGroupDlg dlg(this, previousgroups);

    wxArrayString filepaths;
    const auto editors = GetMainBook()->GetAllEditors();
    for (size_t i = 0; i < editors.size(); ++i) {
        filepaths.Add(editors[i]->GetFileName().GetFullPath());
    }
    dlg.SetListTabs(filepaths);

    while (true) {

        if (dlg.ShowModal() != wxID_OK) {
            return;
        }

        wxString sessionName = dlg.GetTabgroupName();
        if (sessionName.IsEmpty()) {
            if (wxMessageBox(
                    _("Please enter a name for the tab group"), _("CodeLite"), wxICON_ERROR | wxOK | wxCANCEL, this) !=
                wxOK) {
                return;
            } else {
                continue;
            }
        }

        wxString path = dlg.GetSaveInWorkspace() ? TabGroupsManager::Get()->GetTabgroupDirectory()
                                                 : clStandardPaths::Get().GetUserDataDir() + "/tabgroups";

        if (path.Right(1) != wxFileName::GetPathSeparator()) {
            path << wxFileName::GetPathSeparator();
        }
        wxString filepath(path + sessionName + ".tabgroup");
        if (wxFileName::FileExists(filepath)) {
            if (wxMessageBox(_("There is already a file with this name. Do you want to overwrite it?"),
                             _("Are you sure?"),
                             wxICON_EXCLAMATION | wxOK | wxCANCEL,
                             this) != wxOK) {
                return;
            }
        }

        wxArrayInt intArr;
        if (dlg.GetChoices(intArr)) { // Don't bother to save if no tabs were selected
            TabGroupEntry session;
            session.SetTabgroupName(path + sessionName);
            GetMainBook()->SaveSession(session, &intArr);
            SessionManager::Get().Save(session.GetTabgroupName(), session, "tabgroup", tabgroupTag);
            // Add the new tabgroup to the tabgroup manager and pane
            GetWorkspacePane()->GetTabgroupsTab()->AddNewTabgroupToTree(!dlg.GetSaveInWorkspace(), filepath);

            // Remove any previous instance of this group from the history, then prepend it and save
            int index = previousgroups.Index(filepath);
            if (index != wxNOT_FOUND) {
                previousgroups.RemoveAt(index);
            }
            previousgroups.Insert(filepath, 0);
            EditorConfigST::Get()->SetRecentItems(previousgroups, "RecentTabgroups");
            GetStatusBar()->SetMessage(_("Tab group saved"));
        }

        return;
    }
}

void clMainFrame::OnCompleteWordUpdateUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();

    clEditor* editor = GetMainBook()->GetActiveEditor();
    // This menu item is enabled only if the current editor belongs to a project
    event.Enable(editor);
}

void clMainFrame::OnWorkspaceOpen(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    event.Enable(clWorkspaceManager::Get().GetWorkspace());
#if 0
    clCommandEvent e(wxEVT_CMD_IS_WORKSPACE_OPEN, GetId());
    e.SetEventObject(this);
    e.SetAnswer(false);
    EventNotifier::Get()->ProcessEvent(e);

    event.Enable(ManagerST::Get()->IsWorkspaceOpen() || e.IsAnswer());
#endif
}

// Project->New Workspace
void clMainFrame::OnProjectNewWorkspace(wxCommandEvent& event)
{
    // let the plugins handle this event first
    wxArrayString options = clWorkspaceManager::Get().GetAllWorkspaces();
    wxString selection;
    if (options.size() == 1) {
        // only C++ is available
        selection = clCxxWorkspaceST::Get()->GetWorkspaceType();
    } else {
        clSingleChoiceDialog dlg(this, options, 0);
        dlg.SetLabel(_("Select the workspace type:"));
        if (dlg.ShowModal() == wxID_OK) {
            selection = dlg.GetSelection();
        }
    }

    if (selection.IsEmpty()) {
        return;
    }
    if (selection == clCxxWorkspaceST::Get()->GetWorkspaceType()) {
        wxUnusedVar(event);
        NewWorkspaceDlg dlg(this);
        if (dlg.ShowModal() == wxID_OK) {
            wxString fullname = dlg.GetFilePath();
            wxFileName fn(fullname);
            ManagerST::Get()->CreateWorkspace(fn.GetName(), fn.GetPath());
        }
    } else {
        // a pluing workspace, pass it to the plugins
        clCommandEvent e(wxEVT_CMD_CREATE_NEW_WORKSPACE);
        e.SetEventObject(this);
        e.SetString(selection);
        EventNotifier::Get()->AddPendingEvent(e);
    }
}

// Project->New Project
void clMainFrame::OnProjectNewProject(wxCommandEvent& event)
{
    // Let the plugin process this request first
    wxUnusedVar(event);
    ManagerST::Get()->ShowNewProjectWizard();
}

void clMainFrame::OnProjectAddProject(wxCommandEvent& event)
{
    wxUnusedVar(event);

    // Prompt user for project path
    const wxString ALL("CodeLite Projects (*.project)|*.project|"
                       "All Files (*)|*");
    wxFileDialog* dlg = new wxFileDialog(this,
                                         _("Open Project"),
                                         wxEmptyString,
                                         wxEmptyString,
                                         ALL,
                                         wxFD_OPEN | wxFD_FILE_MUST_EXIST,
                                         wxDefaultPosition);
    if (dlg->ShowModal() == wxID_OK) {
        // Open it
        ManagerST::Get()->AddProject(dlg->GetPath());
    }
    dlg->Destroy();
}

void clMainFrame::OnReconcileProject(wxCommandEvent& event)
{
    wxUnusedVar(event);
    ManagerST::Get()->ReconcileProject();
}

void clMainFrame::OnCtagsOptions(wxCommandEvent& event)
{
    wxUnusedVar(event);

    bool colVars(false);
    bool newColVars(false);

    size_t colourTypes(0);

    colVars = (m_tagsOptionsData.GetFlags() & CC_COLOUR_VARS ? true : false);
    colourTypes = m_tagsOptionsData.GetCcColourFlags();

    wxArrayString pathsBefore = m_tagsOptionsData.GetParserSearchPaths();
    CodeCompletionSettingsDialog dlg(this, m_tagsOptionsData);
    if (dlg.ShowModal() != wxID_OK) {
        return;
    }
    m_tagsOptionsData = dlg.GetData();

    // writes the content into the ctags.replacements file (used by
    // codelite_indexer)
    m_tagsOptionsData.SyncData();
    newColVars = (m_tagsOptionsData.GetFlags() & CC_COLOUR_VARS ? true : false);

    TagsManagerST::Get()->SetCtagsOptions(m_tagsOptionsData);

    clConfig ccConfig("code-completion.conf");
    ccConfig.WriteItem(&m_tagsOptionsData);

    // do we need to colourise?
    if ((newColVars != colVars) || (colourTypes != m_tagsOptionsData.GetCcColourFlags())) {
        GetMainBook()->UpdateColours();
    }

    // When new include paths were added, an incremental parse is enough
    wxCommandEvent e(wxEVT_MENU, XRCID("retag_workspace"));
    AddPendingEvent(e);
}

void clMainFrame::RegisterDockWindow(int menuItemId, const wxString& name)
{
    m_panes[menuItemId] = name;
    Connect(menuItemId, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(clMainFrame::OnViewPane), NULL, this);
    Connect(menuItemId, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(clMainFrame::OnViewPaneUI), NULL, this);
}

void clMainFrame::OnToggleMainTBars(wxCommandEvent& event)
{
    wxUnusedVar(event);
    ToggleToolBars(true);
}

void clMainFrame::OnTogglePluginTBars(wxCommandEvent& event)
{
    wxUnusedVar(event);
    ToggleToolBars(false);
}

void clMainFrame::ToggleToolBars(bool std)
{
    wxStringSet_t toolbars;
    {
        wxAuiPaneInfoArray& allPanes = m_mgr.GetAllPanes();
        for (size_t i = 0; i < allPanes.GetCount(); ++i) {
            wxAuiPaneInfo& pane = allPanes.Item(i);
            if (!pane.IsOk() || !pane.IsToolbar()) {
                continue;
            }

            if (std) {
                // collect core toolbars
                if (m_coreToolbars.count(pane.name)) {
                    toolbars.insert(pane.name);
                }
            } else {
                // collect plugins toolbars
                if (m_coreToolbars.count(pane.name) == 0) {
                    toolbars.insert(pane.name);
                }
            }
        }
    }

    if (toolbars.empty()) {
        return;
    }

    // determine that state based on the first toolbar
    bool currentStateVisible = m_mgr.GetPane((*toolbars.begin())).IsShown();

    wxStringSet_t::iterator iter = toolbars.begin();
    for (; iter != toolbars.end(); ++iter) {
        wxString name = *iter;
        wxAuiPaneInfo& pane = m_mgr.GetPane(name);
        pane.Show(!currentStateVisible);
    }
    m_mgr.Update();
}

void clMainFrame::OnViewPane(wxCommandEvent& event)
{
    auto iter = m_panes.find(event.GetId());
    if (iter != m_panes.end()) {
        // In >wxGTK-2.9.4 event.GetChecked() is invalid when coming from an accelerator; instead examine the actual
        // state
        wxAuiPaneInfo& info = m_mgr.GetPane(iter->second);
        if (info.IsOk()) {
            if (info.IsShown()) {
                ViewPane(iter->second, false);
                // set the focus to the editor
                CODELITE_SET_BEST_FOCUS();
            } else {
                ViewPane(iter->second, true);
            }
        }
    }
}

void clMainFrame::OnViewPaneUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    std::map<int, wxString>::iterator iter = m_panes.find(event.GetId());
    if (iter != m_panes.end()) {
        ViewPaneUI(iter->second, event);
    }
}

void clMainFrame::ViewPane(const wxString& paneName, bool checked)
{
    ManagerST::Get()->GetPerspectiveManager().ShowPane(paneName, checked);
}

void clMainFrame::ViewPaneUI(const wxString& paneName, wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    wxAuiPaneInfo& info = m_mgr.GetPane(paneName);
    if (info.IsOk()) {
        event.Check(info.IsShown());
    }
}

void clMainFrame::OnViewOptions(wxCommandEvent& WXUNUSED(event))
{
    PreferencesDialog dlg(this);
    dlg.ShowModal();

    if (dlg.restartRquired) {
        DoSuggestRestart();
    }
}

void clMainFrame::OnTogglePanes(wxCommandEvent& event)
{
    wxUnusedVar(event);
    ManagerST::Get()->TogglePanes();
}

void clMainFrame::OnAddEnvironmentVariable(wxCommandEvent& event)
{
    wxUnusedVar(event);
    EnvironmentVariablesDlg dlg(this);
    dlg.ShowModal();
}

void clMainFrame::OnAdvanceSettings(wxCommandEvent& event)
{
    size_t selected_page(0);
    if (event.GetInt() == 1) {
        selected_page = 1;
    }

    BuildSettingsDialog dlg(this, selected_page);
    if (dlg.ShowModal() == wxID_OK) {
        // mark the whole workspace as dirty so makefile generation will take place
        // force makefile generation upon configuration change
        if (ManagerST::Get()->IsWorkspaceOpen()) {
            wxArrayString projs;
            ManagerST::Get()->GetProjectList(projs);
            for (size_t i = 0; i < projs.GetCount(); i++) {
                ProjectPtr proj = ManagerST::Get()->GetProject(projs.Item(i));
                if (proj) {
                    proj->SetModified(true);
                }
            }
        }
    }
    SelectBestEnvSet();
}

void clMainFrame::OnBuildEnded(clBuildEvent& event)
{
    event.Skip();
    switch (m_postBuildEndAction) {
    case ePostBuildEndAction::kNone:
        break;
    case ePostBuildEndAction::kRunProject: {
        // If the build process was part of a 'Build and Run' command, check whether an erros
        // occurred during build process, if non, launch the output
        wxStandardID answer = wxID_YES;
        bool build_ended_successfully = ManagerST::Get()->IsBuildEndedSuccessfully();
        if (!build_ended_successfully) {
            // The build ended with errors, but the user requested to `Build & Run`
            // prompt the user whether we should continue
            answer = ::PromptForYesNoDialogWithCheckbox(_("Build ended with errors\nContinue with execute?"),
                                                        "BuildAndRunWithErrors",
                                                        _(" Execute "),
                                                        _(" Cancel "),
                                                        _("Remember my answer and don't annoy me again"),
                                                        wxYES_NO | wxCENTER | wxICON_QUESTION | wxNO_DEFAULT);
        }

        if (build_ended_successfully || answer == wxID_YES) {
            ExecuteNoDebug(false);
        }
    } break;
    case ePostBuildEndAction::kRebuildProject:
        // Restart the build process only when the previous 'clean' command was succeeded
        if (ManagerST::Get()->IsBuildEndedSuccessfully()) {
            BuildProject(ManagerST::Get()->GetActiveProjectName());
        }
        break;
    }
    m_postBuildEndAction = ePostBuildEndAction::kNone;

    // Process next command from the queue
    ManagerST::Get()->ProcessCommandQueue();
}

// Build operations
void clMainFrame::BuildProject(const wxString& projectName)
{
    // Let the plugins handle this first
    clBuildEvent buildEvent(wxEVT_BUILD_STARTING);
    buildEvent.SetKind("build");
    if (EventNotifier::Get()->ProcessEvent(buildEvent)) {
        return;
    }

    bool enable = !ManagerST::Get()->IsBuildInProgress() && !projectName.IsEmpty();
    if (enable) {

        // Make sure that the working folder is set to the correct path
        wxString workspacePath = clCxxWorkspaceST::Get()->GetWorkspaceFileName().GetPath();
        ::wxSetWorkingDirectory(workspacePath);
        clDEBUG() << "Setting working directory to" << workspacePath;
        GetStatusBar()->SetMessage(_("Build starting..."));

        wxString conf;
        // get the selected configuration to be built
        BuildConfigPtr bldConf = clCxxWorkspaceST::Get()->GetProjBuildConf(projectName, wxEmptyString);
        if (bldConf) {
            conf = bldConf->GetName();
        }

        QueueCommand info(projectName, conf, false, QueueCommand::kBuild);
        if (bldConf && bldConf->IsCustomBuild()) {
            info.SetKind(QueueCommand::kCustomBuild);
            info.SetCustomBuildTarget("Build");
        }
        ManagerST::Get()->PushQueueCommand(info);
        ManagerST::Get()->ProcessCommandQueue();

        GetStatusBar()->SetMessage("");
    }
}

void clMainFrame::OnBuildProject(wxCommandEvent& event)
{
    wxUnusedVar(event);
    BuildProject(ManagerST::Get()->GetActiveProjectName());
}

void clMainFrame::OnBuildCustomTarget(wxCommandEvent& event)
{
    bool enable = !ManagerST::Get()->IsBuildInProgress() && !ManagerST::Get()->GetActiveProjectName().IsEmpty();
    if (enable) {

        // get the selected configuration to be built
        BuildConfigPtr bldConf =
            clCxxWorkspaceST::Get()->GetProjBuildConf(CustomTargetsMgr::Get().GetProjectName(), wxEmptyString);
        if (bldConf) {
            CustomTargetsMgr::Pair_t target = CustomTargetsMgr::Get().GetTarget(event.GetId());
            if (target.second.IsEmpty()) {
                clDEBUG() << "Failed to find Custom Build Target for event ID:" << event.GetId();
                return;
            }
            QueueCommand info(
                CustomTargetsMgr::Get().GetProjectName(), bldConf->GetName(), false, QueueCommand::kCustomBuild);
            info.SetCustomBuildTarget(target.first);

            ManagerST::Get()->PushQueueCommand(info);
            ManagerST::Get()->ProcessCommandQueue();
        }
    }
}

void clMainFrame::OnBuildAndRunProject(wxCommandEvent& event)
{
    wxUnusedVar(event);

    // Let the plugins handle this first
    clBuildEvent buildEvent(wxEVT_BUILD_STARTING);
    buildEvent.SetKind("build");
    if (EventNotifier::Get()->ProcessEvent(buildEvent)) {
        m_postBuildEndAction = ePostBuildEndAction::kRunProject;
        return;
    }

    bool enable = !ManagerST::Get()->IsBuildInProgress() && !ManagerST::Get()->GetActiveProjectName().IsEmpty();
    if (enable) {
        m_postBuildEndAction = ePostBuildEndAction::kRunProject;

        wxString projectName = ManagerST::Get()->GetActiveProjectName();
        wxString conf;
        // get the selected configuration to be built
        BuildConfigPtr bldConf = clCxxWorkspaceST::Get()->GetProjBuildConf(projectName, wxEmptyString);
        if (bldConf) {
            conf = bldConf->GetName();
        }

        QueueCommand info(projectName, conf, false, QueueCommand::kBuild);

        if (bldConf && bldConf->IsCustomBuild()) {
            info.SetKind(QueueCommand::kCustomBuild);
            info.SetCustomBuildTarget("Build");
        }

        ManagerST::Get()->PushQueueCommand(info);
        ManagerST::Get()->ProcessCommandQueue();
    }
}

void clMainFrame::OnRebuildProject(wxCommandEvent& event)
{
    wxUnusedVar(event);
    RebuildProject(ManagerST::Get()->GetActiveProjectName());
}

void clMainFrame::OnBuildProjectUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    if (!clWorkspaceManager::Get().GetWorkspace() || !clWorkspaceManager::Get().GetWorkspace()->IsBuildSupported()) {
        event.Enable(false);
    } else {
        bool enable = !ManagerST::Get()->IsBuildInProgress();
        event.Enable(enable);
    }
}

void clMainFrame::OnStopExecutedProgramUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    clExecuteEvent e(wxEVT_CMD_IS_PROGRAM_RUNNING, GetId());
    e.SetEventObject(this);
    e.SetAnswer(false);
    EventNotifier::Get()->ProcessEvent(e);

    Manager* mgr = ManagerST::Get();
    bool enable = mgr->IsProgramRunning();
    event.Enable(enable || e.IsAnswer());
}

void clMainFrame::OnStopBuildUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    if (!clWorkspaceManager::Get().GetWorkspace() || !clWorkspaceManager::Get().GetWorkspace()->IsBuildSupported()) {
        event.Enable(false);
    } else {
        Manager* mgr = ManagerST::Get();
        bool enable = mgr->IsBuildInProgress();
        event.Enable(enable);
    }
}

void clMainFrame::OnStopBuild(wxCommandEvent& event)
{
    wxUnusedVar(event);
    Manager* mgr = ManagerST::Get();
    if (mgr->IsBuildInProgress()) {
        mgr->StopBuild();
    }
}

void clMainFrame::OnStopExecutedProgram(wxCommandEvent& event)
{
    clExecuteEvent evtExecute(wxEVT_CMD_STOP_EXECUTED_PROGRAM);
    if (EventNotifier::Get()->ProcessEvent(evtExecute)) {
        return;
    }

    wxUnusedVar(event);
    Manager* mgr = ManagerST::Get();
    if (mgr->IsProgramRunning()) {
        mgr->KillProgram();
    }
}

void clMainFrame::OnCleanProject(wxCommandEvent& event)
{
    wxUnusedVar(event);

    // Let the plugins handle this first
    clBuildEvent buildEvent(wxEVT_BUILD_STARTING);
    buildEvent.SetKind("clean");
    if (EventNotifier::Get()->ProcessEvent(buildEvent)) {
        return;
    }

    QueueCommand buildInfo(QueueCommand::kClean);
    ManagerST::Get()->PushQueueCommand(buildInfo);
    ManagerST::Get()->ProcessCommandQueue();
}

void clMainFrame::OnCleanProjectUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    if (!clWorkspaceManager::Get().GetWorkspace() || !clWorkspaceManager::Get().GetWorkspace()->IsBuildSupported()) {
        event.Enable(false);
        return;
    }
    bool enable = !ManagerST::Get()->IsBuildInProgress();
    event.Enable(enable);
}

void clMainFrame::ExecuteNoDebug(bool promptToBuild)
{
    // Sanity
    if (clCxxWorkspaceST::Get()->IsOpen() && !clCxxWorkspaceST::Get()->GetActiveProject()) {
        return;
    }

    // Let the plugin process this first
    clExecuteEvent evtExecute(wxEVT_CMD_EXECUTE_ACTIVE_PROJECT);
    if (clCxxWorkspaceST::Get()->IsOpen()) {
        // set the project name
        evtExecute.SetTargetName(clCxxWorkspaceST::Get()->GetActiveProject()->GetName());
    }
    if (EventNotifier::Get()->ProcessEvent(evtExecute)) {
        return;
    }

    // Hereon, C++ workspace only

    // Again, sanity
    if (!clCxxWorkspaceST::Get()->IsOpen()) {
        return;
    }

    // Prepare the commands to execute
    QueueCommand commandExecute(QueueCommand::kExecuteNoDebug);
    if (promptToBuild) {
        wxStandardID res = ::PromptForYesNoCancelDialogWithCheckbox(
            _("Would you like to build the active project\nbefore executing it?"),
            "PromptForBuildBeforeExecute",
            _("Build and Execute"),
            _("Execute"),
            _("Cancel"));
        if (res == wxID_CANCEL) {
            return;
        }

        // If "YES" is selected, push a build request to the queue
        if (res == wxID_YES) {
            QueueCommand buildCommand(QueueCommand::kBuild);
            ManagerST::Get()->PushQueueCommand(buildCommand);
            commandExecute.SetCheckBuildSuccess(true); // execute only if build was successfull
        }
    }

    ManagerST::Get()->PushQueueCommand(commandExecute);
    ManagerST::Get()->ProcessCommandQueue();
}

void clMainFrame::OnExecuteNoDebug(wxCommandEvent& event)
{
    wxUnusedVar(event);
    ExecuteNoDebug(true);
}

void clMainFrame::OnExecuteNoDebugUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    if (!clWorkspaceManager::Get().GetWorkspace()) {
        event.Enable(false);
        return;
    }

    clExecuteEvent e(wxEVT_CMD_IS_PROGRAM_RUNNING, GetId());
    e.SetEventObject(this);
    e.SetAnswer(false);
    EventNotifier::Get()->ProcessEvent(e);
    bool somethingIsRunning = ManagerST::Get()->IsProgramRunning() || e.IsAnswer();
    event.Enable(!somethingIsRunning);
}

void clMainFrame::OnTimer(wxTimerEvent& event)
{
    if (::clIsCygwinEnvironment()) {
        clLogMessage("Running under Cygwin environment");
    }

    if (clConfig::Get().Read(kConfigCheckForNewVersion, true)) {
        m_webUpdate = new WebUpdateJob(this, false, clConfig::Get().Read("PromptForNewReleaseOnly", false));
        m_webUpdate->Check();
    }

    // enable/disable plugins toolbar functionality
    PluginManager::Get()->EnableToolbars();

    // Do we need to update the parser paths?
    bool updateParserPaths = clConfig::Get().Read(kConfigUpdateParserPaths, true);
    if (updateParserPaths) {
        UpdateParserSearchPathsFromDefaultCompiler();
        // Now that we have updated them, mark it as done, so next
        // startups we won't do this again
        clConfig::Get().Write(kConfigUpdateParserPaths, false);
    }

    /////////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////
    // clear navigation queue
    if (GetMainBook()->GetCurrentPage() == 0) {
        NavMgr::Get()->Clear();
    }

    // ReTag workspace database if needed (this can happen due to schema version changes)
    // It is important to place the retag code here since the retag workspace should take place after
    // the parser search patha have been updated (if needed)
    if (m_workspaceRetagIsRequired) {
        m_workspaceRetagIsRequired = false;
        wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, XRCID("full_retag_workspace"));
        this->AddPendingEvent(evt);
        m_infoBar->DisplayMessage(_("A workspace reparse is needed"), wxICON_INFORMATION);
    }

    // For some reason, under Linux we need to force the menu accelerator again
    // otherwise some shortcuts are getting lose (e.g. Ctrl-/ to comment line)
    ManagerST::Get()->UpdateMenuAccelerators();

    wxModule::InitializeModules();

    // Send initialization end event
    CallAfter(&clMainFrame::Bootstrap);
    event.Skip();
}

void clMainFrame::UpdateParserSearchPathsFromDefaultCompiler()
{
    // Check that the user has some paths set in the parser
    clConfig ccConfig("code-completion.conf");
    ccConfig.ReadItem(&m_tagsOptionsData);

    // Since the version numbers aren't the same
    // we should merge the new settings with the old ones
    TagsOptionsData tmp;
    m_tagsOptionsData.Merge(tmp);

    // Try to locate the paths automatically
    CompilerPtr pCompiler = BuildSettingsConfigST::Get()->GetDefaultCompiler(wxEmptyString);
    if (!pCompiler) {
        return;
    }

    wxArrayString paths;
    paths = pCompiler->GetDefaultIncludePaths();

    wxArrayString curExcludePaths = m_tagsOptionsData.GetParserExcludePaths();
    wxArrayString curIncluePaths = m_tagsOptionsData.GetParserSearchPaths();

    wxArrayString mergedPaths = ccConfig.MergeArrays(curIncluePaths, paths);
    m_tagsOptionsData.SetParserExcludePaths(curExcludePaths);
    m_tagsOptionsData.SetParserSearchPaths(mergedPaths);
    m_tagsOptionsData.SetVersion(TagsOptionsData::CURRENT_VERSION);

    //-----------------------
    // clang
    //-----------------------

    wxArrayString clangSearchPaths = m_tagsOptionsData.GetClangSearchPathsArray();
    mergedPaths = ccConfig.MergeArrays(paths, clangSearchPaths);
    m_tagsOptionsData.SetClangSearchPathsArray(mergedPaths);
    ccConfig.WriteItem(&m_tagsOptionsData);
}

void clMainFrame::OnFileCloseAll(wxCommandEvent& event)
{
    wxUnusedVar(event);
    GetMainBook()->CloseAllVoid(true);
}

void clMainFrame::OnQuickOutline(wxCommandEvent& event)
{
    wxUnusedVar(event);
    // Sanity
    clEditor* activeEditor = GetMainBook()->GetActiveEditor();
    CHECK_PTR_RET(activeEditor);

    // let the plugins process this first
    clCodeCompletionEvent evt(wxEVT_CC_SHOW_QUICK_OUTLINE, GetId());
    evt.SetFileName(activeEditor->GetFileName().GetFullPath());

    // fire the event so plugins will be able to process it
    EventNotifier::Get()->AddPendingEvent(evt);
    activeEditor->SetActive();
}

void clMainFrame::CreateRecentlyOpenedFilesMenu()
{
    wxArrayString files;
    FileHistory& hs = GetMainBook()->GetRecentlyOpenedFilesClass();
    GetMainBook()->GetRecentlyOpenedFiles(files);

    wxMenu* menu = NULL;
    wxMenuItem* item = GetMainMenuBar()->FindItem(XRCID("recent_files"), &menu);
    if (item && menu) {
        wxMenu* submenu = item->GetSubMenu();
        if (submenu) {
            for (size_t i = files.GetCount(); i > 0; --i) {
                hs.AddFileToHistory(files.Item(i - 1));
            }
            // set this menu as the recent file menu
            hs.SetBaseId(RecentFilesSubMenuID + 1);
            hs.UseMenu(submenu);
            hs.AddFilesToMenu();
        }
    }
}

void clMainFrame::CreateRecentlyOpenedWorkspacesMenu()
{
    wxArrayString files;
    FileHistory& hs = ManagerST::Get()->GetRecentlyOpenedWorkspacesClass();
    ManagerST::Get()->GetRecentlyOpenedWorkspaces(files);

    wxMenu* menu = NULL;
    wxMenuItem* item = GetMainMenuBar()->FindItem(XRCID("recent_workspaces"), &menu);
    if (item && menu) {
        wxMenu* submenu = item->GetSubMenu();
        if (submenu) {
            for (size_t i = files.GetCount(); i > 0; --i) {
                hs.AddFileToHistory(files.Item(i - 1));
            }
            // set this menu as the recent file menu
            hs.SetBaseId(RecentWorkspaceSubMenuID + 1);
            hs.UseMenu(submenu);
            // Clear any stale items
            wxMenuItemList items = submenu->GetMenuItems();
            wxMenuItemList::reverse_iterator lriter = items.rbegin();
            for (; lriter != items.rend(); ++lriter) {
                submenu->Delete(*lriter);
            }
            // Add entries without their .workspace extension
            hs.AddFilesToMenuWithoutExt();
        }
    }
}

void clMainFrame::OnRecentFile(wxCommandEvent& event)
{
    size_t idx = event.GetId() - (RecentFilesSubMenuID + 1);
    FileHistory& fh = GetMainBook()->GetRecentlyOpenedFilesClass();

    wxArrayString files;
    fh.GetFiles(files);

    if (idx < files.GetCount()) {
        wxString projectName = ManagerST::Get()->GetProjectNameByFile(files.Item(idx));
        clMainFrame::Get()->GetMainBook()->OpenFile(files.Item(idx), projectName);
    }
}

void clMainFrame::OnRecentWorkspace(wxCommandEvent& event)
{
    size_t idx = event.GetId() - (RecentWorkspaceSubMenuID + 1);
    FileHistory& fh = ManagerST::Get()->GetRecentlyOpenedWorkspacesClass();

    wxArrayString files;
    fh.GetFiles(files);

    if (idx < files.GetCount()) {
        wxString file_name(files.Item(idx));

        wxCommandEvent open_workspace_event(wxEVT_COMMAND_MENU_SELECTED, XRCID("switch_to_workspace"));
        open_workspace_event.SetEventObject(this);
        open_workspace_event.SetString(file_name);
        GetEventHandler()->AddPendingEvent(open_workspace_event);
    }
}

void clMainFrame::OnBackwardForward(wxCommandEvent& event)
{
    if (event.GetId() == XRCID("id_forward")) {
        NavMgr::Get()->NavigateForward(PluginManager::Get());
    } else if (event.GetId() == XRCID("id_backward")) {
        NavMgr::Get()->NavigateBackward(PluginManager::Get());
    } else {
        event.Skip();
    }
}

void clMainFrame::OnBackwardForwardUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    if (event.GetId() == XRCID("id_forward")) {
        event.Enable(NavMgr::Get()->CanNext());
    } else if (event.GetId() == XRCID("id_backward")) {
        event.Enable(NavMgr::Get()->CanPrev());
    } else {
        event.Skip();
    }
}

void clMainFrame::CreateWelcomePage() { GetMainBook()->InitWelcomePage(); }

void clMainFrame::OnImportMSVS(wxCommandEvent& e)
{
    wxUnusedVar(e);
    const wxString ALL("All Solution File (*.dsw;*.sln;*.dev;*.bpr;*.cbp;*.workspace)|"
                       "*.dsw;*.sln;*.dev;*.bpr;*.cbp;*.workspace|"
                       "MS Visual Studio Solution File (*.dsw;*.sln)|*.dsw;*.sln|"
                       "Bloodshed Dev-C++ Solution File (*.dev)|*.dev|"
                       "Borland C++ Builder Solution File (*.bpr)|*.bpr|"
                       "Code::Blocks Solution File (*.cbp;*.workspace)|*.cbp;*.workspace");

    wxFileDialog dlg(this,
                     _("Open IDE Solution/Workspace File"),
                     wxEmptyString,
                     wxEmptyString,
                     ALL,
                     wxFD_OPEN | wxFD_FILE_MUST_EXIST,
                     wxDefaultPosition);
    if (dlg.ShowModal() == wxID_OK) {

        wxArrayString cmps;
        BuildSettingsConfigCookie cookie;
        CompilerPtr cmp = BuildSettingsConfigST::Get()->GetFirstCompiler(cookie);
        while (cmp) {
            cmps.Add(cmp->GetName());
            cmp = BuildSettingsConfigST::Get()->GetNextCompiler(cookie);
        }

        // Get the preferred compiler type
        wxString compilerName = wxGetSingleChoice(_("Select the compiler to use:"), _("Choose compiler"), cmps);
        ManagerST::Get()->ImportMSVSSolution(dlg.GetPath(), compilerName);
    }
}

void clMainFrame::OnDebug(wxCommandEvent& e)
{
    wxUnusedVar(e);

    bool isBuiltinDebuggerRunning =
        DebuggerMgr::Get().GetActiveDebugger() && DebuggerMgr::Get().GetActiveDebugger()->IsRunning();

    if (!clCxxWorkspaceST::Get()->IsOpen()) {
        // We hae no workspace opened and yet we got here.
        // this can mean one of two:
        // 1. A non C++ workspace is opened - so we initiate the debugger start command
        // 2. User wishes to run QuickDebug// Let the plugin know that we are about to start debugging
        clDebugEvent dbgEvent(wxEVT_DBG_UI_START);

        // if a workspace is opened (other than the CxxWorkspace) set the debugger name
        if (clWorkspaceManager::Get().IsWorkspaceOpened()) {
            dbgEvent.SetDebuggerName(clWorkspaceManager::Get().GetWorkspace()->GetDebuggerName());
        }
        if (EventNotifier::Get()->ProcessEvent(dbgEvent)) {

            // set the debugger features
            m_frameHelper->SetDebuggerFeatures(dbgEvent.GetFeatures());

            // the event was processed by one of the plugins, there is nothing left to
            // be done here
            return;
        }

        // Note:
        // The second scenario (launch QuickDebug) is handled later on this function
    }

    // Enable all features
    m_frameHelper->SetDebuggerFeatures(clDebugEvent::kAllFeatures);

    if (!isBuiltinDebuggerRunning) {
        // Let the plugin know that we are about to start debugging
        clDebugEvent dbgEvent(wxEVT_DBG_UI_CONTINUE);
        ProjectPtr activeProject = clCxxWorkspaceST::Get()->GetActiveProject();
        if (activeProject) {
            BuildConfigPtr buildConfig = activeProject->GetBuildConfiguration();
            if (buildConfig) {
                dbgEvent.SetDebuggerName(buildConfig->GetDebuggerType());
            }
        }
        if (EventNotifier::Get()->ProcessEvent(dbgEvent)) {
            return;
        }
    }

    Manager* mgr = ManagerST::Get();
    if (isBuiltinDebuggerRunning) {
        // Debugger is already running -> continue command
        mgr->DbgContinue();

    } else if (mgr->IsWorkspaceOpen()) {

        if (clCxxWorkspaceST::Get()->GetActiveProjectName().IsEmpty()) {
            clLogMessage(_("Attempting to debug workspace with no active project? Ignoring."));
            return;
        }

        // Debugger is not running, but workspace is opened -> start debug session
        QueueCommand dbgCmd(QueueCommand::kDebug);

        wxStandardID res =
            ::PromptForYesNoDialogWithCheckbox(_("Would you like to build the project before debugging it?"),
                                               "BuildBeforeDebug",
                                               _("Build and Debug"),
                                               _("Debug"));
        // Don't do anything if "X" is pressed
        if (res != wxID_CANCEL) {
            if (res == wxID_YES) {
                QueueCommand bldCmd(QueueCommand::kBuild);
                ManagerST::Get()->PushQueueCommand(bldCmd);
                dbgCmd.SetCheckBuildSuccess(true);
            }

            // place a debug command
            ManagerST::Get()->PushQueueCommand(dbgCmd);

            // trigger the commands queue
            ManagerST::Get()->ProcessCommandQueue();
        }
    } else if (!mgr->IsWorkspaceOpen()) {
        // Run the 'Quick Debug'
        OnQuickDebug(e);
    }
}

void clMainFrame::OnDebugUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    e.Enable(!ManagerST::Get()->IsBuildInProgress());
}

void clMainFrame::OnDebugRestart(wxCommandEvent& e)
{
    clDebugEvent event(wxEVT_DBG_UI_RESTART);
    if (EventNotifier::Get()->ProcessEvent(event)) {
        return;
    }

    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    if (dbgr && dbgr->IsRunning() && ManagerST::Get()->DbgCanInteract()) {
        GetDebuggerPane()->GetLocalsTable()->Clear();
        dbgr->Restart();
    }
}

void clMainFrame::OnDebugRestartUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();

    clDebugEvent event(wxEVT_DBG_IS_RUNNING);
    EventNotifier::Get()->ProcessEvent(event);

    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    e.Enable(event.IsAnswer() || (dbgr && dbgr->IsRunning() && ManagerST::Get()->DbgCanInteract()));
}

void clMainFrame::OnDebugStop(wxCommandEvent& e)
{
    wxUnusedVar(e);
    clDebugEvent debugEvent(wxEVT_DBG_UI_STOP);
    if (EventNotifier::Get()->ProcessEvent(debugEvent)) {
        return;
    }
    ManagerST::Get()->DbgStop();
}

void clMainFrame::OnDebugStopUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();

    clDebugEvent eventIsRunning(wxEVT_DBG_IS_RUNNING);
    EventNotifier::Get()->ProcessEvent(eventIsRunning);

    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    e.Enable(eventIsRunning.IsAnswer() || (dbgr && dbgr->IsRunning()));
}

void clMainFrame::OnDebugManageBreakpointsUI(wxUpdateUIEvent& e)
{
    if (e.GetId() == XRCID("delete_all_breakpoints")) {
        const std::vector<clDebuggerBreakpoint> bps = ManagerST::Get()->GetBreakpointsMgr()->GetBreakpoints();
        e.Enable(bps.size());
    } else if (e.GetId() == XRCID("disable_all_breakpoints")) {
        e.Enable(ManagerST::Get()->GetBreakpointsMgr()->AreThereEnabledBreakpoints());
    } else if (e.GetId() == XRCID("enable_all_breakpoints")) {
        e.Enable(ManagerST::Get()->GetBreakpointsMgr()->AreThereDisabledBreakpoints());
    } else {

        e.Enable(true);
    }
}

void clMainFrame::OnDebugCmd(wxCommandEvent& e)
{
    int cmd(wxNOT_FOUND);
    int eventId(wxNOT_FOUND);

    if (e.GetId() == XRCID("pause_debugger")) {
        cmd = DBG_PAUSE;
        eventId = wxEVT_DBG_UI_INTERRUPT;

    } else if (e.GetId() == XRCID("dbg_stepin")) {
        cmd = DBG_STEPIN;
        eventId = wxEVT_DBG_UI_STEP_IN;

    } else if (e.GetId() == XRCID("dbg_stepi")) {
        cmd = DBG_STEPI;
        eventId = wxEVT_DBG_UI_STEP_I;

    } else if (e.GetId() == XRCID("dbg_stepout")) {
        cmd = DBG_STEPOUT;
        eventId = wxEVT_DBG_UI_STEP_OUT;

    } else if (e.GetId() == XRCID("dbg_next")) {
        cmd = DBG_NEXT;
        eventId = wxEVT_DBG_UI_NEXT;

    } else if (e.GetId() == XRCID("show_cursor")) {
        cmd = DBG_SHOW_CURSOR;
        eventId = wxEVT_DBG_UI_SHOW_CURSOR;

    } else if (e.GetId() == XRCID("dbg_nexti")) {
        cmd = DBG_NEXTI;
        eventId = wxEVT_DBG_UI_NEXT_INST;
    }

    // Allow the plugins to handle this command first
    clDebugEvent evnt(eventId);
    if (EventNotifier::Get()->ProcessEvent(evnt)) {
        return;
    }

    if (cmd != wxNOT_FOUND) {
        ManagerST::Get()->DbgDoSimpleCommand(cmd);
    }
}

void clMainFrame::OnDebugCmdUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();

    clDebugEvent eventIsRunning(wxEVT_DBG_IS_RUNNING);
    EventNotifier::Get()->ProcessEvent(eventIsRunning);

    if (e.GetId() == XRCID("pause_debugger") || e.GetId() == XRCID("dbg_stepin") || e.GetId() == XRCID("dbg_stepi") ||
        e.GetId() == XRCID("dbg_stepout") || e.GetId() == XRCID("dbg_next") || e.GetId() == XRCID("dbg_nexti") ||
        e.GetId() == XRCID("show_cursor")) {
        IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
        e.Enable(eventIsRunning.IsAnswer() || (dbgr && dbgr->IsRunning()));
    }
}

void clMainFrame::OnDebuggerSettings(wxCommandEvent& e)
{
    wxUnusedVar(e);
    DebuggerSettingsDlg* dlg = new DebuggerSettingsDlg(this);
    dlg->ShowModal();
    dlg->Destroy();
}

void clMainFrame::OnIdle(wxIdleEvent& e)
{
    e.Skip();
    if (codelite_initialised) {
        static bool once = true;
        if (once) {
            once = false;
            CODELITE_SET_BEST_FOCUS();
        }
    }
}

void clMainFrame::OnLinkClicked(wxHtmlLinkEvent& e)
{
    wxString action = e.GetLinkInfo().GetHref();
    if (!action.StartsWith("action:", &action)) {
        wxLaunchDefaultBrowser(e.GetLinkInfo().GetHref());
        return;
    }
    wxString command = action.BeforeFirst(':');
    wxString filename = action.AfterFirst(':');
    if (command != "switch-workspace" && command != "open-file" && command != "create-workspace" &&
        command != "import-msvs-solution" && command != "open-workspace") {
        e.Skip();
        return;
    }

    wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, XRCID("link_action"));
    event.SetEventObject(this);
    StartPageData* data = new StartPageData;
    data->action = command;
    data->file_path = filename;
    if (wxFileName(filename).GetExt() == "workspace") {
        data->action = "switch-workspace";
    }
    event.SetClientData(data);
    wxPostEvent(this, event);
}

void clMainFrame::OnStartPageEvent(wxCommandEvent& e)
{
    StartPageData* data = (StartPageData*)e.GetClientData();
    if (data->action == "switch-workspace") {
        clWindowUpdateLocker locker(this);
        ManagerST::Get()->OpenWorkspace(data->file_path);

    } else if (data->action == "open-file") {
        clMainFrame::Get()->GetMainBook()->OpenFile(data->file_path, wxEmptyString);

    } else if (data->action == "create-workspace") {
        OnProjectNewWorkspace(e);

    } else if (data->action == "import-msvs-solution") {
        OnImportMSVS(e);

    } else if (data->action == "open-workspace") {
        OnSwitchWorkspace(e);
    }
    wxDELETE(data);
}

void clMainFrame::SetFrameFlag(bool set, int flag)
{
    if (set) {
        m_frameGeneralInfo.SetFlags(m_frameGeneralInfo.GetFlags() | flag);
    } else {
        m_frameGeneralInfo.SetFlags(m_frameGeneralInfo.GetFlags() & ~(flag));
    }
}

void clMainFrame::CompleteInitialization()
{
    // create indexer to be used by TagsManager
    TagsManagerST::Get()->SetIndexerPath(clStandardPaths::Get().GetBinaryFullPath("codelite_indexer"));

    // cache the locales
    clLocaleManager::get().load();

    // Register the file system workspace type
    clWorkspaceManager::Get().RegisterWorkspace(new clFileSystemWorkspace(true));

    // Create a new file system workspace instance
    clFileSystemWorkspace::Get().Initialise();

    // Populate the list of core toolbars before we start loading
    // the plugins
    wxAuiPaneInfoArray& panes = m_mgr.GetAllPanes();
    for (size_t i = 0; i < panes.GetCount(); ++i) {
        if (panes.Item(i).IsToolbar()) {
            m_coreToolbars.insert(panes.Item(i).name);
        }
    }

    // Load the plugins
    PluginManager::Get()->Load();
    m_pluginsToolbar->Realize();

// Load debuggers (*must* be after the plugins)
#ifdef USE_POSIX_LAYOUT
    wxString plugdir(clStandardPaths::Get().GetPluginsDirectory());
    DebuggerMgr::Get().Initialize(this, EnvironmentConfig::Instance(), plugdir);
#else
    DebuggerMgr::Get().Initialize(this, EnvironmentConfig::Instance(), ManagerST::Get()->GetInstallDir());
#endif
    DebuggerMgr::Get().LoadDebuggers(ManagerST::Get());

    // Connect some system events
    m_mgr.Connect(wxEVT_AUI_PANE_CLOSE, wxAuiManagerEventHandler(clMainFrame::OnDockablePaneClosed), NULL, this);

    // Use the main frame size to determine the best size height & width
    wxRect frameSize = GetClientRect();
    int bestHeight = frameSize.GetHeight() / 4;
    int bestWidth = frameSize.GetWidth() / 5;

    m_mgr.AddPane(m_sidebar,
                  wxAuiPaneInfo()
                      .CaptionVisible(false)
                      .Name(m_sidebar->GetCaption())
                      .Caption(m_sidebar->GetCaption())
                      .Left()
                      .Layer(1)
                      .Position(0)
                      .MinSize(FromDIP(200), -1)
                      .CloseButton(false)
                      .Show(true));

    m_mgr.AddPane(m_secondary_sidebar,
                  wxAuiPaneInfo()
                      .CaptionVisible(false)
                      .Name(m_secondary_sidebar->GetCaption())
                      .Caption(m_secondary_sidebar->GetCaption())
                      .Right()
                      .Layer(1)
                      .MinSize(FromDIP(200), -1)
                      .Position(0)
                      .CloseButton(false)
                      .Show(true));

    m_mgr.AddPane(m_debuggerPane,
                  wxAuiPaneInfo()
                      .CaptionVisible(false)
                      .Name(m_debuggerPane->GetCaption())
                      .Caption(m_debuggerPane->GetCaption())
                      .Bottom()
                      .Layer(1)
                      .Position(1)
                      .CloseButton(false)
                      .Hide());

    m_mgr.AddPane(m_outputPane,
                  wxAuiPaneInfo()
                      .CaptionVisible(false)
                      .Name("Output View")
                      .Caption("Output View")
                      .Bottom()
                      .Layer(1)
                      .Position(0)
                      .Show(false));
    UpdateAUI();
    m_defaultLayout = m_mgr.SavePerspective();
    Layout();
    SelectBestEnvSet();

    // Now everything is loaded, set the saved tab-order in the workspace and the output pane
    GetWorkspacePane()->ApplySavedTabOrder(false);
    GetOutputPane()->ApplySavedTabOrder(false);

    ManagerST::Get()->GetPerspectiveManager().ConnectEvents(&m_mgr);
    ManagerST::Get()->GetPerspectiveManager().LoadPerspective(NORMAL_LAYOUT);
    wxCommandEvent evt(wxEVT_CL_THEME_CHANGED);
    EventNotifier::Get()->AddPendingEvent(evt);

#ifndef __WXMSW__
    sigset_t child_set;
    sigemptyset(&child_set);
    sigaddset(&child_set, SIGCHLD);

    // make sure SIGCHILD is not blocked
    sigprocmask(SIG_UNBLOCK, &child_set, NULL);

    // Start the Zombie Reaper thread
    m_zombieReaper.Start();
#endif

    // Hide / Show status/tool bar (native)
    DoShowToolbars(clConfig::Get().Read(kConfigShowToolBar, false));

    if (!clConfig::Get().Read(kConfigShowStatusBar, true)) {
        GetStatusBar()->Show(false);
    }

    // Hide / Show tab bar
    wxCommandEvent eventShowTabBar;
    eventShowTabBar.SetInt(clConfig::Get().Read(kConfigShowTabBar, true));
    OnShowTabBar(eventShowTabBar);
    ShowOrHideCaptions();

    TabGroupsManager::Get(); // Ensure that the events are binded

    ManagerST::Get()->GetPerspectiveManager().LoadPerspective(NORMAL_LAYOUT);
    m_initCompleted = true;

    if (GetTheApp()->IsStartedInDebuggerMode()) {
        wxCommandEvent quickDebugEvent(wxEVT_MENU, XRCID("quick_debug"));
        quickDebugEvent.SetEventObject(this);
        GetEventHandler()->AddPendingEvent(quickDebugEvent);
    }

    clGotoAnythingManager::Get().Initialise();

    // Update the toolbar view
    wxArrayString hiddenItems = clConfig::Get().Read("ToolBarHiddenItems", StdToWX::ToArrayString({ "No Entries" }));
    if (hiddenItems.GetCount() == 1 && hiddenItems.Item(0) == "No Entries") {
        // By default, hide these entries
        std::vector<wxString> v = { "New",
                                    "Open",
                                    "Reload",
                                    "Save",
                                    "Close",
                                    "Cut",
                                    "Copy",
                                    "Paste",
                                    "Undo",
                                    "Redo",
                                    "Toggle Bookmark",
                                    "Find",
                                    "Replace",
                                    "Find In Files",
                                    "Find Resource In Workspace",
                                    "Format Source",
                                    "Format Options",
                                    "Find this Csymbol",
                                    "Find functions calling this function",
                                    "Find functions called by this function",
                                    "Configureexternal tools...",
                                    "Show Running Tools...",
                                    "Create new qmake based project",
                                    "Check spelling...",
                                    "Checkcontinuous",
                                    "Run Unit tests..." };
        hiddenItems.Clear();
        hiddenItems.reserve(v.size());
        for (const auto& s : v) {
            hiddenItems.Add(s);
        }
    }

    auto& buttons = m_pluginsToolbar->GetButtons();
    for (size_t i = 0; i < hiddenItems.size(); ++i) {
        const wxString& label = hiddenItems.Item(i);
        auto iter = std::find_if(
            buttons.begin(), buttons.end(), [&](clToolBarButtonBase* button) { return button->GetLabel() == label; });
        if (iter != buttons.end()) {
            (*iter)->Show(false);
        }
    }

    MSWSetWindowDarkTheme(this);

    // time to create the file explorer
    wxCommandEvent e(wxEVT_COMMAND_MENU_SELECTED, XRCID("go_home"));
    GetFileExplorer()->GetEventHandler()->ProcessEvent(e);

    // SendSizeEvent();
    StartTimer();

    // Save the current layout as the "Default" layout (unless we already got one ...)
    ManagerST::Get()->GetPerspectiveManager().SavePerspectiveIfNotExists(NORMAL_LAYOUT);

    // Process the remainder of the command line arguments
    static_cast<CodeLiteApp*>(wxTheApp)->ProcessCommandLineParams();
    m_mgr.Update();

#ifdef __WXMSW__
    // needs to be done in an "CallAfter" to avoid the flicker
    CallAfter(&clMainFrame::RestoreFrameSizeAndPosition);
#else
    RestoreFrameSizeAndPosition();
#endif

#if defined(__WXGTK__)
    // Needed on GTK
    if (GetMainBook()->GetActiveEditor() == nullptr) {
        GetWorkspacePane()->GetWorkspaceTab()->SetFocus();
    }
#endif
}

void clMainFrame::RestoreFrameSizeAndPosition()
{
    SetPosition(m_frameGeneralInfo.GetFramePosition());
    SetSize(m_frameGeneralInfo.GetFrameSize());
    Maximize(m_frameGeneralInfo.GetFlags() & CL_MAXIMIZE_FRAME);
    if (m_frameGeneralInfo.GetFlags() & CL_FULLSCREEN) {
        ShowFullScreen(
            true, wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION);
    }
}

void clMainFrame::OnAppActivated(wxActivateEvent& e)
{
    e.Skip();
    codelite_active = e.GetActive();

    // check for external theme detection
    wxColour currentBgColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
    if (currentBgColour != startupBackgroundColour) {
        /// external theme change detected, fire an event
        clCommandEvent themeChangedEvent(wxEVT_CMD_COLOURS_FONTS_UPDATED);
        EventNotifier::Get()->AddPendingEvent(themeChangedEvent);
        /// and update the new background colour
        startupBackgroundColour = currentBgColour;
    }

    if (m_theFrame && e.GetActive()) {

        // if workspace or project was modified, don't prompt for
        // file(s) reload
        if (!m_theFrame->ReloadExternallyModifiedProjectFiles()) {
            m_theFrame->GetMainBook()->CallAfter(&MainBook::ReloadExternallyModified, true);
        }

        // Notify plugins that we got the focus.
        // Some plugins want to hide some frames etc
        wxCommandEvent evtGotFocus(wxEVT_CODELITE_MAINFRAME_GOT_FOCUS);
        EventNotifier::Get()->AddPendingEvent(evtGotFocus);

#ifdef __WXOSX__
        // Set the focus back to the active editor
        clEditor* activeEditor = dynamic_cast<clEditor*>(GetMainBook()->GetActiveEditor());
        if (activeEditor) {
            activeEditor->CallAfter(&clEditor::SetActive);
        }
#endif

    } else if (m_theFrame) {

#ifndef __WXMAC__
        /// this code causes crash on Mac, since it destorys an active CCBox
        clEditor* editor = GetMainBook()->GetActiveEditor();
        if (editor) {
            // we are loosing the focus
            editor->DoCancelCalltip();
        }
#endif
    }

    e.Skip();
}

void clMainFrame::OnCompileFileProject(wxCommandEvent& e)
{
    wxUnusedVar(e);
    CHECK_COND_RET(clCxxWorkspaceST::Get()->IsOpen());
    CHECK_COND_RET(!ManagerST::Get()->IsBuildInProgress());

    clEditor* editor = GetMainBook()->GetActiveEditor();
    CHECK_PTR_RET(editor);

    wxString projname = clCxxWorkspaceST::Get()->GetProjectFromFile(editor->GetFileName());
    CHECK_COND_RET(!projname.IsEmpty());

    ProjectPtr p = clCxxWorkspaceST::Get()->GetProject(projname);
    CHECK_PTR_RET(p);

    // Trigger the build
    wxCommandEvent eventBuild(wxEVT_CMD_BUILD_PROJECT_ONLY);
    eventBuild.SetString(p->GetName());
    EventNotifier::Get()->QueueEvent(eventBuild.Clone());
}

void clMainFrame::OnCompileFile(wxCommandEvent& e)
{
    wxUnusedVar(e);
    Manager* mgr = ManagerST::Get();
    if (mgr->IsWorkspaceOpen() && !mgr->IsBuildInProgress()) {
        clEditor* editor = GetMainBook()->GetActiveEditor();
        if (editor && !editor->GetProject().IsEmpty()) {
            mgr->CompileFile(editor->GetProject(), editor->GetFileName().GetFullPath());
        }
    }
}

void clMainFrame::OnCompileFileUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    e.Enable(false);
    if (!clWorkspaceManager::Get().GetWorkspace() || !clWorkspaceManager::Get().GetWorkspace()->IsBuildSupported()) {
        return;
    }
    Manager* mgr = ManagerST::Get();
    if (mgr->IsWorkspaceOpen() && !mgr->IsBuildInProgress()) {
        clEditor* editor = GetMainBook()->GetActiveEditor();
        if (editor && !editor->GetProject().IsEmpty()) {
            e.Enable(true);
        }
    }
}

void clMainFrame::OnDebugAttach(wxCommandEvent& event)
{
    wxUnusedVar(event);
    // Start the debugger
    Manager* mgr = ManagerST::Get();
    mgr->DbgStart(1);
}

void clMainFrame::OnCloseAllButThis(wxCommandEvent& e)
{
    wxUnusedVar(e);
    auto win = GetWindowFromEvent(GetMainBook(), e);
    if (win != nullptr) {
        GetMainBook()->CallAfter(&MainBook::CloseAllButThisVoid, win);
    }
}

WorkspaceTab* clMainFrame::GetWorkspaceTab() { return GetWorkspacePane()->GetWorkspaceTab(); }

FileExplorer* clMainFrame::GetFileExplorer() { return GetWorkspacePane()->GetFileExplorer(); }

void clMainFrame::OnFileCloseUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    auto win = GetWindowFromEvent(GetMainBook(), event);
    event.Enable(win != NULL);
}

void clMainFrame::OnConvertEol(wxCommandEvent& e)
{
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor) {
        int eol(wxSTC_EOL_LF);
        if (e.GetId() == XRCID("convert_eol_win")) {
            eol = wxSTC_EOL_CRLF;
        } else if (e.GetId() == XRCID("convert_eol_mac")) {
            eol = wxSTC_EOL_CR;
        }
        editor->ConvertEOLs(eol);
        editor->SetEOLMode(eol);
    }
}

void clMainFrame::OnViewDisplayEOL(wxCommandEvent& e)
{
    bool visible;
    size_t frame_flags = m_frameGeneralInfo.GetFlags();
    if (e.IsChecked()) {
        frame_flags |= CL_SHOW_EOL;
        visible = true;
    } else {
        frame_flags &= ~CL_SHOW_EOL;
        visible = false;
    }

    m_frameGeneralInfo.SetFlags(frame_flags);
    GetMainBook()->SetViewEOL(visible);
}

void clMainFrame::OnViewDisplayEOL_UI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    clEditor* editor = GetMainBook()->GetActiveEditor();
    bool hasEditor = editor ? true : false;
    if (!hasEditor) {
        e.Enable(false);
        return;
    }

    e.Enable(true);
    e.Check(m_frameGeneralInfo.GetFlags() & CL_SHOW_EOL ? true : false);
}

void clMainFrame::OnCopyFileName(wxCommandEvent& event)
{
    clEditor* editor = GetEditorFromEvent(GetMainBook(), event);
    CHECK_PTR_RET(editor);

    wxString fileName = editor->GetFileName().GetFullName();
    ::CopyToClipboard(fileName);
}

void clMainFrame::OnCopyFilePath(wxCommandEvent& event)
{
    clEditor* editor = GetEditorFromEvent(GetMainBook(), event);
    CHECK_PTR_RET(editor);

    wxString fileName = editor->GetFileName().GetFullPath();
    ::CopyToClipboard(fileName);
}

void clMainFrame::OnCopyFilePathOnly(wxCommandEvent& event)
{
    clEditor* editor = GetEditorFromEvent(GetMainBook(), event);
    CHECK_PTR_RET(editor);
    wxString fileName = editor->GetFileName().GetPath(wxPATH_GET_VOLUME);
    ::CopyToClipboard(fileName);
}

void clMainFrame::OnWorkspaceMenuUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    if (ManagerST::Get()->IsWorkspaceOpen() == false) {
        e.Enable(false);
        return;
    }
    if (ManagerST::Get()->IsBuildInProgress()) {
        e.Enable(false);
        return;
    }
    e.Enable(true);
}

void clMainFrame::OnManagePlugins(wxCommandEvent& e)
{
    PluginMgrDlg dlg(this);
    if (dlg.ShowModal() == wxID_OK) {
        DoSuggestRestart();
    }
}

void clMainFrame::OnCppContextMenu(wxCommandEvent& e)
{
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor) {
        editor->GetContext()->ProcessEvent(e);
    }
}

void clMainFrame::OnConfigureAccelerators(wxCommandEvent& e)
{
    AccelTableDlg dlg(this);
    dlg.ShowModal();
}

void clMainFrame::OnHighlightWordUI(wxUpdateUIEvent& event) { event.Check(m_highlightWord); }

void clMainFrame::OnHighlightWord(wxCommandEvent& event)
{
    m_highlightWord = event.IsChecked();

    // Notify all open editors that word highlight is checked
    wxCommandEvent evtEnable(wxCMD_EVENT_ENABLE_WORD_HIGHLIGHT);
    if (m_highlightWord) {
        GetMainBook()->HighlightWord(true);
        EditorConfigST::Get()->SetInteger("highlight_word", 1);
        evtEnable.SetInt(1);

    } else {
        GetMainBook()->HighlightWord(false);
        EditorConfigST::Get()->SetInteger("highlight_word", 0);
        evtEnable.SetInt(0);
    }

    EventNotifier::Get()->AddPendingEvent(evtEnable);
}

void clMainFrame::OnShowNavBar(wxCommandEvent& e)
{
    GetMainBook()->ShowNavBar(!GetMainBook()->IsNavBarShown());
    m_mgr.Update();
}

void clMainFrame::OnShowNavBarUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    e.Check(GetMainBook()->IsNavBarShown());
}

void clMainFrame::OnSingleInstanceOpenFiles(clCommandEvent& e)
{
    const wxArrayString& files = e.GetStrings();
    for (size_t i = 0; i < files.GetCount(); ++i) {
        wxFileName fn(files.Item(i));

        // if file is workspace, load it
        if (fn.GetExt() == "workspace") {
            wxCommandEvent workspaceEvent;
            workspaceEvent.SetString(files.Item(i));
            OnSwitchWorkspace(workspaceEvent);

        } else {
            long lineNumber = e.GetLineNumber();
            if (lineNumber > 0) {
                lineNumber--;
            } else {
                lineNumber = 0;
            }
            GetMainBook()->OpenFile(files.Item(i), wxEmptyString, lineNumber);
        }
    }

    CallAfter(&clMainFrame::Raise);
}

void clMainFrame::OnSingleInstanceRaise(clCommandEvent& e)
{
    wxUnusedVar(e);
    Raise();
}

void clMainFrame::OnNewVersionAvailable(wxCommandEvent& e)
{
    clDEBUG() << "clMainFrame::OnNewVersionAvailable called:"
              << (e.GetEventType() == wxEVT_CMD_VERSION_UPTODATE ? "up-to-date" : "new version found") << clEndl;
    if ((e.GetEventType() == wxEVT_CMD_VERSION_UPTODATE) && m_webUpdate->IsUserRequest()) {
        // All is up to date
        wxMessageBox(_("You already have the latest version of CodeLite"), "CodeLite", wxOK | wxCENTRE, this);
    } else {
        WebUpdateJobData* data = reinterpret_cast<WebUpdateJobData*>(e.GetClientData());
        if (data) {
            if (data->IsUpToDate() == false) {
                wxRichMessageDialog dlg(this,
                                        _("A new version of CodeLite is available for download"),
                                        "CodeLite",
                                        wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE | wxICON_INFORMATION);
                dlg.SetYesNoLabels(_("Download"), _("No"));
                if (dlg.ShowModal() == wxID_YES) {
                    ::wxLaunchDefaultBrowser(data->GetUrl());
                }
            }
            wxDELETE(data);
        }
    }
    wxDELETE(m_webUpdate);
}

void clMainFrame::OnDetachWorkspaceViewTab(wxCommandEvent& e) { wxUnusedVar(e); }

void clMainFrame::OnHideOutputViewTab(wxCommandEvent& e)
{
    size_t sel = GetOutputPane()->GetNotebook()->GetSelection();
    wxString text = GetOutputPane()->GetNotebook()->GetPageText(sel);

    clCommandEvent eventHide(wxEVT_SHOW_OUTPUT_TAB);
    eventHide.SetSelected(false).SetString(text);
    EventNotifier::Get()->AddPendingEvent(eventHide);
}

void clMainFrame::OnHideWorkspaceViewTab(wxCommandEvent& e)
{
    size_t sel = GetWorkspacePane()->GetNotebook()->GetSelection();
    wxString text = GetWorkspacePane()->GetNotebook()->GetPageText(sel);

    clCommandEvent eventHide(wxEVT_SHOW_WORKSPACE_TAB);
    eventHide.SetSelected(false).SetString(text);
    EventNotifier::Get()->AddPendingEvent(eventHide);
}

void clMainFrame::OnNewDetachedPane(wxCommandEvent& e)
{
    DockablePane* pane = (DockablePane*)(e.GetClientData());
    if (pane) {
        wxString text = pane->GetName();
        m_DPmenuMgr->AddMenu(text);

        // keep list of all detached panes
        wxArrayString panes = m_DPmenuMgr->GetDeatchedPanesList();
        DetachedPanesInfo dpi(panes);
        EditorConfigST::Get()->WriteObject("DetachedPanesList", &dpi);
    }
}

void clMainFrame::OnDestroyDetachedPane(wxCommandEvent& e)
{
    DockablePane* pane = (DockablePane*)(e.GetClientData());
    if (pane) {
        m_mgr.DetachPane(pane);

        // remove any menu entries for this pane
        m_DPmenuMgr->RemoveMenu(pane->GetName());

        // keep list of all detached panes
        wxArrayString panes = m_DPmenuMgr->GetDeatchedPanesList();
        DetachedPanesInfo dpi(panes);
        EditorConfigST::Get()->WriteObject("DetachedPanesList", &dpi);

        pane->Destroy();
        m_mgr.Update();
    }
}

void clMainFrame::OnDockablePaneClosed(wxAuiManagerEvent& e)
{
    DockablePane* pane = dynamic_cast<DockablePane*>(e.GetPane()->window);
    wxAuiPaneInfo* pInfo = e.GetPane();
    if (pInfo->IsOk()) {
        DockablePaneMenuManager::HackHidePane(false, *pInfo, &m_mgr);
    }
    if (pane) {
        wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, XRCID("close_pane"));
        pane->GetEventHandler()->ProcessEvent(evt);
    } else {
        e.Skip();
    }
}

void clMainFrame::OnFunctionCalltipUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    clEditor* editor = GetMainBook()->GetActiveEditor();
    event.Enable(editor ? true : false);
}

void clMainFrame::OnReloadWorkspace(wxCommandEvent& event)
{
    wxUnusedVar(event);

    // let the plugins close any custom workspace
    clCommandEvent e(wxEVT_CMD_RELOAD_WORKSPACE, GetId());
    e.SetEventObject(this);
    if (EventNotifier::Get()->ProcessEvent(e)) {
        return; // this event was handled by a plugin
    }

    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    if (dbgr && dbgr->IsRunning()) {
        return;
    }

    SaveLayoutAndSession();
    ManagerST::Get()->ReloadWorkspace();
}

void clMainFrame::OnReloadWorkspaceUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    event.Enable(ManagerST::Get()->IsWorkspaceOpen());
}

void clMainFrame::RebuildProject(const wxString& projectName)
{
    // Let the plugins handle this first
    clBuildEvent buildEvent(wxEVT_BUILD_STARTING);
    buildEvent.SetKind("clean");
    if (EventNotifier::Get()->ProcessEvent(buildEvent)) {
        // For plugin-based workspaces, invoke the rebuild event afterward
        m_postBuildEndAction = ePostBuildEndAction::kRebuildProject;
        return;
    }

    // For C++ workspace, push 'clean' and 'build' command into a queue
    bool enable = !ManagerST::Get()->IsBuildInProgress() && !projectName.IsEmpty();
    if (enable) {
        wxString conf;
        // get the selected configuration to be built
        BuildConfigPtr bldConf = clCxxWorkspaceST::Get()->GetProjBuildConf(projectName, wxEmptyString);
        if (bldConf) {
            conf = bldConf->GetName();
        }

        // first we place a clean command
        QueueCommand buildInfo(projectName, conf, false, QueueCommand::kClean);
        if (bldConf && bldConf->IsCustomBuild()) {
            buildInfo.SetKind(QueueCommand::kCustomBuild);
            buildInfo.SetCustomBuildTarget("Clean");
        }
        ManagerST::Get()->PushQueueCommand(buildInfo);

        // now we place a build command
        buildInfo = QueueCommand(projectName, conf, false, QueueCommand::kBuild);

        if (bldConf && bldConf->IsCustomBuild()) {
            buildInfo.SetKind(QueueCommand::kCustomBuild);
            buildInfo.SetCustomBuildTarget("Build");
        }
        ManagerST::Get()->PushQueueCommand(buildInfo);

        // process the queue
        ManagerST::Get()->ProcessCommandQueue();
    }
}

void clMainFrame::OnBatchBuildUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    if (!clWorkspaceManager::Get().GetWorkspace() || !clWorkspaceManager::Get().GetWorkspace()->IsBuildSupported()) {
        e.Enable(false);
    } else {
        bool enable = !ManagerST::Get()->IsBuildInProgress() && ManagerST::Get()->IsWorkspaceOpen();
        e.Enable(enable);
    }
}

void clMainFrame::OnBatchBuild(wxCommandEvent& e)
{
    BatchBuildDlg* batchBuild = new BatchBuildDlg(this);
    if (batchBuild->ShowModal() == wxID_OK) {
        // build the projects
        // add all build items to queue
        for (const auto& queueCommand : batchBuild->GetBuildInfoList()) {
            ManagerST::Get()->PushQueueCommand(queueCommand);
        }
    }
    batchBuild->Destroy();

    // start the build process
    ManagerST::Get()->ProcessCommandQueue();
}

void clMainFrame::SetFrameTitle(clEditor* editor)
{
    wxString title;
    if (editor && editor->GetModify()) {
        title << wxT(" \u25CF ");
    }

    if (m_mainFrameTitleTemplate.empty()) {
        m_mainFrameTitleTemplate = clConfig::Get().Read(kConfigFrameTitlePattern, wxString("$workspace $fullpath"));
    }
    wxString username = ::wxGetUserId();
    username.Prepend("[ ").Append(" ]");

    wxString workspace =
        clWorkspaceManager::Get().GetWorkspace() ? clWorkspaceManager::Get().GetWorkspace()->GetName() : wxString();

    if (!workspace.IsEmpty()) {
        workspace.Prepend("[ ").Append(" ]");
    }

    wxString fullname, fullpath;
    if (editor) {
        if (editor->IsRemoteFile()) {
            fullname = editor->GetRemotePath().AfterLast('/');
            fullpath = editor->GetRemotePath();
        } else {
            // We support the following macros:
            fullname = editor->GetFileName().GetFullName();
            fullpath = editor->GetFileName().GetFullPath();
        }
    }

    wxString pattern = m_mainFrameTitleTemplate;
    pattern.Replace("$workspace", workspace);
    pattern.Replace("$user", username);
    pattern.Replace("$filename", fullname);
    pattern.Replace("$fullpath", fullpath);

    pattern.Trim().Trim(false);
    if (pattern.empty()) {
        pattern << "CodeLite";
    }

    title << pattern;

    // notify the plugins
    clCommandEvent titleEvent(wxEVT_CL_FRAME_TITLE);
    titleEvent.SetString(title);
    EventNotifier::Get()->AddPendingEvent(titleEvent);

    // Update the title
    SetTitle(titleEvent.GetString());

#if !wxUSE_NATIVE_CAPTION
    m_captionBar->SetCaption(titleEvent.GetString());
#endif
}

void clMainFrame::OnBuildWorkspace(wxCommandEvent& e)
{
    // start the build process
    wxUnusedVar(e);
    ManagerST::Get()->BuildWorkspace();
}

void clMainFrame::OnBuildWorkspaceUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    if (!clWorkspaceManager::Get().GetWorkspace() || !clWorkspaceManager::Get().GetWorkspace()->IsBuildSupported()) {
        e.Enable(false);
    } else {
        e.Enable(ManagerST::Get()->IsWorkspaceOpen() && !ManagerST::Get()->IsBuildInProgress());
    }
}

void clMainFrame::OnDetachDebuggerViewTab(wxCommandEvent& e)
{
    size_t sel = GetDebuggerPane()->GetNotebook()->GetSelection();
    wxWindow* page = GetDebuggerPane()->GetNotebook()->GetCurrentPage();
    wxString text = GetDebuggerPane()->GetNotebook()->GetPageText(sel);
    int bmp = GetDebuggerPane()->GetNotebook()->GetPageBitmapIndex(sel);

    DockablePane* pane = new DockablePane(this, PaneId::DEBUG_BAR, text, false, wxSize(200, 200));
    page->Reparent(pane);

    // remove the page from the notebook
    GetDebuggerPane()->GetNotebook()->RemovePage(sel);
    pane->SetChildNoReparent(page);
    wxUnusedVar(e);
}

void clMainFrame::OnCleanWorkspace(wxCommandEvent& e)
{
    wxUnusedVar(e);
    ManagerST::Get()->CleanWorkspace();
}

void clMainFrame::OnCleanWorkspaceUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    if (!clWorkspaceManager::Get().GetWorkspace() || !clWorkspaceManager::Get().GetWorkspace()->IsBuildSupported()) {
        e.Enable(false);
    } else {
        e.Enable(ManagerST::Get()->IsWorkspaceOpen() && !ManagerST::Get()->IsBuildInProgress());
    }
}

void clMainFrame::OnReBuildWorkspace(wxCommandEvent& e)
{
    wxUnusedVar(e);
    ManagerST::Get()->RebuildWorkspace();
}

void clMainFrame::OnReBuildWorkspaceUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    if (!clWorkspaceManager::Get().GetWorkspace() || !clWorkspaceManager::Get().GetWorkspace()->IsBuildSupported()) {
        e.Enable(false);
    } else {
        e.Enable(ManagerST::Get()->IsWorkspaceOpen() && !ManagerST::Get()->IsBuildInProgress());
    }
}

void clMainFrame::OnOpenShellFromFilePath(wxCommandEvent& e)
{
    // get the file path
    wxString filepath;
    clEditor* editor = GetEditorFromEvent(GetMainBook(), e);
    if (editor) {
        filepath = editor->GetFileName().GetPath();
    }

    if (filepath.IsEmpty()) {
        return;
    }

    DirSaver ds;
    wxSetWorkingDirectory(filepath);

    // Apply the environment variables before opening the shell
    EnvSetter setter;
    FileUtils::OpenTerminal(filepath);
}

void clMainFrame::OnSyntaxHighlight(wxCommandEvent& e)
{
    SyntaxHighlightDlg dlg(this);
    if ((dlg.ShowModal() == wxID_OK) && dlg.IsRestartRequired()) {
        // A restart required
        DoSuggestRestart();
    }
}

void clMainFrame::OnStartQuickDebug(clDebugEvent& e)
{
    e.Skip();

    bool bStartedInDebugMode = GetTheApp()->IsStartedInDebuggerMode();
    // Disable the 'StartedInDebuggerMode' flag - so this will only happen once
    GetTheApp()->SetStartedInDebuggerMode(false);

    // Set the selected debugger
    DebuggerMgr::Get().SetActiveDebugger(e.GetDebuggerName());
    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();

    if (dbgr && !dbgr->IsRunning()) {

        wxString exepath = bStartedInDebugMode ? GetTheApp()->GetExeToDebug() : e.GetExecutableName();
        wxString wd = bStartedInDebugMode ? GetTheApp()->GetDebuggerWorkingDirectory() : e.GetWorkingDirectory();
        wxArrayString cmds =
            bStartedInDebugMode ? wxArrayString() : wxStringTokenize(e.GetStartupCommands(), "\n", wxTOKEN_STRTOK);

        // update the debugger information
        DebuggerInformation dinfo;
        DebuggerMgr::Get().GetDebuggerInformation(e.GetDebuggerName(), dinfo);
        dinfo.breakAtWinMain = true;

        // Allow the quick debug to replace the debugger executable
        if (!bStartedInDebugMode && !e.GetAlternateDebuggerPath().IsEmpty()) {
            dinfo.path = e.GetAlternateDebuggerPath();
        }

        // read the console command
        dinfo.consoleCommand = EditorConfigST::Get()->GetOptions()->GetProgramConsoleCommand();

        wxString dbgname = dinfo.path;
        dbgname = EnvironmentConfig::Instance()->ExpandVariables(dbgname, true);

        // launch the debugger
        dbgr->SetObserver(ManagerST::Get());
        dbgr->SetDebuggerInformation(dinfo);

        DebuggerStartupInfo startup_info;
        startup_info.debugger = dbgr;

        // notify plugins that we're about to start debugging
        clDebugEvent eventStarting(wxEVT_DEBUG_STARTING);
        eventStarting.SetClientData(&startup_info);
        if (EventNotifier::Get()->ProcessEvent(eventStarting)) {
            return;
        }

        clDebuggerBreakpoint::Vec_t bpList = ManagerST::Get()->GetBreakpointsMgr()->GetBreakpoints();
        if (!eventStarting.GetBreakpoints().empty()) {
            // one or some plugins sent us list of breakpoints, use them instead
            bpList.swap(eventStarting.GetBreakpoints());
        }

        wxString tty;
#ifndef __WXMSW__
        if (!ManagerST::Get()->StartTTY(
                clDebuggerTerminalPOSIX::MakeExeTitle(
                    exepath, (bStartedInDebugMode ? GetTheApp()->GetDebuggerArgs() : e.GetArguments())),
                tty)) {
            wxMessageBox(_("Could not start TTY console for debugger!"), _("codelite"), wxOK | wxCENTER | wxICON_ERROR);
        }
#endif

        dbgr->SetIsRemoteDebugging(false);

        // Start the debugger
        DebugSessionInfo si;
        si.debuggerPath = dbgname;
        si.exeName = exepath;
        si.cwd = wd;
        si.cmds = cmds;
        si.bpList = bpList;
        si.ttyName = tty;
        si.enablePrettyPrinting = dinfo.enableGDBPrettyPrinting;
        si.isSSHDebugging = e.IsSSHDebugging();
        si.sshAccountName = e.GetSshAccount();
        dbgr->Start(si, nullptr);

        // notify plugins that the debugger just started
        {
            clDebugEvent eventStarted(wxEVT_DEBUG_STARTED);
            eventStarted.SetClientData(&startup_info);
            EventNotifier::Get()->ProcessEvent(eventStarted);
        }
        dbgr->Run(bStartedInDebugMode ? GetTheApp()->GetDebuggerArgs() : e.GetArguments(), wxEmptyString);
    } else if (!dbgr && !bStartedInDebugMode) {
        e.Skip(false); // let other plugins process this
    }
}

void clMainFrame::OnQuickDebug(wxCommandEvent& e)
{
    // launch the debugger
    QuickDebugDlg dlg(this);
    dlg.ShowModal();
}

void clMainFrame::OnDebugCoreDump(wxCommandEvent& e)
{
    // launch the debugger
    DebugCoreDumpDlg* dlg = new DebugCoreDumpDlg(this);
    if (dlg->ShowModal() == wxID_OK) {

        DebuggerMgr::Get().SetActiveDebugger(dlg->GetDebuggerName());
        IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();

        if (dbgr && !dbgr->IsRunning()) {

            wxString debuggingcommand;
            debuggingcommand << "-c " << dlg->GetCore() << " " << dlg->GetExe();
            wxString wd = dlg->GetWorkingDirectory();

            // update the debugger information
            DebuggerInformation dinfo;
            DebuggerMgr::Get().GetDebuggerInformation(dlg->GetDebuggerName(), dinfo);
            dinfo.breakAtWinMain = false;

            // read the console command
            dinfo.consoleCommand = EditorConfigST::Get()->GetOptions()->GetProgramConsoleCommand();

            wxString dbgname = dinfo.path;
            dbgname = EnvironmentConfig::Instance()->ExpandVariables(dbgname, true);

            // launch the debugger
            dbgr->SetObserver(ManagerST::Get());
            dbgr->SetDebuggerInformation(dinfo);

            DebuggerStartupInfo startup_info;
            startup_info.debugger = dbgr;

            // notify plugins that we're about to start debugging
            {
                clDebugEvent eventStarting(wxEVT_DEBUG_STARTING);
                eventStarting.SetClientData(&startup_info);
                if (EventNotifier::Get()->ProcessEvent(eventStarting)) {
                    dlg->Destroy();
                    return;
                }
            }

            wxString tty;
#ifndef __WXMSW__
            if (!ManagerST::Get()->StartTTY(clDebuggerTerminalPOSIX::MakeCoreTitle(dlg->GetCore()), tty)) {
                wxMessageBox(
                    _("Could not start TTY console for debugger!"), _("codelite"), wxOK | wxCENTER | wxICON_ERROR);
            }
#endif
            dbgr->SetIsRemoteDebugging(false);

            // The next two are empty, but are required as parameters
            std::vector<clDebuggerBreakpoint> bpList;
            wxArrayString cmds;

            DebugSessionInfo si;
            si.debuggerPath = dbgname;
            si.exeName = debuggingcommand;
            si.cwd = wd;
            si.bpList = bpList;
            si.cmds = cmds;
            si.ttyName = tty;
            si.enablePrettyPrinting = dinfo.enableGDBPrettyPrinting;
            dbgr->Start(si, nullptr);

            // notify plugins that the debugger just started
            {
                clDebugEvent eventStarted(wxEVT_DEBUG_STARTED);
                eventStarted.SetClientData(&startup_info);
                EventNotifier::Get()->ProcessEvent(eventStarted);
            }

            // Make sure that the debugger pane is visible, and select the stack trace tab
            wxAuiPaneInfo& info = GetDockingManager().GetPane("Debugger");
            if (info.IsOk() && !info.IsShown()) {
                ManagerST::Get()->ShowDebuggerPane();
            }

            clMainFrame::Get()->GetDebuggerPane()->SelectTab(wxGetTranslation(DebuggerPane::FRAMES));
            ManagerST::Get()->UpdateDebuggerPane();

            // Finally, get the call-stack and 'continue' gdb (which seems to be necessary for things to work...)
            dbgr->ListFrames();
            dbgr->Continue();

        } else if (!dbgr) {
            clDebugEvent event(wxEVT_DBG_UI_CORE_FILE);
            event.SetDebuggerName(dlg->GetDebuggerName());
            event.SetExecutableName(dlg->GetExe());
            event.SetCoreFile(dlg->GetCore());
            event.SetWorkingDirectory(dlg->GetWorkingDirectory());
            EventNotifier::Get()->AddPendingEvent(event);
        }
    }
    dlg->Destroy();
}

void clMainFrame::OnQuickDebugUI(wxUpdateUIEvent& e) // (Also used by DebugCoreDump)
{
    CHECK_SHUTDOWN();
    e.Enable(true);
}

void clMainFrame::OnShowWhitespaceUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    OptionsConfigPtr options = EditorConfigST::Get()->GetOptions();
    if (e.GetId() == XRCID("whitepsace_invisible")) {
        e.Check(options->GetShowWhitspaces() == 0);
    } else if (e.GetId() == XRCID("whitepsace_always")) {
        e.Check(options->GetShowWhitspaces() == 1);
    } else if (e.GetId() == XRCID("whitespace_visiable_after_indent")) {
        e.Check(options->GetShowWhitspaces() == 2);
    } else if (e.GetId() == XRCID("whitespace_indent_only")) {
        e.Check(options->GetShowWhitspaces() == 3);
    }
}

void clMainFrame::OnShowWhitespace(wxCommandEvent& e)
{
    OptionsConfigPtr options = EditorConfigST::Get()->GetOptions();
    if (e.GetId() == XRCID("whitepsace_invisible")) {
        options->SetShowWhitspaces(0);
    } else if (e.GetId() == XRCID("whitepsace_always")) {
        options->SetShowWhitspaces(1);
    } else if (e.GetId() == XRCID("whitespace_visiable_after_indent")) {
        options->SetShowWhitspaces(2);
    } else if (e.GetId() == XRCID("whitespace_indent_only")) {
        options->SetShowWhitspaces(3);
    }

    GetMainBook()->ShowWhitespace(options->GetShowWhitspaces());

    // save the settings
    EditorConfigST::Get()->SetOptions(options);
}

void clMainFrame::OnNextTab(wxCommandEvent& e)
{
    int idx = GetMainBook()->GetCurrentPageIndex();

    if (idx != wxNOT_FOUND) {
        clTab::Vec_t tabs;
        GetMainBook()->GetAllTabs(tabs);

        idx = (idx + 1) % tabs.size();
        GetMainBook()->SelectPage(GetMainBook()->GetPage(idx));
    }
}

void clMainFrame::OnPrevTab(wxCommandEvent& e)
{
    int idx = GetMainBook()->GetCurrentPageIndex();

    if (idx != wxNOT_FOUND) {
        idx--;

        if (idx < 0) {
            clTab::Vec_t tabs;
            GetMainBook()->GetAllTabs(tabs);

            idx = tabs.size() - 1;
        }

        GetMainBook()->SelectPage(GetMainBook()->GetPage(idx));
    }
}

void clMainFrame::OnNextPrevTab_UI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    clEditor* editor = GetMainBook()->GetActiveEditor();
    bool hasEditor = editor ? true : false;
    if (!hasEditor) {
        e.Enable(false);
        return;
    }

    e.Enable(true);
}

void clMainFrame::OnIncrementalSearch(wxCommandEvent& event)
{
    wxUnusedVar(event);
    GetMainBook()->ShowQuickBar(true);
}

void clMainFrame::OnIncrementalReplace(wxCommandEvent& event)
{
    wxUnusedVar(event);
    GetMainBook()->ShowQuickBar(true);
}

void clMainFrame::OnRetagWorkspace(wxCommandEvent& event)
{
    // See if any of the plugins want to handle this event by itself
    bool fullRetag = !(event.GetId() == XRCID("retag_workspace"));
    wxCommandEvent e(fullRetag ? wxEVT_CMD_RETAG_WORKSPACE_FULL : wxEVT_CMD_RETAG_WORKSPACE, GetId());
    e.SetEventObject(this);
    if (EventNotifier::Get()->ProcessEvent(e)) {
        return;
    }

    TagsManager::RetagType type = TagsManager::Retag_Quick_No_Scan;
    if (event.GetId() == XRCID("retag_workspace")) {
        type = TagsManager::Retag_Quick;
    }

    else if (event.GetId() == XRCID("full_retag_workspace")) {
        type = TagsManager::Retag_Full;
    }

    else if (event.GetId() == XRCID("retag_workspace_no_includes")) {
        type = TagsManager::Retag_Quick_No_Scan;
    }
    ManagerST::Get()->RetagWorkspace(type);
}

void clMainFrame::OnShowBuiltInTerminal(wxCommandEvent& e)
{
    wxUnusedVar(e);
    // toggle the terminal view
    if (GetOutputPane()->IsShown() && GetOutputPane()->GetBuiltInTerminal()->IsFocused()) {
        ManagerST::Get()->ShowOutputPane(_("Terminal"), false, false);
        if (clGetManager()->GetActiveEditor()) {
            // set the focus back to the editor
            clGetManager()->GetActiveEditor()->SetActive();
        } else {
            ::SetBestFocus(this);
        }
    } else {
        ManagerST::Get()->ShowOutputPane(_("Terminal"), true, true);
        GetOutputPane()->GetBuiltInTerminal()->Focus();
    }
}

void clMainFrame::OnShowFullScreen(wxCommandEvent& e)
{
    wxUnusedVar(e);

    if (IsFullScreen()) {
        ShowFullScreen(false);

    } else {
        ShowFullScreen(
            true, wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION);

        // Re-apply the menu accelerators
        ManagerST::Get()->UpdateMenuAccelerators();
    }
}

void clMainFrame::OnReloadExternallModified(wxCommandEvent& e)
{
    wxUnusedVar(e);
    ReloadExternallyModifiedProjectFiles();
    GetMainBook()->ReloadExternallyModified(true);
}

void clMainFrame::OnReloadExternallModifiedNoPrompt(wxCommandEvent& e)
{
    wxUnusedVar(e);
    GetMainBook()->ReloadExternallyModified(false);
}

bool clMainFrame::ReloadExternallyModifiedProjectFiles()
{
    if (ManagerST::Get()->IsWorkspaceOpen() == false) {
        return false;
    }

    clCxxWorkspace* workspace = clCxxWorkspaceST::Get();
    bool workspace_modified = false, project_modified = false;

    // check if the workspace needs reloading and ask the user for confirmation
    // if it does
    if (workspace->GetWorkspaceLastModifiedTime() < workspace->GetFileLastModifiedTime()) {
        // always update last modification time: if the user chooses to reload it
        // will not matter, and it avoids the program prompting the user repeatedly
        // if he chooses not to reload the workspace
        workspace->SetWorkspaceLastModifiedTime(workspace->GetFileLastModifiedTime());
        workspace_modified = true;
    }

    // check if any of the projects in the workspace needs reloading
    wxArrayString projects;
    workspace->GetProjectList(projects);

    for (size_t i = 0; i < projects.GetCount(); ++i) {
        wxString errStr;
        ProjectPtr proj = workspace->FindProjectByName(projects[i], errStr);

        if (proj->GetProjectLastModifiedTime() < proj->GetFileLastModifiedTime()) {
            // always update last modification time: if the user chooses to reload it
            // will not matter, and it avoids the program prompting the user repeatedly
            // if he chooses not to reload some of the projects
            proj->SetProjectLastModifiedTime(proj->GetFileLastModifiedTime());
            project_modified = true;
        }
    }

    if (!project_modified && !workspace_modified) {
        return false;
    }

    wxStandardID res = ::PromptForYesNoDialogWithCheckbox(_("Workspace or project settings have been modified outside "
                                                            "of CodeLite\nWould you like to reload the workspace?"),
                                                          "ReloadWorkspaceWhenAltered",
                                                          _("Reload workspace"),
                                                          _("Not now"));
    // Don't do anything if "X" is pressed
    if (res != wxID_CANCEL) {
        if (res == wxID_YES) {
            wxCommandEvent evtReload(wxEVT_COMMAND_MENU_SELECTED, XRCID("reload_workspace"));
            GetEventHandler()->ProcessEvent(evtReload);

        } else {
            // user cancelled the dialog or chosed not to reload the workspace
            if (GetMainBook()->GetActiveEditor()) {
                GetMainBook()->GetActiveEditor()->CallAfter(&clEditor::SetActive);
            }
        }
    }
    return true;
}

bool clMainFrame::SaveLayoutAndSession()
{
    // save the current session before closing
    // We do this before 'CloseAll' so the session will
    // store the list of tabs

    // Let the plugin process this first
    clCommandEvent eventSaveSession(wxEVT_SAVE_SESSION_NEEDED);
    if (!EventNotifier::Get()->ProcessEvent(eventSaveSession)) {
        // Do the default session store
        if (ManagerST::Get()->IsWorkspaceOpen()) {
            wxString sessionName = clCxxWorkspaceST::Get()->GetWorkspaceFileName().GetFullPath();
            SessionEntry session;
            session.SetWorkspaceName(sessionName);
            GetMainBook()->SaveSession(session);
            ManagerST::Get()->GetBreakpointsMgr()->SaveSession(session);
            SessionManager::Get().Save(sessionName, session);
            SessionManager::Get().SetLastSession(sessionName);
        } else {
            // Create a default session
            wxString sessionName("Default");
            SessionEntry session;
            session.SetWorkspaceName(sessionName);
            GetMainBook()->SaveSession(session);
            ManagerST::Get()->GetBreakpointsMgr()->SaveSession(session);
            SessionManager::Get().Save(sessionName, session);
            SessionManager::Get().SetLastSession(sessionName);
        }
    }

    // make sure there are no 'unsaved documents'
    if (!GetMainBook()->CloseAll(true)) {
        return false;
    }

    // save general information
    if (IsMaximized()) {
        m_frameGeneralInfo.SetFrameSize(wxSize(800, 600));
    } else {
        m_frameGeneralInfo.SetFrameSize(this->GetSize());
    }
#ifdef __WXMAC__
    m_frameGeneralInfo.SetFramePosition(wxPoint(50, 50));
#else
    m_frameGeneralInfo.SetFramePosition(this->GetScreenPosition());
#endif

    EditorConfigST::Get()->Begin();

    SetFrameFlag(IsMaximized(), CL_MAXIMIZE_FRAME);
    SetFrameFlag(IsFullScreen(), CL_FULLSCREEN);
    EditorConfigST::Get()->WriteObject("GeneralInfo", &m_frameGeneralInfo);
    EditorConfigST::Get()->SetInteger("ShowNavBar", m_mainBook->IsNavBarEnabled() ? 1 : 0);
    GetWorkspacePane()->SaveWorkspaceViewTabOrder();
    GetOutputPane()->SaveTabOrder();

    // keep list of all detached panes
    wxArrayString panes = m_DPmenuMgr->GetDeatchedPanesList();
    DetachedPanesInfo dpi(panes);
    EditorConfigST::Get()->WriteObject("DetachedPanesList", &dpi);

    // Update the current perspective as the "NORMAL" one
    ManagerST::Get()->GetPerspectiveManager().SavePerspective(NORMAL_LAYOUT);

    // save the notebooks styles
    EditorConfigST::Get()->SetInteger("MainBook", GetMainBook()->GetBookStyle());
    EditorConfigST::Get()->Save();
    return true;
}

void clMainFrame::SaveGeneralSettings() { EditorConfigST::Get()->WriteObject("GeneralInfo", &m_frameGeneralInfo); }

void clMainFrame::OnNextFiFMatch(wxCommandEvent& e)
{
    wxUnusedVar(e);
    GetOutputPane()->GetFindResultsTab()->NextMatch();
}

void clMainFrame::OnPreviousFiFMatch(wxCommandEvent& e)
{
    wxUnusedVar(e);
    GetOutputPane()->GetFindResultsTab()->PrevMatch();
}

void clMainFrame::OnNextFiFMatchUI(wxUpdateUIEvent& e) { CHECK_SHUTDOWN(); }

void clMainFrame::OnPreviousFiFMatchUI(wxUpdateUIEvent& e) { CHECK_SHUTDOWN(); }

void clMainFrame::OnFindResourceXXX(wxCommandEvent& e)
{
    // sanity
    if (!clWorkspaceManager::Get().IsWorkspaceOpened()) {
        return;
    }

    // Determine the search type
    // Let the plugins a chance before we handle this event
    wxCommandEvent eventOpenResource(wxEVT_CMD_OPEN_RESOURCE, GetId());
    eventOpenResource.SetEventObject(this);
    if (EventNotifier::Get()->ProcessEvent(eventOpenResource)) {
        return;
    }

    wxString initialText;
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor && editor->HasSelection()) {
        int start = editor->GetSelectionNStart(0);
        int end = editor->GetSelectionNEnd(0);
        initialText = editor->GetTextRange(start, end);
    }
    OpenResourceDialog dlg(this, PluginManager::Get(), initialText);

    if (dlg.ShowModal() == wxID_OK && !dlg.GetSelections().empty()) {
        std::vector<OpenResourceDialogItemData*> items = dlg.GetSelections();
        for (auto item : items) {

            // try the plugins first
            clCommandEvent open_resource_event(wxEVT_OPEN_RESOURCE_FILE_SELECTED);
            open_resource_event.SetFileName(item->m_file);
            open_resource_event.SetLineNumber(item->m_line);
            open_resource_event.SetInt(item->m_column); // use the int field for the column

            if (EventNotifier::Get()->ProcessEvent(open_resource_event)) {
                continue;
            }

            // default behaviour
            OpenResourceDialog::OpenSelection(*item, PluginManager::Get());
        }
    }
}

void clMainFrame::OnCheckForUpdate(wxCommandEvent& e)
{
    if (!m_webUpdate) {
        m_webUpdate = new WebUpdateJob(this, true, clConfig::Get().Read("PromptForNewReleaseOnly", false));
        m_webUpdate->Check();
    }
}

void clMainFrame::OnShowActiveProjectSettings(wxCommandEvent& e)
{
    wxUnusedVar(e);
    if (!clCxxWorkspaceST::Get()->IsOpen()) {
        return;
    }
    GetWorkspaceTab()->OpenProjectSettings();
}

void clMainFrame::OnShowActiveProjectSettingsUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    wxArrayString projectList;
    clCxxWorkspaceST::Get()->GetProjectList(projectList);
    e.Enable(ManagerST::Get()->IsWorkspaceOpen() && (projectList.IsEmpty() == false));
}

void clMainFrame::StartTimer() { m_timer->Start(1000, true); }

void clMainFrame::SelectBestEnvSet()
{
    ///////////////////////////////////////////////////
    // Select the environment variables set to use
    ///////////////////////////////////////////////////

    // Set the workspace's environment variable set to the active one
    wxString projectSetName;
    wxString projectDbgSetName;

    // First, if the project has an environment which is not '<Use Defaults>' use it
    wxString workspaceSetName;
    if (ManagerST::Get()->IsWorkspaceOpen()) {
        wxString activeProj = clCxxWorkspaceST::Get()->GetActiveProjectName();
        ProjectPtr p = ManagerST::Get()->GetProject(activeProj);
        if (p) {
            BuildConfigPtr buildConf = clCxxWorkspaceST::Get()->GetProjBuildConf(activeProj, wxEmptyString);
            if (buildConf) {
                if (buildConf->GetEnvVarSet() != USE_WORKSPACE_ENV_VAR_SET &&
                    buildConf->GetEnvVarSet() != "<Use Workspace Settings>" /* backward support */) {
                    projectSetName = buildConf->GetEnvVarSet();
                }

                if (buildConf->GetDbgEnvSet() != USE_GLOBAL_SETTINGS) {
                    projectDbgSetName = buildConf->GetDbgEnvSet();
                }
            }
        }
        workspaceSetName = clCxxWorkspaceST::Get()->GetLocalWorkspace()->GetActiveEnvironmentSet();
    }

    wxString globalActiveSet = "Default";
    wxString activeSetName;
    EnvVarList vars = EnvironmentConfig::Instance()->GetSettings();

    // By default, use the global one
    activeSetName = globalActiveSet;

    if (!projectSetName.IsEmpty() && vars.IsSetExist(projectSetName)) {
        activeSetName = projectSetName;

    } else if (!workspaceSetName.IsEmpty() && vars.IsSetExist(workspaceSetName)) {
        activeSetName = workspaceSetName;
    }

    vars.SetActiveSet(activeSetName);
    EnvironmentConfig::Instance()->SetSettings(vars);

    ///////////////////////////////////////////////////
    // Select the debugger PreDefined Types settings
    ///////////////////////////////////////////////////
    DebuggerSettingsPreDefMap preDefTypeMap;
    DebuggerConfigTool::Get()->ReadObject("DebuggerCommands", &preDefTypeMap);

    wxString dbgSetName = "Default";
    if (!projectDbgSetName.IsEmpty() && preDefTypeMap.IsSetExist(projectDbgSetName)) {
        dbgSetName = projectDbgSetName;
    }

    preDefTypeMap.SetActive(dbgSetName);
    DebuggerConfigTool::Get()->WriteObject("DebuggerCommands", &preDefTypeMap);
}

void clMainFrame::OnWorkspaceEditorPreferences(wxCommandEvent& e)
{
    GetWorkspaceTab()->GetFileView()->GetEventHandler()->ProcessEvent(e);
}

void clMainFrame::OnWorkspaceSettings(wxCommandEvent& e)
{
    GetWorkspaceTab()->GetFileView()->GetEventHandler()->ProcessEvent(e);
}

void clMainFrame::OnGotoCodeLiteDownloadPage(wxCommandEvent& e)
{
    wxUnusedVar(e);
    wxLaunchDefaultBrowser(m_codeliteDownloadPageURL);
    m_codeliteDownloadPageURL.Clear();
}

void clMainFrame::DoSuggestRestart()
{
#ifdef __WXMSW__
    ::wxMessageBox(
        _("A restart is required for changes to take effect"), "CodeLite", wxICON_INFORMATION | wxOK | wxCENTER);
#else
    if (::wxMessageBox(_("A restart is required for changes to take effect\rContinue with restart?"),
                       "CodeLite",
                       wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION,
                       this) != wxYES) {
        return;
    }

    clCommandEvent event_dummy;
    ManagerST::Get()->OnRestart(event_dummy);
#endif
}

void clMainFrame::OnRestoreDefaultLayout(wxCommandEvent& e)
{
    e.Skip();

#ifndef __WXMAC__
    clWindowUpdateLocker locker(this);
#endif

    clDEBUG() << "Restoring layout" << endl;

    // Close all docking panes
    wxAuiPaneInfoArray& panes = m_mgr.GetAllPanes();

    for (size_t i = 0; i < panes.GetCount(); ++i) {
        // make sure that the caption is visible
        panes.Item(i).CaptionVisible(true);
        wxAuiPaneInfo& p = panes.Item(i);

        if (p.window) {
            DockablePane* d = dynamic_cast<DockablePane*>(p.window);
            if (d) {

                wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, XRCID("close_pane"));
                p.window->GetEventHandler()->AddPendingEvent(evt);
            }
        }
    }

    ManagerST::Get()->GetPerspectiveManager().DeleteAllPerspectives();

    // clear the detached panes list
    DetachedPanesInfo dpi;
    clGetManager()->GetConfigTool()->WriteObject("DetachedPanesList", &dpi);

    m_mgr.LoadPerspective(m_defaultLayout, false);
    m_mgr.Update();
}

void clMainFrame::SetAUIManagerFlags()
{
    // Set the manager flags
    unsigned int auiMgrFlags = wxAUI_MGR_TRANSPARENT_HINT | wxAUI_MGR_HINT_FADE;
#if defined(__WXMAC__) || defined(__WXGTK__)
    auiMgrFlags |= wxAUI_MGR_LIVE_RESIZE;
#endif
    m_mgr.SetFlags(auiMgrFlags);
}

void clMainFrame::UpdateAUI()
{
    SetAUIManagerFlags();
    m_mgr.Update();
}

void clMainFrame::OnRetagWorkspaceUI(wxUpdateUIEvent& event) { CHECK_SHUTDOWN(); }

void clMainFrame::OnViewWordWrap(wxCommandEvent& e)
{
    CHECK_SHUTDOWN();

    OptionsConfigPtr opts = EditorConfigST::Get()->GetOptions();
    opts->SetWordWrap(e.IsChecked());
    EditorConfigST::Get()->SetOptions(opts);

    GetMainBook()->SetViewWordWrap(e.IsChecked());
}

void clMainFrame::OnViewWordWrapUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    clEditor* editor = GetMainBook()->GetActiveEditor();
    bool hasEditor = editor ? true : false;
    if (!hasEditor) {
        e.Enable(false);
        return;
    }

    OptionsConfigPtr opts = EditorConfigST::Get()->GetOptions();
    e.Enable(true);
    e.Check(opts->GetWordWrap());
}

void clMainFrame::OnGrepWord(wxCommandEvent& e)
{
    CHECK_SHUTDOWN();
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (!editor || editor->GetSelectedText().IsEmpty()) {
        return;
    }

    // Prepare the search data
    bool singleFileSearch(true);
    if (e.GetId() == XRCID("grep_current_workspace")) {
        singleFileSearch = false;
    }

    SearchData data;
    data.SetFindString(editor->GetSelectedText());
    data.SetMatchCase(true);
    data.SetMatchWholeWord(true);
    data.SetRegularExpression(false);
    data.SetDisplayScope(false);
    data.SetEncoding(wxFontMapper::GetEncodingName(editor->GetOptions()->GetFileFontEncoding()));
    data.SetSkipComments(false);
    data.SetSkipStrings(false);
    data.SetColourComments(false);

    wxArrayString files;
    wxArrayString rootDirs;
    wxString mask;
    if (singleFileSearch) {
        rootDirs.Add(wxGetTranslation(SEARCH_IN_CURRENT_FILE));
        files.Add(editor->GetFileName().GetFullPath());
        mask << editor->GetFileName().GetFullName(); // this will ensure that this file is scanned

    } else {
        rootDirs.Add(wxGetTranslation(SEARCH_IN_WORKSPACE_FOLDER));
        ManagerST::Get()->GetWorkspaceFiles(files);
        wxStringSet_t masks;
        // Build a mask that matches the workspace content
        std::for_each(files.begin(), files.end(), [&](const wxString& filename) {
            wxFileName fn(filename);
            wxString curfileMask = fn.GetExt();
            if (fn.GetExt().IsEmpty()) {
                curfileMask = "*";
            } else {
                curfileMask = "*." + fn.GetExt();
            }

            if (masks.count(curfileMask) == 0) {
                masks.insert(curfileMask);
                mask << curfileMask << ";";
            }
        });
    }

    data.SetRootDirs(rootDirs);
    data.SetFiles(files);
    data.UseNewTab(true);
    data.SetOwner(GetOutputPane()->GetFindResultsTab());
    data.SetExtensions(mask);
    SearchThreadST::Get()->PerformSearch(data);
}

void clMainFrame::OnGrepWordUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (e.GetId() == XRCID("grep_current_workspace")) {
        // grep in workspace
        e.Enable(clWorkspaceManager::Get().IsWorkspaceOpened() && editor && !editor->GetSelectedText().IsEmpty());
    } else {
        // grep in file
        e.Enable(editor && !editor->GetSelectedText().IsEmpty());
    }
}

void clMainFrame::OnWebSearchSelection(wxCommandEvent& e)
{
    CHECK_SHUTDOWN();

    const auto editor = GetMainBook()->GetActiveEditor();
    if (!editor) {
        return;
    }

    const auto text = editor->GetSelectedText();
    if (text.IsEmpty()) {
        return;
    }

    const auto options = EditorConfigST::Get()->GetOptions();
    if (options) {
        wxLaunchDefaultBrowser(wxString(options->GetWebSearchPrefix()) << text, wxBROWSER_NOBUSYCURSOR);
    }
}

void clMainFrame::OnWebSearchSelectionUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();

    const auto editor = GetMainBook()->GetActiveEditor();
    e.Enable(editor && !editor->GetSelectedText().IsEmpty());
}

///////////////////// Helper methods /////////////////////////////

void clMainFrame::OnFileSaveUI(wxUpdateUIEvent& event) { event.Enable(true); }

void clMainFrame::OnActivateEditor(wxCommandEvent& e)
{
    clEditor* editor = dynamic_cast<clEditor*>(e.GetEventObject());
    if (editor) {
        editor->SetActive();
    }
}

void clMainFrame::OnActiveEditorChanged(wxCommandEvent& e)
{
    e.Skip();
    SelectBestEnvSet(); // Updates the statusbar bookmark display
}

void clMainFrame::OnLoadSession(wxCommandEvent& e)
{
    wxUnusedVar(e);
    LoadSession(SessionManager::Get().GetLastSession());
}

void clMainFrame::OnShowBuildMenu(wxCommandEvent& e)
{
    // Show the build menu
    CHECK_PTR_RET(m_mainToolbar);
    wxMenu menu;

    // let the plugins build a different menu
    clContextMenuEvent evt(wxEVT_BUILD_CUSTOM_TARGETS_MENU_SHOWING);
    evt.SetEventObject(m_mainToolbar);
    evt.SetMenu(&menu);
    if (!EventNotifier::Get()->ProcessEvent(evt)) {
        DoCreateBuildDropDownMenu(&menu);
    }

    // show the menu
    m_mainToolbar->PopupMenu(&menu);
}

void clMainFrame::DoCreateBuildDropDownMenu(wxMenu* menu)
{
    if (clCxxWorkspaceST::Get()->IsOpen()) {
        menu->Append(XRCID("build_active_project"), _("Build"));
        menu->AppendSeparator();
        menu->Append(XRCID("build_active_project_only"), _("Project Only ") + L"\u2192" + _(" Build"));
        menu->Append(XRCID("clean_active_project_only"), _("Project Only ") + L"\u2192" + _(" Clean"));

        // build the menu and show it
        BuildConfigPtr bldcfg =
            clCxxWorkspaceST::Get()->GetProjBuildConf(clCxxWorkspaceST::Get()->GetActiveProjectName(), "");
        if (bldcfg && bldcfg->IsCustomBuild()) {

            // Update the custom targets
            CustomTargetsMgr::Get().SetTargets(clCxxWorkspaceST::Get()->GetActiveProjectName(),
                                               bldcfg->GetCustomTargets());

            if (!CustomTargetsMgr::Get().GetTargets().empty()) {
                menu->AppendSeparator();
            }

            const CustomTargetsMgr::Map_t& targets = CustomTargetsMgr::Get().GetTargets();
            for (const auto& [winid, d] : targets) {
                menu->Append(winid, d.first);
            }
        }
    }
}

void clMainFrame::OnWorkspaceClosed(clWorkspaceEvent& e)
{
    e.Skip();
    CustomTargetsMgr::Get().Clear();
    PostSizeEvent();
}

void clMainFrame::OnIncrementalSearchUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    event.Enable(true);
    event.Skip();
}

void clMainFrame::OnBuildProjectOnly(wxCommandEvent& event)
{
    wxCommandEvent e(wxEVT_CMD_BUILD_PROJECT_ONLY);
    EventNotifier::Get()->AddPendingEvent(e);
}

void clMainFrame::OnCleanProjectOnly(wxCommandEvent& event)
{
    wxCommandEvent e(wxEVT_CMD_CLEAN_PROJECT_ONLY);
    EventNotifier::Get()->AddPendingEvent(e);
}

void clMainFrame::OnFileSaveAllUI(wxUpdateUIEvent& event)
{
    bool hasModifiedEditor = false;
    clTab::Vec_t tabs;
    GetMainBook()->GetAllTabs(tabs);

    for (size_t i = 0; i < tabs.size(); ++i) {
        if (tabs.at(i).isFile && tabs.at(i).isModified) {
            hasModifiedEditor = true;
            break;

        } else if (!tabs.at(i).isFile) {
            // Send an event
            clCommandEvent modifyEvent(wxEVT_PAGE_MODIFIED_UPDATE_UI);
            modifyEvent.SetClientData(tabs.at(i).window);
            if (EventNotifier::Get()->ProcessEvent(modifyEvent) && modifyEvent.IsAnswer()) {
                hasModifiedEditor = true;
                break;
            }
        }
    }

    event.Enable(hasModifiedEditor);
}

void clMainFrame::OnShowDebuggerWindow(wxCommandEvent& e)
{
    // load the debugger configuration
    clConfig conf("debugger-view.conf");
    DebuggerPaneConfig item;
    conf.ReadItem(&item);

    bool show = e.IsChecked();
    if (e.GetId() == XRCID("debugger_win_locals")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::Locals, show);
    }

    if (e.GetId() == XRCID("debugger_win_watches")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::Watches, show);
    }

    if (e.GetId() == XRCID("debugger_win_output")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::Output, show);
    }

    if (e.GetId() == XRCID("debugger_win_threads")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::Threads, show);
    }

    if (e.GetId() == XRCID("debugger_win_callstack")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::Callstack, show);
    }

    if (e.GetId() == XRCID("debugger_win_memory")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::Memory, show);
    }

    if (e.GetId() == XRCID("debugger_win_breakpoints")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::Breakpoints, show);
    }

    if (e.GetId() == XRCID("debugger_win_asciiview")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::AsciiViewer, show);
    }

    if (e.GetId() == XRCID("debugger_win_disassemble")) {
        item.ShowDebuggerWindow(DebuggerPaneConfig::Disassemble, show);
    }

    conf.WriteItem(&item);
    // Reload the perspective
    ManagerST::Get()->GetPerspectiveManager().LoadPerspective();
}

void clMainFrame::OnShowDebuggerWindowUI(wxUpdateUIEvent& e)
{
    // load the debugger configuration
    // clConfig conf("debugger-view.conf");
    DebuggerPaneConfig item;
    // conf.ReadItem( &item );

    DebuggerPaneConfig::eDebuggerWindows winid = DebuggerPaneConfig::None;

    if (e.GetId() == XRCID("debugger_win_locals")) {
        winid = DebuggerPaneConfig::Locals;
    }

    if (e.GetId() == XRCID("debugger_win_watches")) {
        winid = DebuggerPaneConfig::Watches;
    }

    if (e.GetId() == XRCID("debugger_win_output")) {
        winid = DebuggerPaneConfig::Output;
    }

    if (e.GetId() == XRCID("debugger_win_threads")) {
        winid = DebuggerPaneConfig::Threads;
    }

    if (e.GetId() == XRCID("debugger_win_callstack")) {
        winid = DebuggerPaneConfig::Callstack;
    }

    if (e.GetId() == XRCID("debugger_win_memory")) {
        winid = DebuggerPaneConfig::Memory;
    }

    if (e.GetId() == XRCID("debugger_win_breakpoints")) {
        winid = DebuggerPaneConfig::Breakpoints;
    }

    if (e.GetId() == XRCID("debugger_win_asciiview")) {
        winid = DebuggerPaneConfig::AsciiViewer;
    }

    if (winid != DebuggerPaneConfig::None) {
        e.Check(item.IsDebuggerWindowShown(winid));
    }
}

void clMainFrame::OnThemeChanged(wxCommandEvent& e) { e.Skip(); }

void clMainFrame::OnChangeActiveBookmarkType(wxCommandEvent& e)
{
    clEditor* editor = GetMainBook()->GetActiveEditor();
    if (editor) {
        editor->OnChangeActiveBookmarkType(e);
    }
}

void clMainFrame::OnSettingsChanged(wxCommandEvent& e)
{
    e.Skip();
    SetFrameTitle(GetMainBook()->GetActiveEditor());
    ShowOrHideCaptions();

    // As the toolbar is showing, refresh in case the group spacing was changed
    m_pluginsToolbar->SetGroupSpacing(clConfig::Get().Read(kConfigToolbarGroupSpacing, 50));
    m_pluginsToolbar->Realize();

    auto editors = GetMainBook()->GetAllEditors();

    std::for_each(editors.begin(), editors.end(), [&](clEditor* editor) { editor->PreferencesChanged(); });
    m_mainFrameTitleTemplate = clConfig::Get().Read(kConfigFrameTitlePattern, wxString("$workspace $fullpath"));
}

void clMainFrame::OnDetachEditor(wxCommandEvent& e) { wxUnusedVar(e); }

void clMainFrame::OnDetachEditorUI(wxUpdateUIEvent& e) { e.Enable(GetMainBook()->GetActiveEditor() != NULL); }

void clMainFrame::OnShowStatusBar(wxCommandEvent& event)
{
    GetStatusBar()->Show(event.IsChecked());
    SendSizeEvent();
    clConfig::Get().Write(kConfigShowStatusBar, event.IsChecked());
}

void clMainFrame::OnShowStatusBarUI(wxUpdateUIEvent& event) { event.Check(m_frameHelper->IsStatusBarVisible()); }

void clMainFrame::OnShowToolbar(wxCommandEvent& event)
{
    wxUnusedVar(event);
    DoShowToolbars(!m_pluginsToolbar->IsShown());
    clConfig::Get().Write(kConfigShowToolBar, m_pluginsToolbar->IsShown());
}

void clMainFrame::OnShowToolbarUI(wxUpdateUIEvent& event) { event.Check(m_frameHelper->IsToolbarShown()); }

void clMainFrame::ShowOrHideCaptions()
{
    // load the current state
    bool showCaptions = EditorConfigST::Get()->GetOptions()->IsShowDockingWindowCaption();
    DoShowCaptions(showCaptions);
    m_mgr.Update();
    PostSizeEvent();
}

void clMainFrame::OnOpenFileExplorerFromFilePath(wxCommandEvent& e)
{
    clEditor* editor = GetEditorFromEvent(GetMainBook(), e);
    CHECK_PTR_RET(editor);
    FileUtils::OpenFileExplorerAndSelect(editor->GetFileName());
}

void clMainFrame::OnSwitchWorkspaceUI(wxUpdateUIEvent& event)
{
    CHECK_SHUTDOWN();
    // event.Enable(!clWorkspaceManager::Get().IsWorkspaceOpened());
    event.Enable(true);
}

void clMainFrame::OnSplitSelection(wxCommandEvent& event)
{
    clEditor* editor = GetMainBook()->GetActiveEditor();
    CHECK_PTR_RET(editor);

    editor->SplitSelection();
}

void clMainFrame::OnSplitSelectionUI(wxUpdateUIEvent& event)
{
    clEditor* editor = GetMainBook()->GetActiveEditor();
    event.Enable(editor && editor->HasSelection());
}

void clMainFrame::OnProjectRenamed(clCommandEvent& event)
{
    event.Skip();
    SetFrameTitle(GetMainBook()->GetActiveEditor());
}

void clMainFrame::OnShowTabBar(wxCommandEvent& event)
{
    clConfig::Get().Write(kConfigShowTabBar, event.IsChecked());
    GetMainBook()->ShowTabBar(event.IsChecked());
}

void clMainFrame::OnShowTabBarUI(wxUpdateUIEvent& event) { event.Check(clConfig::Get().Read(kConfigShowTabBar, true)); }

void clMainFrame::OnRunSetupWizard(wxCommandEvent& e)
{
    wxUnusedVar(e);
    if (!StartSetupWizard(false)) {
        GetMainBook()->ApplySettingsChanges();
    }
}

void clMainFrame::OnCloseTabsToTheRight(wxCommandEvent& e)
{
    wxWindow* win = GetWindowFromEvent(GetMainBook(), e);
    if (win) {
        GetMainBook()->CallAfter(&MainBook::CloseTabsToTheRight, win);
    }
}

void clMainFrame::OnMarkEditorReadonly(wxCommandEvent& e)
{
    auto editor = GetEditorFromEvent(GetMainBook(), e);
    CHECK_PTR_RET(editor);

    editor->SetReadOnly(e.IsChecked());
    GetMainBook()->MarkEditorReadOnly(editor);
}

void clMainFrame::OnMarkEditorReadonlyUI(wxUpdateUIEvent& e)
{
    auto editor = GetEditorFromEvent(GetMainBook(), e);
    CHECK_PTR_RET(editor);

    e.Check(!editor->IsEditable());
}

void clMainFrame::OnWorkspaceLoaded(clWorkspaceEvent& e)
{
    e.Skip();
    // If the workspace tab is visible, make it active
    int where = GetWorkspacePane()->GetNotebook()->GetPageIndex(_("Workspace"));
    if (where != wxNOT_FOUND) {
        GetWorkspacePane()->GetNotebook()->SetSelection(where);
    }
}

void clMainFrame::OnFileOpenFolder(wxCommandEvent& event)
{
    wxString path = ::wxDirSelector(_("Select Folder"));
    if (path.IsEmpty()) {
        return;
    }
    GetWorkspacePane()->GetFileExplorer()->OpenFolder(path);
    GetWorkspacePane()->SelectTab(GetWorkspacePane()->GetFileExplorer()->GetCaption());
}

void clMainFrame::OnNewWorkspaceUI(wxUpdateUIEvent& event)
{
    event.Enable(!clWorkspaceManager::Get().IsWorkspaceOpened());
}

void clMainFrame::OnNewProjectUI(wxUpdateUIEvent& event)
{
    event.Enable(clWorkspaceManager::Get().IsWorkspaceOpened() &&
                 clWorkspaceManager::Get().GetWorkspace()->IsProjectSupported());
}

void clMainFrame::OnDebugStarted(clDebugEvent& event)
{
    event.Skip();
    m_debuggerToolbar->Show();
    m_debuggerToolbar->GetParent()->GetSizer()->Layout();

    if (DebuggerMgr::Get().GetActiveDebugger() && DebuggerMgr::Get().GetActiveDebugger()->IsRunning()) {
        // One of CodeLite builtin debuggers is running, load the perspective
        clMainFrame::Get()->GetDebuggerPane()->GetBreakpointView()->Initialize();
        ManagerST::Get()->GetPerspectiveManager().SavePerspective(NORMAL_LAYOUT);
        ManagerST::Get()->GetPerspectiveManager().LoadPerspective(DEBUG_LAYOUT);
    }
}

void clMainFrame::OnDebugEnded(clDebugEvent& event)
{
    event.Skip();
    m_debuggerToolbar->Hide();
    m_debuggerToolbar->GetParent()->GetSizer()->Layout();
}

void clMainFrame::OnPrint(wxCommandEvent& event)
{
    if (GetMainBook()->GetActiveEditor()) {
        GetMainBook()->GetActiveEditor()->Print();
    }
}

void clMainFrame::OnPageSetup(wxCommandEvent& event)
{
    if (GetMainBook()->GetActiveEditor()) {
        GetMainBook()->GetActiveEditor()->PageSetup();
    }
}

void clMainFrame::OnRecentWorkspaceUI(wxUpdateUIEvent& e)
{
    // We don't allow reloading of recent workspace while another is opened
    e.Enable(!clWorkspaceManager::Get().IsWorkspaceOpened());
}

void clMainFrame::OnToggleReverseDebugging(wxCommandEvent& e)
{
    wxUnusedVar(e);
    // Currently only supported on GDB
    if (DebuggerMgr::Get().GetActiveDebugger()) {
        DebuggerMgr::Get().GetActiveDebugger()->EnableReverseDebugging(e.IsChecked());
    }
}

void clMainFrame::OnToggleReverseDebuggingUI(wxUpdateUIEvent& e)
{
    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    bool enable = clCxxWorkspaceST::Get()->IsOpen() && (dbgr && (dbgr->GetName() == "GNU gdb debugger"));
    if (enable && dbgr->IsRecording()) {
        e.Enable(true);
        e.Check(dbgr->IsReverseDebuggingEnabled());
    } else {
        e.Check(false);
        e.Enable(false);
    }
}

void clMainFrame::OnToggleReverseDebuggingRecording(wxCommandEvent& e)
{
    wxUnusedVar(e);
    // Currently only supported on GDB
    if (DebuggerMgr::Get().GetActiveDebugger()) {
        DebuggerMgr::Get().GetActiveDebugger()->EnableRecording(e.IsChecked());
    }
}

void clMainFrame::OnToggleReverseDebuggingRecordingUI(wxUpdateUIEvent& e)
{
    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    bool enable = clCxxWorkspaceST::Get()->IsOpen() && (dbgr && (dbgr->GetName() == "GNU gdb debugger") &&
                                                        dbgr->IsRunning() && ManagerST::Get()->DbgCanInteract());
    if (enable) {
        e.Enable(true);
        e.Check(DebuggerMgr::Get().GetActiveDebugger()->IsRecording());
    } else {
        e.Enable(false);
    }
}

void clMainFrame::OnCopyFilePathRelativeToWorkspace(wxCommandEvent& event)
{
    wxUnusedVar(event);
    IEditor* editor = GetIEditorFromEvent(GetMainBook(), event);
    CHECK_PTR_RET(editor);
    CHECK_COND_RET(clWorkspaceManager::Get().IsWorkspaceOpened());

    wxFileName fn(editor->GetFileName());
    fn.MakeRelativeTo(clWorkspaceManager::Get().GetWorkspace()->GetDir());

    ::CopyToClipboard(fn.GetFullPath());
}

void clMainFrame::OnCopyFilePathRelativeToWorkspaceUI(wxUpdateUIEvent& event)
{
    event.Enable(clWorkspaceManager::Get().IsWorkspaceOpened() && clGetManager()->GetActiveEditor());
}

void clMainFrame::InitializeLogo()
{
    wxIconBundle app_icons;
    if (PluginManager::Get()->GetStdIcons()->GetIconBundle("codelite-logo", &app_icons)) {
        SetIcons(app_icons);
    }
}

void clMainFrame::OnDuplicateTab(wxCommandEvent& event)
{
    // Create a new empty tab
    IEditor* currentFile = GetIEditorFromEvent(GetMainBook(), event);
    CHECK_PTR_RET(currentFile);

    IEditor* newEditor = clGetManager()->NewEditor();
    CHECK_PTR_RET(newEditor);

    newEditor->GetCtrl()->SetText(currentFile->GetCtrl()->GetText());

    // Open the 'Save As' dialog, with some sensible defaults
    if (!newEditor->SaveAs(currentFile->GetFileName().GetFullName(), currentFile->GetFileName().GetPath())) {
        // If the "Save As" failed for any reason, remove the current editor
        clGetManager()->CloseEditor(newEditor, false);
        // Set the editor back to the current editor
        GetMainBook()->GetFindBar()->SetEditor(currentFile->GetCtrl());
    }
}

void clMainFrame::DoShowCaptions(bool show)
{
    if (!show) {
        wxAuiPaneInfoArray& panes = m_mgr.GetAllPanes();
        for (size_t i = 0; i < panes.GetCount(); ++i) {
            if (panes.Item(i).IsOk() && !panes.Item(i).IsToolbar()) {
                panes.Item(i).CaptionVisible(false);
            }
        }
    } else {
        wxAuiPaneInfoArray& panes = m_mgr.GetAllPanes();
        for (size_t i = 0; i < panes.GetCount(); ++i) {
            // Editor is the center pane - don't add it a caption
            if (panes.Item(i).IsOk() && !panes.Item(i).IsToolbar() && panes.Item(i).name != "Editor") {
                panes.Item(i).CaptionVisible(true);
            }
        }
    }
}

void clMainFrame::OnToggleMinimalView(wxCommandEvent& event)
{
    // Hide the toolbars + captions
    // Hide the _native_ toolbar
    bool minimalView = clConfig::Get().Read("MinimalView", true);
    if (minimalView) {
        if (m_frameHelper->IsToolbarShown()) {
            // Hide the toolbar
            DoShowToolbars(false, false);
        }
        if (m_frameHelper->IsCaptionsVisible()) {
            DoShowCaptions(false);
        }
        DoShowMenuBar(false);
    } else {
        if (!m_frameHelper->IsToolbarShown()) {
            DoShowToolbars(true, false);
        }
        if (!m_frameHelper->IsCaptionsVisible()) {
            DoShowCaptions(true);
        }
        DoShowMenuBar(true);
    }

    // Update the various configurations
    clConfig::Get().Write(kConfigShowToolBar, !minimalView);
    clConfig::Get().Write("MinimalView", !minimalView); // for next time

    // Update the captions settings
    OptionsConfigPtr opts = EditorConfigST::Get()->GetOptions();
    opts->SetShowDockingWindowCaption(!minimalView);
    EditorConfigST::Get()->SetOptions(opts);

    m_mgr.Update();
    PostSizeEvent();
}

void clMainFrame::OnToggleMinimalViewUI(wxUpdateUIEvent& event)
{
    bool inMinimalView = clConfig::Get().Read("MinimalView", false);
    event.Check(!inMinimalView);
}

void clMainFrame::OnDebugStepInstUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    e.Enable(IsDebuggerRunning() && m_frameHelper->GetDebuggerFeatures() & clDebugEvent::kStepInst);
}

void clMainFrame::OnDebugJumpToCursorUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    e.Enable(IsDebuggerRunning() && m_frameHelper->GetDebuggerFeatures() & clDebugEvent::kJumpToCursor);
}

void clMainFrame::OnDebugRunToCursorUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    e.Enable(IsDebuggerRunning() && m_frameHelper->GetDebuggerFeatures() & clDebugEvent::kRunToCursor);
}

void clMainFrame::OnDebugInterruptUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    e.Enable(IsDebuggerRunning() && m_frameHelper->GetDebuggerFeatures() & clDebugEvent::kInterrupt);
}

void clMainFrame::OnDebugShowCursorUI(wxUpdateUIEvent& e)
{
    CHECK_SHUTDOWN();
    e.Enable(IsDebuggerRunning() && m_frameHelper->GetDebuggerFeatures() & clDebugEvent::kShowCursor);
}

void clMainFrame::OnDebugRunToCursor(wxCommandEvent& e)
{
    // Allow the plugins to handle this command first
    if (EventNotifier::Get()->ProcessEvent(e)) {
        return;
    }

    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    IEditor* editor = clGetManager()->GetActiveEditor();
    if (editor && dbgr && dbgr->IsRunning() && ManagerST::Get()->DbgCanInteract()) {
        clDebuggerBreakpoint bp;
        bp.Create(editor->GetRemotePathOrLocal(),
                  editor->GetCurrentLine() + 1,
                  ManagerST::Get()->GetBreakpointsMgr()->GetNextID());
        bp.bp_type = BP_type_tempbreak;
        dbgr->Break(bp);
        dbgr->Continue();
    }
}

void clMainFrame::OnDebugJumpToCursor(wxCommandEvent& e)
{
    // Allow the plugins to handle this command first
    if (EventNotifier::Get()->ProcessEvent(e)) {
        return;
    }

    IDebugger* dbgr = DebuggerMgr::Get().GetActiveDebugger();
    IEditor* editor = clGetManager()->GetActiveEditor();
    if (editor && dbgr && dbgr->IsRunning() && ManagerST::Get()->DbgCanInteract()) {
        dbgr->Jump(editor->GetRemotePathOrLocal(), editor->GetCurrentLine() + 1);
    }
}

CodeLiteApp* clMainFrame::GetTheApp()
{
    CodeLiteApp* app = dynamic_cast<CodeLiteApp*>(wxApp::GetInstance());
    wxASSERT(app);
    return app;
}

void clMainFrame::OnEnvironmentVariablesModified(clCommandEvent& e)
{
    e.Skip();
    SelectBestEnvSet();
    {
        if (clCxxWorkspaceST::Get()->IsOpen()) {
            // mark all the projects as dirty
            wxArrayString projects;
            clCxxWorkspaceST::Get()->GetProjectList(projects);
            for (size_t i = 0; i < projects.size(); i++) {
                ProjectPtr proj = clCxxWorkspaceST::Get()->GetProject(projects.Item(i));
                if (proj) {
                    proj->SetModified(true);
                }
            }
        }
    }
}

void clMainFrame::OnWordComplete(wxCommandEvent& event)
{
    wxUnusedVar(event);
    clEditor* editor = GetMainBook()->GetActiveEditor();
    CHECK_PTR_RET(editor);

    // Get the filter
    wxStyledTextCtrl* stc = editor->GetCtrl();
    int curPos = stc->GetCurrentPos();
    int start = stc->WordStartPosition(stc->GetCurrentPos(), true);
    if (curPos < start) {
        return;
    }

    clCodeCompletionEvent ccEvent(wxEVT_CC_WORD_COMPLETE);
    ccEvent.SetTriggerKind(LSP::CompletionItem::kTriggerUser);
    ccEvent.SetWord(stc->GetTextRange(start, curPos));
    ccEvent.SetFileName(editor->GetFileName().GetFullPath());
    EventNotifier::Get()->ProcessEvent(ccEvent);

    const wxCodeCompletionBoxEntry::Vec_t& entries = ccEvent.GetEntries();
    if (entries.empty()) {
        return;
    }

    wxCodeCompletionBoxManager::Get().ShowCompletionBox(
        editor->GetCtrl(),
        entries,
        wxCodeCompletionBox::kNoShowingEvent, // Don't fire the "wxEVT_CCBOX_SHOWING event
        wxNOT_FOUND);
}

void clMainFrame::OnGotoAnything(wxCommandEvent& e)
{
    wxUnusedVar(e);
    clGotoAnythingManager::Get().ShowDialog();
}

void clMainFrame::OnVersionCheckError(wxCommandEvent& e)
{
    clWARNING() << "New version check failed:" << e.GetString();
    wxDELETE(m_webUpdate);
}

namespace
{
void ShowNavDialog(Notebook* book)
{
    if (!EditorConfigST::Get()->GetOptions()->IsCtrlTabEnabled()) {
        return;
    }

    if (book->GetPageCount() == 0) {
        return;
    }

    NotebookNavigationDlg dlg(EventNotifier::Get()->TopFrame(), book);
    if (dlg.ShowModal() == wxID_OK && dlg.GetSelection() != wxNOT_FOUND) {
        book->SetSelection(dlg.GetSelection());
    }
}

void ShowNavDialog(clAuiBook* book)
{
    if (!EditorConfigST::Get()->GetOptions()->IsCtrlTabEnabled()) {
        return;
    }

    if (book->GetPageCount() == 0) {
        return;
    }

    NotebookNavigationDlg dlg(book->GetParent(), book);
    if (dlg.ShowModal() == wxID_OK && dlg.GetSelection() != wxNOT_FOUND) {
        book->SetSelection(dlg.GetSelection());
    }
}
} // namespace

void clMainFrame::OnMainBookNavigating(wxCommandEvent& e)
{
    wxUnusedVar(e);

    if (::IsChildOf(wxWindow::FindFocus(), GetMainBook()->GetNotebook())) {
        ShowNavDialog(GetMainBook()->GetNotebook());
    } else {

        // show the navigation dialog based on the current focus
        Notebook* book = ::FindNotebookParentOf(wxWindow::FindFocus());
        if (book) {
            ShowNavDialog(book);
        }
    }
}

void clMainFrame::OnMainBookMovePage(wxCommandEvent& e)
{
    GetMainBook()->MovePage(e.GetId() == XRCID("wxEVT_BOOK_MOVE_TAB_RIGHT"));
}

void clMainFrame::OnFindNext(wxCommandEvent& event)
{
    event.Skip();
    auto stc = dynamic_cast<wxStyledTextCtrl*>(wxWindow::FindFocus());
    CHECK_PTR_RET(stc);
    event.Skip(false);

    auto find_bar = GetMainBook()->GetFindBar();
    OptionsConfigPtr opts = EditorConfigST::Get()->GetOptions();
    if (opts->GetFindNextOrPreviousUseSelection()) {
        wxString selection =
            stc->GetSelectedText().IsEmpty() ? GetMainBook()->GetFindBar()->GetFindWhat() : stc->GetSelectedText();
        find_bar->SetFindWhat(selection);
    }
    find_bar->FindNext();
}

void clMainFrame::OnFindPrevious(wxCommandEvent& event)
{
    event.Skip();
    auto stc = dynamic_cast<wxStyledTextCtrl*>(wxWindow::FindFocus());
    CHECK_PTR_RET(stc);
    event.Skip(false);

    auto find_bar = GetMainBook()->GetFindBar();
    OptionsConfigPtr opts = EditorConfigST::Get()->GetOptions();
    if (opts->GetFindNextOrPreviousUseSelection()) {
        wxString selection =
            stc->GetSelectedText().IsEmpty() ? GetMainBook()->GetFindBar()->GetFindWhat() : stc->GetSelectedText();
        find_bar->SetFindWhat(selection);
    }
    find_bar->FindPrevious();
}


void clMainFrame::OnCustomiseToolbar(wxCommandEvent& event)
{
    clCustomiseToolBarDlg dlg(this, m_pluginsToolbar);
    if (dlg.ShowModal() != wxID_OK) {
        return;
    }
    m_pluginsToolbar->Realize();
    m_pluginsToolbar->Refresh();

    wxArrayString hiddenItems;
    const std::vector<clToolBarButtonBase*>& buttons = m_pluginsToolbar->GetButtons();
    for (size_t i = 0; i < buttons.size(); ++i) {
        if (buttons[i]->IsHidden() && !buttons[i]->IsSeparator()) {
            hiddenItems.Add(buttons[i]->GetLabel());
        }
    }
    clConfig::Get().Write("ToolBarHiddenItems", hiddenItems);
}

void clMainFrame::OnInfobarButton(wxCommandEvent& event)
{
    event.Skip(); // needed to make sure that the bar is hidden
    int buttonID = event.GetId();
    if (buttonID == XRCID("restart-codelite")) {
        clCommandEvent restartEvent{ wxEVT_FORCE_RESTART_CODELITE };
        ManagerST::Get()->OnForcedRestart(restartEvent);
    } else {
        clCommandEvent buttonEvent(wxEVT_INFO_BAR_BUTTON);
        buttonEvent.SetInt(buttonID);
        buttonEvent.SetEventObject(m_infoBar);
        EventNotifier::Get()->AddPendingEvent(buttonEvent);
    }
}

void clMainFrame::OnShowMenuBar(wxCommandEvent& event)
{
    wxUnusedVar(event);
#ifndef __WXMAC__
    bool currentState = clConfig::Get().Read(kConfigShowMenuBar, true);
    currentState = !currentState;
    clConfig::Get().Write(kConfigShowMenuBar, currentState);
#ifdef __WXMSW__
    DoSuggestRestart();
#else
    // No need for restart on wxGTK, just show it and post a size event
    m_mainMenuBar->Show(currentState);
    PostSizeEvent();
#endif
#endif
}

void clMainFrame::OnShowMenuBarUI(wxUpdateUIEvent& event)
{
#if defined(__WXGTK__) || defined(__WXMSW__)
    event.Check(GetMainMenuBar()->IsShown());
#else
    event.Check(true);
    event.Enable(false);
#endif
}

void clMainFrame::Raise() { wxFrame::Raise(); }

void clMainFrame::OnReportIssue(wxCommandEvent& event)
{
    wxUnusedVar(event);
    ::wxLaunchDefaultBrowser("https://github.com/eranif/codelite/issues");
}

void clMainFrame::ShowBuildMenu(clToolBar* toolbar, wxWindowID buttonID)
{
    CHECK_PTR_RET(toolbar);
    wxMenu menu;

    // let the plugins build a different menu
    clContextMenuEvent evt(wxEVT_BUILD_CUSTOM_TARGETS_MENU_SHOWING);
    evt.SetEventObject(toolbar);
    evt.SetMenu(&menu);
    if (!EventNotifier::Get()->ProcessEvent(evt)) {
        DoCreateBuildDropDownMenu(&menu);
    }

    // show the menu
    toolbar->ShowMenuForButton(buttonID, &menu);
}

void clMainFrame::DoShowMenuBar(bool show) { wxUnusedVar(show); }

void clMainFrame::OnSysColoursChanged(clCommandEvent& event)
{
    event.Skip();
    clBitmaps::Get().SysColoursChanged(); // Notify the bitmap manager that system colour has changed
    DoSysColoursChanged();                // scrollbars etc (MSW only)

#if !wxUSE_NATIVE_CAPTION
    clColours colours;
    colours.InitFromColour(clSystemSettings::GetDefaultPanelColour());
    colours.SetBgColour(clSystemSettings::GetDefaultPanelColour());
    colours.SetItemTextColour(clSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
    // update the colours used by the caption
    m_captionBar->SetColours(colours);
    // update the bitmap as well
    m_captionBar->ShowActionButton(clGetManager()->GetStdIcons()->LoadBitmap("menu-lines"));
#endif

#ifndef __WXMAC__
    m_mainPanel->SetBackgroundColour(clSystemSettings::GetDefaultPanelColour());
    m_debuggerPane->SetBackgroundColour(clSystemSettings::GetDefaultPanelColour());
    m_outputPane->SetBackgroundColour(clSystemSettings::GetDefaultPanelColour());
    m_sidebar->SetBackgroundColour(clSystemSettings::GetDefaultPanelColour());
#endif
}

void clMainFrame::DoSysColoursChanged() { MSWSetWindowDarkTheme(this); }

void clMainFrame::OnSetActivePoject(wxCommandEvent& e)
{
    auto workspace = clWorkspaceManager::Get().GetWorkspace();
    CHECK_PTR_RET(workspace);

    auto cur_active_project = workspace->GetActiveProjectName();
    auto projects = workspace->GetWorkspaceProjects();

    CHECK_COND_RET(!projects.empty());

    // sort the entries
    projects.Sort(+[](const wxString& first, const wxString& second) { return first.CmpNoCase(second) < 0; });

    int initialSelection = projects.Index(cur_active_project);
    clSingleChoiceDialog dlg(this, projects, initialSelection == wxNOT_FOUND ? 0 : initialSelection);
    dlg.SetLabel(_("Select Project"));
    if (dlg.ShowModal() != wxID_OK) {
        return;
    }
    wxString new_selection = dlg.GetSelection();
    workspace->SetProjectActive(new_selection);
}

void clMainFrame::OnSetActivePojectUI(wxUpdateUIEvent& e)
{
    bool enable =
        clWorkspaceManager::Get().IsWorkspaceOpened() && clWorkspaceManager::Get().GetWorkspace()->IsProjectSupported();
    e.Enable(enable);
}

void clMainFrame::UpdateMainToolbarOrientation(int newOrientation)
{
    // check if we already have this style in the tool bar style
    bool orientation_already_exists = (m_mainToolbarStyle & newOrientation);

    m_mainToolbarStyle &= ~TB_POS_ALL;
    m_mainToolbarStyle |= newOrientation;
    if (!orientation_already_exists) {
        DoSuggestRestart();
    }

    // store the new style
    clConfig::Get().Write("MainToolBarStyle", m_mainToolbarStyle);
}

void clMainFrame::OnMainToolBarPlaceTop(wxCommandEvent& event)
{
    wxUnusedVar(event);
    UpdateMainToolbarOrientation(wxTB_TOP);
}

void clMainFrame::OnMainToolBarHide(wxCommandEvent& event)
{
    wxUnusedVar(event);
    m_mainToolbarStyle &= ~TB_POS_ALL;
    // store the new style
    clConfig::Get().Write("MainToolBarStyle", m_mainToolbarStyle);

    if (m_mainToolbar != nullptr) {
        // tb is shown, suggest restart
        DoSuggestRestart();
    }
}

void clMainFrame::OnMainToolBarPlaceBottom(wxCommandEvent& event)
{
    wxUnusedVar(event);
    UpdateMainToolbarOrientation(wxTB_BOTTOM);
}

void clMainFrame::OnMainToolBarPlaceLeft(wxCommandEvent& event)
{
    wxUnusedVar(event);
    UpdateMainToolbarOrientation(wxTB_LEFT);
}

void clMainFrame::OnMainToolBarPlaceRight(wxCommandEvent& event)
{
    wxUnusedVar(event);
    UpdateMainToolbarOrientation(wxTB_RIGHT);
}

void clMainFrame::OnMainToolBarHideUI(wxUpdateUIEvent& event) { event.Check((m_mainToolbarStyle & TB_POS_ALL) == 0); }
void clMainFrame::OnMainToolBarPlaceTopUI(wxUpdateUIEvent& event) { event.Check(m_mainToolbarStyle & wxTB_TOP); }
void clMainFrame::OnMainToolBarPlaceBottomUI(wxUpdateUIEvent& event) { event.Check(m_mainToolbarStyle & wxTB_BOTTOM); }
void clMainFrame::OnMainToolBarPlaceLeftUI(wxUpdateUIEvent& event) { event.Check(m_mainToolbarStyle & wxTB_LEFT); }
void clMainFrame::OnMainToolBarPlaceRightUI(wxUpdateUIEvent& event) { event.Check(m_mainToolbarStyle & wxTB_RIGHT); }
