diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index 0a8148de2..047832f9c 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -435,6 +435,7 @@ class EE_API UICodeEditorSplitter { Float mVisualSplitEdgePercent{ 0.1 }; TabTryCloseCallback mTabTryCloseCb; std::function mCanCreateSplitFn; + std::unordered_map> mEventCbs; UICodeEditorSplitter( UICodeEditorSplitter::Client* client, UISceneNode* sceneNode, std::shared_ptr threadPool, diff --git a/src/eepp/ui/iconmanager.cpp b/src/eepp/ui/iconmanager.cpp index 4ff387476..94e8ce8f7 100644 --- a/src/eepp/ui/iconmanager.cpp +++ b/src/eepp/ui/iconmanager.cpp @@ -119,6 +119,7 @@ UIIconTheme* IconManager::init( const std::string& iconThemeName, FontTrueType* { "input-field", 0xF47A }, { "sun", 0xF1BC }, { "moon", 0xEF72 }, + { "calendar-2", 0xEB21 }, } ) { iconTheme->add( UIGlyphIcon::New( icon.first, remixIconFont, icon.second ) ); } diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index 1c36a7cd2..cdd1a79aa 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -110,7 +110,42 @@ UICodeEditorSplitter::UICodeEditorSplitter( UICodeEditorSplitter::Client* client } } -UICodeEditorSplitter::~UICodeEditorSplitter() {} +UICodeEditorSplitter::~UICodeEditorSplitter() { + std::vector tabWidgets; + { + Lock l( mTabWidgetMutex ); + tabWidgets = mTabWidgets; + mTabWidgets.clear(); + } + + for ( UITabWidget* tabWidget : tabWidgets ) { + if ( nullptr == tabWidget ) + continue; + + tabWidget->setTabTryCloseCallback( nullptr ); + tabWidget->setSplitFunction( nullptr, mVisualSplitEdgePercent ); + auto tabWidgetCbsIt = mEventCbs.find( tabWidget ); + if ( tabWidgetCbsIt != mEventCbs.end() ) + tabWidget->removeEventListener( tabWidgetCbsIt->second ); + + for ( size_t i = 0; i < tabWidget->getTabCount(); ++i ) { + UITab* tab = tabWidget->getTab( i ); + if ( nullptr == tab || nullptr == tab->getOwnedWidget() || + !tab->getOwnedWidget()->isWidget() ) + continue; + + UIWidget* widget = tab->getOwnedWidget()->asType(); + auto widgetCbsIt = mEventCbs.find( widget ); + if ( widgetCbsIt != mEventCbs.end() ) + widget->removeEventListener( widgetCbsIt->second ); + } + } + + mEventCbs.clear(); + mCurEditor = nullptr; + mCurWidget = nullptr; + mClient = nullptr; +} UITabWidget* UICodeEditorSplitter::tabWidgetFromEditor( UICodeEditor* editor ) const { if ( editor && editor->getData() != 0 ) @@ -256,7 +291,7 @@ UICodeEditor* UICodeEditorSplitter::createCodeEditor() { registerSplitterCommands( doc ); /* Splitter commands */ - editor->on( Event::OnFocus, [this]( const Event* event ) { + mEventCbs[editor].push_back( editor->on( Event::OnFocus, [this]( const Event* event ) { UICodeEditor* editor = event->getNode()->asType(); UICodeEditor* prevEditor = mCurEditor; if ( mRestoreEditorSelectionOnFocus && prevEditor && prevEditor != editor && @@ -265,29 +300,32 @@ UICodeEditor* UICodeEditorSplitter::createCodeEditor() { setCurrentWidget( editor ); if ( mRestoreEditorSelectionOnFocus && prevEditor && prevEditor != editor ) restoreEditorSelection( editor ); - } ); - editor->on( Event::OnFocusLoss, [this]( const Event* event ) { + } ) ); + mEventCbs[editor].push_back( editor->on( Event::OnFocusLoss, [this]( const Event* event ) { if ( mRestoreEditorSelectionOnFocus ) saveEditorSelection( event->getNode()->asType() ); - } ); - editor->on( Event::OnTextChanged, [this]( const Event* event ) { + } ) ); + mEventCbs[editor].push_back( editor->on( Event::OnTextChanged, [this]( const Event* event ) { mClient->onDocumentModified( event->getNode()->asType(), event->getNode()->asType()->getDocument() ); - } ); - editor->on( Event::OnSelectionChanged, [this]( const Event* event ) { - mClient->onDocumentSelectionChange( - event->getNode()->asType(), - event->getNode()->asType()->getDocument() ); - } ); - editor->on( Event::OnCursorPosChange, [this]( const Event* event ) { - mClient->onDocumentCursorPosChange( - event->getNode()->asType(), - event->getNode()->asType()->getDocument() ); - } ); - editor->on( Event::OnDocumentUndoRedo, [this]( const Event* event ) { - mClient->onDocumentUndoRedo( event->getNode()->asType(), - event->getNode()->asType()->getDocument() ); - } ); + } ) ); + mEventCbs[editor].push_back( + editor->on( Event::OnSelectionChanged, [this]( const Event* event ) { + mClient->onDocumentSelectionChange( + event->getNode()->asType(), + event->getNode()->asType()->getDocument() ); + } ) ); + mEventCbs[editor].push_back( + editor->on( Event::OnCursorPosChange, [this]( const Event* event ) { + mClient->onDocumentCursorPosChange( + event->getNode()->asType(), + event->getNode()->asType()->getDocument() ); + } ) ); + mEventCbs[editor].push_back( + editor->on( Event::OnDocumentUndoRedo, [this]( const Event* event ) { + mClient->onDocumentUndoRedo( event->getNode()->asType(), + event->getNode()->asType()->getDocument() ); + } ) ); editor->addKeyBinds( getLocalDefaultKeybindings() ); editor->addUnlockedCommands( getUnlockedCommands() ); @@ -569,11 +607,12 @@ UICodeEditorSplitter::createCodeEditorInTabWidget( UITabWidget* tabWidget ) { return std::make_pair( (UITab*)nullptr, (UICodeEditor*)nullptr ); UICodeEditor* editor = createCodeEditor(); mAboutToAddEditor = editor; - editor->on( Event::OnDocumentChanged, [this]( const Event* event ) { - UICodeEditor* editor = event->getNode()->asType(); - mEditorSelections.erase( editor ); - mClient->onDocumentStateChanged( editor, editor->getDocument() ); - } ); + mEventCbs[editor].push_back( + editor->on( Event::OnDocumentChanged, [this]( const Event* event ) { + UICodeEditor* editor = event->getNode()->asType(); + mEditorSelections.erase( editor ); + mClient->onDocumentStateChanged( editor, editor->getDocument() ); + } ) ); UITab* tab = tabWidget->add( editor->getDocument().getFilename(), editor ); editor->setData( (UintPtr)tab ); DocEvent docEvent( editor, &editor->getDocument(), Event::OnEditorTabReady ); @@ -616,13 +655,13 @@ UICodeEditorSplitter::createWidgetInTabWidget( UITabWidget* tabWidget, UIWidget* widget->setData( (UintPtr)tab ); // We use both events because there was an strange behavior that sometimes OnFocusWithin was not // enough, so this is just in case. - widget->on( Event::OnFocus, [this]( const Event* event ) { + mEventCbs[widget].push_back( widget->on( Event::OnFocus, [this]( const Event* event ) { setCurrentWidget( event->getNode()->asType() ); - } ); - widget->on( Event::OnFocusWithin, [this]( const Event* event ) { + } ) ); + mEventCbs[widget].push_back( widget->on( Event::OnFocusWithin, [this]( const Event* event ) { setCurrentWidget( event->getNode()->asType() ); - } ); - widget->on( Event::OnTitleChange, [this]( const Event* event ) { + } ) ); + mEventCbs[widget].push_back( widget->on( Event::OnTitleChange, [this]( const Event* event ) { const TextEvent* tevent = static_cast( event ); UIWidget* widget = event->getNode()->asType(); UITabWidget* tabWidget = tabWidgetFromWidget( widget ); @@ -630,7 +669,7 @@ UICodeEditorSplitter::createWidgetInTabWidget( UITabWidget* tabWidget, UIWidget* if ( !tab ) return; tab->setText( tevent->getText() ); - } ); + } ) ); if ( focus ) tabWidget->setTabSelected( tab ); mClient->onTabCreated( tab, widget ); @@ -713,7 +752,8 @@ UITabWidget* UICodeEditorSplitter::createTabWidget( Node* parent ) { }, mVisualSplitEdgePercent ); } - tabWidget->on( Event::OnTabSelected, [this]( const Event* event ) { + mEventCbs[tabWidget].push_back( tabWidget->on( Event::OnTabSelected, [this]( + const Event* event ) { UITabWidget* tabWidget = event->getNode()->asType(); eeASSERT( nullptr != tabWidget && nullptr != tabWidget->getTabSelected() && nullptr != tabWidget->getTabSelected()->getOwnedWidget() ); @@ -727,7 +767,7 @@ UITabWidget* UICodeEditorSplitter::createTabWidget( Node* parent ) { } else { setCurrentWidget( tabWidget->getTabSelected()->getOwnedWidget()->asType() ); } - } ); + } ) ); tabWidget->setTabTryCloseCallback( [this]( UITab* tab, UITabWidget::FocusTabBehavior focusTabBehavior ) -> bool { if ( tab->getOwnedWidget() && @@ -737,9 +777,10 @@ UITabWidget* UICodeEditorSplitter::createTabWidget( Node* parent ) { } return false; } ); - tabWidget->on( Event::OnTabClosed, [this]( const Event* event ) { - onTabClosed( static_cast( event ) ); - } ); + mEventCbs[tabWidget].push_back( + tabWidget->on( Event::OnTabClosed, [this]( const Event* event ) { + onTabClosed( static_cast( event ) ); + } ) ); if ( mOnTabWidgetCreateCb ) mOnTabWidgetCreateCb( tabWidget ); Lock l( mTabWidgetMutex ); @@ -1842,12 +1883,22 @@ void UICodeEditorSplitter::closeSplitter( UISplitter* splitter ) { void UICodeEditorSplitter::onTabClosed( const TabEvent* tabEvent ) { UIWidget* widget = tabEvent->getTab()->getOwnedWidget()->asType(); UITabWidget* tabWidget = tabEvent->getTab()->getTabWidget(); + auto widgetCbsIt = mEventCbs.find( widget ); + if ( widgetCbsIt != mEventCbs.end() ) { + widget->removeEventListener( widgetCbsIt->second ); + mEventCbs.erase( widgetCbsIt ); + } if ( widget && widget->isType( UI_TYPE_CODEEDITOR ) ) mEditorSelections.erase( widget->asType() ); if ( tabWidget->getTabCount() == 0 ) { UISplitter* splitter = splitterFromWidget( widget ); if ( splitter ) { if ( splitter->isFull() ) { + auto tabWidgetCbsIt = mEventCbs.find( tabWidget ); + if ( tabWidgetCbsIt != mEventCbs.end() ) { + tabWidget->removeEventListener( tabWidgetCbsIt->second ); + mEventCbs.erase( tabWidgetCbsIt ); + } tabWidget->close(); auto itWidget = std::find( mTabWidgets.begin(), mTabWidgets.end(), tabWidget ); if ( itWidget != mTabWidgets.end() ) { diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index 4669a6024..a0fb7d418 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -177,6 +177,7 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, TextFormat::stringToLineEnding( ini.getValue( "document", "line_endings", "LF" ) ); editor.newTabPosition = NewTabPosition::fromString( ini.getValue( "editor", "new_tab_position", "last" ) ); + editor.customDateFormat = ini.getValue( "editor", "custom_date_format", "%d.%m.%Y %H:%M:%S" ); // Migrate old data if ( ini.keyValueExists( "document", "windows_line_endings" ) && !ini.keyValueExists( "document", "line_endings" ) && @@ -395,6 +396,7 @@ void AppConfig::save( const std::vector& recentFiles, ini.setValue( "editor", "tab_jump_mode", UITabWidget::tabJumpModeToString( editor.tabJumpMode ) ); ini.setValue( "editor", "new_tab_position", NewTabPosition::toString( editor.newTabPosition ) ); + ini.setValue( "editor", "custom_date_format", editor.customDateFormat ); ini.setValueB( "editor", "single_click_tree_navigation", editor.singleClickNavigation ); ini.setValueB( "editor", "sync_project_tree_with_editor", editor.syncProjectTreeWithEditor ); ini.setValueB( "editor", "auto_close_xml_tags", editor.autoCloseXMLTags ); diff --git a/src/tools/ecode/appconfig.hpp b/src/tools/ecode/appconfig.hpp index 77ac3a468..ef2786ace 100644 --- a/src/tools/ecode/appconfig.hpp +++ b/src/tools/ecode/appconfig.hpp @@ -121,6 +121,7 @@ struct CodeEditorConfig { bool restoreEditorSelectionOnFocus{ true }; UITabWidget::TabJumpMode tabJumpMode{ UITabWidget::TabJumpMode::Linear }; NewTabPosition::Position newTabPosition{ NewTabPosition::Last }; + std::string customDateFormat{ "%d.%m.%Y %H:%M:%S" }; bool singleClickNavigation{ false }; bool syncProjectTreeWithEditor{ true }; diff --git a/src/tools/ecode/datetimecontroller.cpp b/src/tools/ecode/datetimecontroller.cpp new file mode 100644 index 000000000..087b9f764 --- /dev/null +++ b/src/tools/ecode/datetimecontroller.cpp @@ -0,0 +1,128 @@ +#include "datetimecontroller.hpp" +#include "appconfig.hpp" +#include "plugins/plugincontextprovider.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ecode { + +DateTimeController::DateTimeController( PluginContextProvider* context ) : mContext( context ) {} + +void DateTimeController::registerCommands( EE::UI::Doc::TextDocument& doc ) { + doc.setCommand( "insert-date-dd-mm-yyyy", [this] { insertDate( "%d.%m.%Y" ); } ); + doc.setCommand( "insert-date-mm-dd-yyyy", [this] { insertDate( "%m.%d.%Y" ); } ); + doc.setCommand( "insert-date-yyyy-mm-dd", [this] { insertDate( "%Y/%m/%d" ); } ); + doc.setCommand( "insert-date-time-dd-mm-yyyy", [this] { insertDate( "%d.%m.%Y %H:%M:%S" ); } ); + doc.setCommand( "insert-date-time-mm-dd-yyyy", [this] { insertDate( "%m.%d.%Y %H:%M:%S" ); } ); + doc.setCommand( "insert-date-time-yyyy-mm-dd", [this] { insertDate( "%Y/%m/%d %H:%M:%S" ); } ); + doc.setCommand( "insert-date-custom", + [this] { insertDate( mContext->getConfig().editor.customDateFormat ); } ); + doc.setCommand( "set-custom-date-format", [this] { setCustomDateFormat(); } ); +} + +bool DateTimeController::isValidDateFormat( const std::string& format ) { + if ( String::trim( format ).empty() ) + return false; + + for ( size_t i = 0; i < format.size(); ++i ) { + if ( format[i] != '%' ) + continue; + + if ( ++i >= format.size() ) + return false; + + while ( i < format.size() && ( format[i] == '_' || format[i] == '-' || format[i] == '0' || + format[i] == '^' || format[i] == '#' ) ) + ++i; + + while ( i < format.size() && std::isdigit( static_cast( format[i] ) ) ) + ++i; + + if ( i < format.size() && ( format[i] == 'E' || format[i] == 'O' ) ) + ++i; + + if ( i >= format.size() ) + return false; + + static constexpr std::string_view VALID_SPECIFIERS = + "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%"; + if ( VALID_SPECIFIERS.find( format[i] ) == std::string_view::npos ) + return false; + } + + return true; +} + +std::string DateTimeController::formatCurrentDate( const std::string& format ) { + std::time_t rawtime; + std::time( &rawtime ); + std::tm* timeinfo = std::localtime( &rawtime ); + if ( nullptr == timeinfo ) + return {}; + + char buf[256]; + if ( std::strftime( buf, sizeof( buf ), format.c_str(), timeinfo ) == 0 ) + return {}; + return std::string( buf ); +} + +void DateTimeController::insertDate( const std::string& format ) { + if ( !mContext->getSplitter()->curEditorExistsAndFocused() ) + return; + + UICodeEditor* editor = mContext->getSplitter()->getCurEditor(); + if ( nullptr == editor || editor->isLocked() ) + return; + + if ( !isValidDateFormat( format ) ) + return; + + std::string date( formatCurrentDate( format ) ); + if ( !date.empty() ) + editor->getDocument().textInput( String::fromUtf8( date ) ); +} + +void DateTimeController::setCustomDateFormat() { + UIMessageBox* msgBox = + UIMessageBox::New( UIMessageBox::INPUT, + mContext + ->i18n( "set_custom_date_format_message", + "Set Custom Date Format:\nUses strftime format specifiers." ) + .unescape() ); + msgBox->setTitle( mContext->i18n( "set_custom_date_format", "Set Custom Date Format" ) ); + msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } ); + msgBox->getTextInput()->setText( mContext->getConfig().editor.customDateFormat ); + msgBox->getTextInput()->on( Event::OnTextChanged, [msgBox]( const Event* ) { + msgBox->getTextInput()->removeClass( "error" ); + } ); + msgBox->showWhenReady(); + msgBox->on( Event::OnConfirm, [this, msgBox]( const Event* ) { + std::string format( msgBox->getTextInput()->getText().toUtf8() ); + if ( !isValidDateFormat( format ) || formatCurrentDate( format ).empty() ) { + msgBox->getTextInput()->addClass( "error" ); + mContext->errorMsgBox( + mContext->i18n( "invalid_date_format", "Invalid date format." ) ); + return; + } + mContext->getConfig().editor.customDateFormat = std::move( format ); + msgBox->closeWindow(); + } ); + setFocusEditorOnClose( msgBox ); +} + +void DateTimeController::setFocusEditorOnClose( EE::UI::UIMessageBox* msgBox ) { + UICodeEditor* editor = mContext->getSplitter()->getCurEditor(); + msgBox->on( Event::OnWindowClose, [editor]( const Event* ) { + if ( editor && SceneManager::existsSingleton() && + !SceneManager::instance()->isShuttingDown() ) + editor->setFocus(); + } ); +} + +} // namespace ecode diff --git a/src/tools/ecode/datetimecontroller.hpp b/src/tools/ecode/datetimecontroller.hpp new file mode 100644 index 000000000..ce7bce51b --- /dev/null +++ b/src/tools/ecode/datetimecontroller.hpp @@ -0,0 +1,37 @@ +#ifndef ECODE_DATETIMECONTROLLER_HPP +#define ECODE_DATETIMECONTROLLER_HPP + +#include +#include + +namespace EE { namespace UI { +class UIMessageBox; +}} // namespace EE::UI + +namespace ecode { + +class PluginContextProvider; + +class DateTimeController { + public: + explicit DateTimeController( PluginContextProvider* context ); + + void registerCommands( EE::UI::Doc::TextDocument& doc ); + + private: + PluginContextProvider* mContext{ nullptr }; + + static bool isValidDateFormat( const std::string& format ); + + static std::string formatCurrentDate( const std::string& format ); + + void insertDate( const std::string& format ); + + void setCustomDateFormat(); + + void setFocusEditorOnClose( EE::UI::UIMessageBox* msgBox ); +}; + +} // namespace ecode + +#endif diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 1f956b912..31ac7a91c 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -1,6 +1,7 @@ #include "ecode.hpp" #include "colorschemetranslator.hpp" #include "customwidgets.hpp" +#include "datetimecontroller.hpp" #include "featureshealth.hpp" #include "keybindingshelper.hpp" #include "pathhelper.hpp" @@ -1107,6 +1108,7 @@ App::App( const size_t& jobs, const std::vector& args ) : mArgs( args ), mThreadPool( ThreadPool::createShared( jobs > 0 ? jobs : eemax( 4, Sys::getCPUCount() ) ) ), + mDateTimeController( std::make_unique( this ) ), mSettingsActions( std::make_unique( this ) ) {} static void fsRemoveAll( const std::string& fpath ) { @@ -2311,6 +2313,7 @@ std::vector App::getUnlockedCommands() { "maximize-tab-widget", "restore-maximized-tab-widget", "close-folder", + "set-custom-date-format", }; } @@ -3112,6 +3115,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { } } } ); + mDateTimeController->registerCommands( doc ); registerUnlockedCommands( doc ); editor->on( Event::OnDocumentSave, [this]( const Event* event ) { diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index 8209abb76..587fb6c1e 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -31,6 +31,7 @@ namespace ecode { class AutoCompletePlugin; class LinterPlugin; class FormatterPlugin; +class DateTimeController; class SettingsMenu; class UITreeViewFS; @@ -748,6 +749,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { std::unique_ptr mTerminalManager; std::unique_ptr mPluginManager; std::unique_ptr mSettings; + std::unique_ptr mDateTimeController; std::string mFileToOpen; UITheme* mTheme{ nullptr }; UIStatusBar* mStatusBar{ nullptr }; diff --git a/src/tools/ecode/plugins/plugincontextprovider.hpp b/src/tools/ecode/plugins/plugincontextprovider.hpp index 0e4c116b0..4bfd92c08 100644 --- a/src/tools/ecode/plugins/plugincontextprovider.hpp +++ b/src/tools/ecode/plugins/plugincontextprovider.hpp @@ -22,6 +22,7 @@ class UISplitter; class UITabWidget; class UISceneNode; class UICodeEditor; +class UIMessageBox; namespace Doc { class SyntaxColorScheme; @@ -94,6 +95,8 @@ class PluginContextProvider { virtual String i18n( const std::string& key, const String& def ) = 0; + virtual UIMessageBox* errorMsgBox( const String& msg ) = 0; + virtual const std::string& getWindowTitle() const = 0; virtual EE::Window::Window* getWindow() const = 0; diff --git a/src/tools/ecode/settingsmenu.cpp b/src/tools/ecode/settingsmenu.cpp index 67fbaa674..460245f41 100644 --- a/src/tools/ecode/settingsmenu.cpp +++ b/src/tools/ecode/settingsmenu.cpp @@ -1340,6 +1340,15 @@ UIMenu* SettingsMenu::createEditMenu() { getKeybind( "delete-to-next-char" ) ) ->setId( "delete-to-next-char" ); mEditMenu->addSeparator(); + mDateMenu = UIPopUpMenu::New(); + auto* dateMenuItem = mEditMenu->addSubMenu( i18n( "insert_date", "Insert Date" ), + findIcon( "calendar-2" ), mDateMenu ); + dateMenuItem->setId( "insert_date_menu" ); + dateMenuItem->on( Event::OnMenuShow, [this, dateMenuItem]( const Event* ) { + mDateMenu->setOwnerNode( dateMenuItem ); + updateDateMenu(); + } ); + mEditMenu->addSeparator(); mEditMenu ->add( i18n( "select_all", "Select All" ), findIcon( "select-all" ), getKeybind( "select-all" ) ) @@ -1395,6 +1404,7 @@ UIMenu* SettingsMenu::createEditMenu() { mEditMenu->getItemId( "redo" )->setEnabled( false ); mEditMenu->getItemId( "copy" )->setEnabled( false ); mEditMenu->getItemId( "cut" )->setEnabled( false ); + mEditMenu->getItemId( "insert_date_menu" )->setEnabled( false ); mEditMenu->getItemId( "open-containing-folder" )->setVisible( false ); mEditMenu->getItemId( "copy-containing-folder-path" )->setVisible( false ); moveSep->setEnabled( false )->setVisible( false ); @@ -1409,6 +1419,7 @@ UIMenu* SettingsMenu::createEditMenu() { mEditMenu->getItemId( "redo" )->setEnabled( doc->hasRedo() ); mEditMenu->getItemId( "copy" )->setEnabled( doc->hasSelection() ); mEditMenu->getItemId( "cut" )->setEnabled( doc->hasSelection() ); + mEditMenu->getItemId( "insert_date_menu" )->setEnabled( true ); mEditMenu->getItemId( "open-containing-folder" )->setVisible( doc->hasFilepath() ); mEditMenu->getItemId( "copy-containing-folder-path" )->setVisible( doc->hasFilepath() ); moveSep->setEnabled( true )->setVisible( true ); @@ -1420,6 +1431,48 @@ UIMenu* SettingsMenu::createEditMenu() { return mEditMenu; } +void SettingsMenu::updateDateMenu() { + if ( mDateMenu->getCount() != 0 ) + return; + mDateMenu->removeAll(); + mDateMenu->removeEventsOfType( Event::OnItemClicked ); + + struct DateFormatCommand { + const char* command; + const char* i18nKey; + const char* label; + }; + + static constexpr DateFormatCommand DATE_COMMANDS[] = { + { "insert-date-dd-mm-yyyy", "insert_date_dd_mm_yyyy", "dd.mm.yyyy" }, + { "insert-date-mm-dd-yyyy", "insert_date_mm_dd_yyyy", "mm.dd.yyyy" }, + { "insert-date-yyyy-mm-dd", "insert_date_yyyy_mm_dd", "yyyy/mm/dd" }, + { "insert-date-time-dd-mm-yyyy", "insert_date_time_dd_mm_yyyy", "dd.mm.yyyy hh:mm:ss" }, + { "insert-date-time-mm-dd-yyyy", "insert_date_time_mm_dd_yyyy", "mm.dd.yyyy hh:mm:ss" }, + { "insert-date-time-yyyy-mm-dd", "insert_date_time_yyyy_mm_dd", "yyyy/mm/dd hh:mm:ss" }, + }; + + for ( const auto& cmd : DATE_COMMANDS ) { + mDateMenu->add( i18n( cmd.i18nKey, cmd.label ), nullptr, getKeybind( cmd.command ) ) + ->setId( cmd.command ); + } + + mDateMenu->addSeparator(); + mDateMenu + ->add( i18n( "use_custom_date_format", "Use Custom Date Format" ), nullptr, + getKeybind( "insert-date-custom" ) ) + ->setId( "insert-date-custom" ); + mDateMenu + ->add( i18n( "set_custom_date_format", "Set Custom Date Format" ), nullptr, + getKeybind( "set-custom-date-format" ) ) + ->setId( "set-custom-date-format" ); + + mDateMenu->on( Event::OnItemClicked, [this]( const Event* event ) { + if ( event->getNode()->isType( UI_TYPE_MENUITEM ) ) + runCommand( event->getNode()->getId() ); + } ); +} + UIMenu* SettingsMenu::createWindowMenu() { mWindowMenu = UIPopUpMenu::New(); auto shouldCloseCb = []( UIMenuItem* ) -> bool { return false; }; diff --git a/src/tools/ecode/settingsmenu.hpp b/src/tools/ecode/settingsmenu.hpp index 1a13158e9..f6dd2c4b7 100644 --- a/src/tools/ecode/settingsmenu.hpp +++ b/src/tools/ecode/settingsmenu.hpp @@ -117,6 +117,7 @@ class SettingsMenu { UIPopUpMenu* mProjectMenu{ nullptr }; UIPopUpMenu* mHExtLanguageTypeMenu{ nullptr }; UIPopUpMenu* mEditMenu{ nullptr }; + UIPopUpMenu* mDateMenu{ nullptr }; UIPopUpMenu* mHelpMenu{ nullptr }; UIPopUpMenu* mLineWrapMenu{ nullptr }; UIPopUpMenu* mCodeFoldingMenu{ nullptr }; @@ -132,6 +133,8 @@ class SettingsMenu { void forEachTerminal( const std::function fn ); + void updateDateMenu(); + UITerminal* getCurrentTerminal() const; };