66#include " Platform/Platform.hpp"
77#include " bitmap_loader.h"
88#include " clFileName.hpp"
9+ #include " clINIParser.hpp"
910#include " clWorkspaceManager.h"
1011#include " codelite_events.h"
1112#include " environmentconfig.h"
1213#include " event_notifier.h"
14+ #include " file_logger.h"
1315#include " globals.h"
1416#include " imanager.h"
1517#include " macros.h"
1921
2022#include < wx/app.h>
2123#include < wx/choicdlg.h>
24+ #include < wx/dir.h>
2225#include < wx/frame.h>
2326#include < wx/sizer.h>
2427#include < wx/xrc/xmlres.h>
@@ -29,12 +32,18 @@ std::map<wxString, wxString> LocateDefaultTerminals()
2932{
3033 std::map<wxString, wxString> terminals;
3134 auto bash = ThePlatform->Which (" bash" );
35+ auto zsh = ThePlatform->Which (" zsh" );
3236 auto cmd = ThePlatform->Which (" powershell" );
3337 if (bash.has_value ()) {
3438 terminals.insert (
3539 {wxString::Format (" %s --login -i" , bash.value ()), wxString::Format (" %s --login -i" , bash.value ())});
3640 }
3741
42+ if (zsh.has_value ()) {
43+ terminals.insert (
44+ {wxString::Format (" %s --login -i" , zsh.value ()), wxString::Format (" %s --login -i" , zsh.value ())});
45+ }
46+
3847 if (cmd.has_value ()) {
3948 terminals.insert ({cmd.value (), cmd.value ()});
4049 }
@@ -62,9 +71,17 @@ clBuiltinTerminalPane::clBuiltinTerminalPane(wxWindow* parent, wxWindowID id)
6271
6372 // Get list of terminals
6473 m_terminal_types = new wxChoice (m_toolbar, wxID_ANY, wxDefaultPosition, wxSize (FromDIP (300 ), wxNOT_FOUND));
74+ m_terminal_types->SetToolTip (_ (" Choose shell interpreter" ));
6575 UpdateTerminalsChoice (false );
6676 m_toolbar->AddControl (m_terminal_types);
6777
78+ // Get list of terminals
79+ m_choice_themes = new wxChoice (m_toolbar, wxID_ANY, wxDefaultPosition, wxSize (FromDIP (200 ), wxNOT_FOUND));
80+ m_toolbar->AddControl (m_choice_themes);
81+ m_choice_themes->SetToolTip (_ (" Choose terminal theme" ));
82+ m_choice_themes->Bind (wxEVT_CHOICE, &clBuiltinTerminalPane::OnChoiceTheme, this );
83+ UpdateFont ();
84+
6885#ifdef __WXMSW__
6986 m_toolbar->AddTool (
7087 wxID_REFRESH, _ (" Scan" ), image_list->LoadBitmap (" debugger_restart" ), wxEmptyString, wxITEM_NORMAL);
@@ -78,12 +95,20 @@ clBuiltinTerminalPane::clBuiltinTerminalPane(wxWindow* parent, wxWindowID id)
7895 GetSizer ()->Fit (this );
7996 m_book->Bind (wxEVT_BOOK_PAGE_CHANGED, &clBuiltinTerminalPane::OnPageChanged, this );
8097 EventNotifier::Get ()->Bind (wxEVT_WORKSPACE_LOADED, &clBuiltinTerminalPane::OnWorkspaceLoaded, this );
98+ EventNotifier::Get ()->Bind (wxEVT_SYS_COLOURS_CHANGED, &clBuiltinTerminalPane::OnThemeChanged, this );
99+ EventNotifier::Get ()->Bind (wxEVT_INIT_DONE, &clBuiltinTerminalPane::OnInitDone, this );
100+
101+ #ifdef __WXMAC__
102+ wxTheApp->Bind (wxEVT_MENU, &clBuiltinTerminalPane::OnCopy, this , wxID_COPY);
103+ wxTheApp->Bind (wxEVT_MENU, &clBuiltinTerminalPane::OnPaste, this , wxID_PASTE);
104+ #endif
81105}
82106
83107clBuiltinTerminalPane::~clBuiltinTerminalPane ()
84108{
85109 m_book->Unbind (wxEVT_BOOK_PAGE_CHANGED, &clBuiltinTerminalPane::OnPageChanged, this );
86110 EventNotifier::Get ()->Unbind (wxEVT_WORKSPACE_LOADED, &clBuiltinTerminalPane::OnWorkspaceLoaded, this );
111+ EventNotifier::Get ()->Unbind (wxEVT_SYS_COLOURS_CHANGED, &clBuiltinTerminalPane::OnThemeChanged, this );
87112 clConfig::Get ().Write (" terminal/last_used_terminal" , m_terminal_types->GetStringSelection ());
88113}
89114
@@ -117,6 +142,7 @@ void clBuiltinTerminalPane::OnNew(wxCommandEvent& event)
117142 EnvSetter env_setter{};
118143 std::optional<TerminalView::EnvironmentList> env{std::nullopt };
119144 TerminalView* ctrl = new TerminalView (m_book, cmd, env);
145+ ctrl->SetTheme (m_activeTheme.has_value () ? *m_activeTheme : wxTerminalTheme::MakeDarkTheme ());
120146 m_book->AddPage (ctrl, cmd, true );
121147 m_book->SetPageToolTip (m_book->GetPageCount () - 1 , cmd);
122148
@@ -148,6 +174,11 @@ void clBuiltinTerminalPane::OnNew(wxCommandEvent& event)
148174 V.push_back (wxAcceleratorEntry{wxACCEL_RAW_CTRL, (int )' A' , XRCID (" Ctrl_ID_start_of_line" )});
149175 V.push_back (wxAcceleratorEntry{wxACCEL_RAW_CTRL, (int )' E' , XRCID (" Ctrl_ID_end_of_line" )});
150176
177+ #ifdef __WXMAC__
178+ V.push_back (wxAcceleratorEntry{wxACCEL_CMD, (int )' V' , wxID_PASTE});
179+ V.push_back (wxAcceleratorEntry{wxACCEL_CMD, (int )' C' , wxID_COPY});
180+ #endif
181+
151182 wxAcceleratorTable accel_table (V.size (), V.data ());
152183 ctrl->SetAcceleratorTable (accel_table);
153184
@@ -162,6 +193,7 @@ void clBuiltinTerminalPane::OnNew(wxCommandEvent& event)
162193 ctrl->Bind (wxEVT_MENU, &clBuiltinTerminalPane::OnAltF, this , XRCID (" Alt_ID_forward" ));
163194 ctrl->Bind (wxEVT_MENU, &clBuiltinTerminalPane::OnCtrlA, this , XRCID (" Ctrl_ID_start_of_line" ));
164195 ctrl->Bind (wxEVT_MENU, &clBuiltinTerminalPane::OnCtrlE, this , XRCID (" Ctrl_ID_end_of_line" ));
196+ ctrl->Bind (wxEVT_MENU, &clBuiltinTerminalPane::OnCtrlE, this , XRCID (" Ctrl_ID_end_of_line" ));
165197}
166198
167199void clBuiltinTerminalPane::OnSetTitle (wxTerminalEvent& event)
@@ -238,7 +270,6 @@ void clBuiltinTerminalPane::WriteTerminalOptionsToDisk(const std::map<wxString,
238270std::map<wxString, wxString> clBuiltinTerminalPane::GetTerminalsOptions (bool scan)
239271{
240272 std::map<wxString, wxString> terminals = LocateDefaultTerminals ();
241- ;
242273#ifdef __WXMSW__
243274 if (scan) {
244275 terminals.clear ();
@@ -353,6 +384,179 @@ void clBuiltinTerminalPane::OnCtrlE(wxCommandEvent& e)
353384 CHECK_IF_CAN_HANDLE (e);
354385 terminal->SendCtrlE ();
355386}
356- #undef CHECK_IF_CAN_HANDLE
357387
358- void clBuiltinTerminalPane::OnInitDone (wxCommandEvent& e) { e.Skip (); }
388+ void clBuiltinTerminalPane::OnInitDone (wxCommandEvent& e)
389+ {
390+ e.Skip ();
391+ std::thread ([this ]() {
392+ wxFileName themes_path{clStandardPaths::Get ().GetDataDir (), wxEmptyString};
393+ themes_path.AppendDir (" terminal_themes" );
394+ themes_path.AppendDir (" themes" );
395+ wxArrayString theme_files;
396+ wxDir::GetAllFiles (themes_path.GetPath (), &theme_files, " *.toml" , wxDIR_FILES);
397+
398+ std::map<wxString, wxTerminalTheme> themes;
399+ for (const wxString& toml_file : theme_files) {
400+ wxFileName fn{toml_file};
401+ auto theme = clBuiltinTerminalPane::FromTOML (fn);
402+ if (theme.has_value ()) {
403+ themes.insert ({fn.GetName (), *theme});
404+ }
405+ }
406+
407+ {
408+ wxMutexLocker locker (m_themes_mutex);
409+ m_themes.swap (themes);
410+ }
411+ EventNotifier::Get ()->RunOnMain <void >([this ]() { ThemesUpdated (); });
412+ }).detach ();
413+ }
414+
415+ #ifdef __WXMAC__
416+ void clBuiltinTerminalPane::OnCopy (wxCommandEvent& e)
417+ {
418+ CHECK_IF_CAN_HANDLE (e);
419+ terminal->Copy ();
420+ }
421+
422+ void clBuiltinTerminalPane::OnPaste (wxCommandEvent& e)
423+ {
424+ CHECK_IF_CAN_HANDLE (e);
425+ terminal->Paste ();
426+ }
427+
428+ #endif
429+
430+ void clBuiltinTerminalPane::OnThemeChanged (clCommandEvent& event)
431+ {
432+ event.Skip ();
433+ UpdateFont ();
434+ ApplyThemeChanges ();
435+ }
436+
437+ std::optional<wxTerminalTheme> clBuiltinTerminalPane::FromTOML (const wxFileName& filepath)
438+ {
439+ clDEBUG () << " > Importing Alacritty Theme (TOML) file:" << filepath << endl;
440+ std::string filename = StringUtils::ToStdString (filepath.GetFullPath ());
441+
442+ clINIParser ini_parser;
443+ ini_parser.ParseFile (filepath.GetFullPath ());
444+
445+ wxTerminalTheme theme;
446+ theme.bg = ini_parser[" colors.primary" ][" background" ].GetValue ();
447+ theme.fg = ini_parser[" colors.primary" ][" foreground" ].GetValue ();
448+
449+ if (!theme.bg .IsOk () || !theme.fg .IsOk ()) {
450+ clSYSTEM () << " Can not import theme:" << filepath << endl;
451+ return std::nullopt ;
452+ }
453+
454+ bool is_dark = DrawingUtils::IsDark (theme.bg );
455+
456+ wxString section_name = " colors.normal" ;
457+ theme.black = ini_parser[section_name][" black" ].GetValue ();
458+ theme.red = ini_parser[section_name][" red" ].GetValue ();
459+ theme.green = ini_parser[section_name][" green" ].GetValue ();
460+ theme.yellow = ini_parser[section_name][" yellow" ].GetValue ();
461+ theme.blue = ini_parser[section_name][" blue" ].GetValue ();
462+ theme.magenta = ini_parser[section_name][" magenta" ].GetValue ();
463+ theme.cyan = ini_parser[section_name][" cyan" ].GetValue ();
464+ theme.white = ini_parser[section_name][" white" ].GetValue ();
465+
466+ section_name = " colors.bright" ;
467+ theme.brightBlack = ini_parser[section_name][" black" ].GetValue ();
468+ theme.brightRed = ini_parser[section_name][" red" ].GetValue ();
469+ theme.brightGreen = ini_parser[section_name][" green" ].GetValue ();
470+ theme.brightYellow = ini_parser[section_name][" yellow" ].GetValue ();
471+ theme.brightBlue = ini_parser[section_name][" blue" ].GetValue ();
472+ theme.brightMagenta = ini_parser[section_name][" magenta" ].GetValue ();
473+ theme.brightCyan = ini_parser[section_name][" cyan" ].GetValue ();
474+ theme.brightWhite = ini_parser[section_name][" white" ].GetValue ();
475+
476+ // Optional colours
477+ theme.cursorColour = ini_parser[" colors.cursor" ][" cursor" ].GetValue ();
478+ theme.selectionBg = ini_parser[" colors.selection" ][" background" ].GetValue ();
479+ theme.highlightBg = ini_parser[" colors.search.matches" ][" background" ].GetValue ();
480+
481+ if (!theme.cursorColour .IsOk ()) {
482+ theme.cursorColour = is_dark ? *wxYELLOW : *wxBLACK;
483+ }
484+
485+ if (!theme.selectionBg .IsOk ()) {
486+ theme.selectionBg = is_dark ? wxT (" ORANGE" ) : wxT (" BLUE" );
487+ }
488+
489+ if (!theme.highlightBg .IsOk ()) {
490+ theme.selectionBg = is_dark ? wxT (" GOLD" ) : wxT (" PINK" );
491+ }
492+
493+ if (!theme.black .IsOk () || !theme.red .IsOk () || !theme.green .IsOk () || !theme.yellow .IsOk () || !theme.blue .IsOk () ||
494+ !theme.magenta .IsOk () || !theme.cyan .IsOk () || !theme.white .IsOk () || !theme.brightBlack .IsOk () ||
495+ !theme.brightRed .IsOk () || !theme.brightGreen .IsOk () || !theme.brightYellow .IsOk () ||
496+ !theme.brightBlue .IsOk () || !theme.brightMagenta .IsOk () || !theme.brightCyan .IsOk () ||
497+ !theme.brightWhite .IsOk ()) {
498+ clSYSTEM () << " failed to read basic colour for theme:" << filepath << endl;
499+ return std::nullopt ;
500+ }
501+ return theme;
502+ }
503+
504+ void clBuiltinTerminalPane::ThemesUpdated ()
505+ {
506+ m_choice_themes->Clear ();
507+ if (m_themes.empty ()) {
508+ return ;
509+ }
510+ for (const auto & [theme_name, _] : m_themes) {
511+ m_choice_themes->Append (theme_name);
512+ }
513+
514+ wxString selected_theme = m_themes.begin ()->first ;
515+ selected_theme = clConfig::Get ().Read (" terminal/theme" , selected_theme);
516+ m_choice_themes->SetStringSelection (selected_theme);
517+
518+ if (m_themes.contains (selected_theme)) {
519+ m_activeTheme = m_themes[selected_theme];
520+ } else {
521+ m_activeTheme = wxTerminalTheme::MakeDarkTheme ();
522+ }
523+ ApplyThemeChanges ();
524+ }
525+
526+ void clBuiltinTerminalPane::OnChoiceTheme (wxCommandEvent& event)
527+ {
528+ wxUnusedVar (event);
529+ wxString selected_theme = m_choice_themes->GetStringSelection ();
530+ if (m_themes.contains (selected_theme)) {
531+ m_activeTheme = m_themes[selected_theme];
532+ clConfig::Get ().Write (" terminal/theme" , selected_theme);
533+ clConfig::Get ().Save ();
534+ } else {
535+ m_activeTheme = wxTerminalTheme::MakeDarkTheme ();
536+ }
537+ ApplyThemeChanges ();
538+ }
539+
540+ void clBuiltinTerminalPane::ApplyThemeChanges ()
541+ {
542+ if (!m_activeTheme.has_value ()) {
543+ return ;
544+ }
545+
546+ m_activeTheme.value ().font = m_activeFont;
547+
548+ for (size_t i = 0 ; i < m_book->GetPageCount (); ++i) {
549+ auto terminal = dynamic_cast <TerminalView*>(m_book->GetPage (i));
550+ if (terminal) {
551+ terminal->SetTheme (m_activeTheme.value ());
552+ }
553+ }
554+ }
555+
556+ void clBuiltinTerminalPane::UpdateFont ()
557+ {
558+ auto lexer = ColoursAndFontsManager::Get ().GetLexer (" text" );
559+ if (lexer) {
560+ m_activeFont = lexer->GetFontForStyle (0 , this );
561+ }
562+ }
0 commit comments