From 7226baab2c344b8ab3189d77e264b5c080795102 Mon Sep 17 00:00:00 2001 From: Oleg Fedko Date: Mon, 28 Sep 2026 18:16:29 +0300 Subject: [PATCH 1/3] User menu: replace the file mask filter with a "Show if" condition Items can be shown always, when there is a selection, or depending on whether the current item is a file or a folder (optionally with a file mask). Use Ctrl+H in the menu editor to show or hide filtered items. Keep selection on the item when moving it with Ctrl+Up/Ctrl+Down. --- far/FarCze.hlf.m4 | 9 +- far/FarEng.hlf.m4 | 11 +- far/FarGer.hlf.m4 | 11 +- far/FarHun.hlf.m4 | 11 +- far/FarPol.hlf.m4 | 10 +- far/FarRus.hlf.m4 | 8 +- far/FarSky.hlf.m4 | 10 +- far/FarUkr.hlf.m4 | 8 +- far/changelog | 7 + far/farlang.templ.m4 | 90 +++++++++++ far/usermenu.cpp | 348 ++++++++++++++++++++++++++++++++++++------- 11 files changed, 455 insertions(+), 68 deletions(-) diff --git a/far/FarCze.hlf.m4 b/far/FarCze.hlf.m4 index 3f7b142b19..05cd406640 100644 --- a/far/FarCze.hlf.m4 +++ b/far/FarCze.hlf.m4 @@ -1555,7 +1555,8 @@ $ #Menu Příkazy# #Upr. menu uživatele# Umožňuje upravit hlavní nebo lokání ~uživatelské menu~@UserMenu@. Můžete stisknout Ins pro vložení, Del pro - smazání a F4 pro úpravu záznamu. + smazání, Ctrl+H pro zobrazení filtrovaných položek + a F4 pro úpravu záznamu. #Závislosti souborů# Zobrazí seznam ~závislosti souborů~@FileAssoc@. Můžete stisknout Ins pro vložení, Del pro @@ -2248,12 +2249,18 @@ přiřazenou danému příkazu. podmenu, nebo položku v menu můžete klávesou Ins. Upravit existující podmenu, nebo položku v menu můžete klávesou F4. Pokud chcete menu upravovat v podobě textového souboru stiskněte Alt+F4. + Stiskněte #Ctrl+H# pro zobrazení položek menu skrytých podmínkou "Zobrazit, pokud". V uživatelském menu je možné použít jako zkratkové klávesy číslice, písmena a funkční klávezy (#F1#…#F24#). Pokud jsou použity #F1# nebo #F4#, je jejich původní funkce v menu potlačena. Nicméně, můžete pro úpravu menu použít #Shift+F4#. + Podmínka #Zobrazit, pokud# umožňuje skrýt položku menu nevhodnou v aktuální +situaci: #Vždy#, #Existuje výběr#, #Aktuální je soubor# nebo #Aktuální je složka#. +Pro poslední dvě lze zadat volitelnou ~masku souboru~@FileMasks@; položka se +zobrazí, pouze pokud název objektu pod kurzorem odpovídá masce. + Když tvoříte, nebo upravujet položku v menu, měli byste zadat horkou klávesu, pro rychlý přístup k položce, popis položky, který bude v menu zobrazen a sekvenci příkazů, která má být vykonána. diff --git a/far/FarEng.hlf.m4 b/far/FarEng.hlf.m4 index f09359b3a1..3fead8b8c5 100644 --- a/far/FarEng.hlf.m4 +++ b/far/FarEng.hlf.m4 @@ -1528,8 +1528,8 @@ $ #Menus: commands menu# detailed description. #Edit user menu# Allows to edit main or local ~user menu~@UserMenu@. - You can press #Ins# to insert, #Del# to delete - and #F4# to edit menu records. + You can press #Ins# to insert, #Del# to delete, + #Ctrl+H# to show filtered items and #F4# to edit menu records. #File associations# Displays the list of ~file associations~@FileAssoc@. You can press #Ins# to insert, #Del# to delete @@ -2215,11 +2215,18 @@ You can also press the hot key assigned to the required menu item. You can delete a submenu or menu item with the #Del# key, insert new submenu or menu item with the #Ins# key or edit an existing submenu or menu item with the #F4# key. Press #Alt+F4# to edit the menu in text file form. + Press #Ctrl+H# to show menu items hidden by the "Show if" condition. It is possible to use digits, letters and function keys (#F1#…#F24#) as hot keys in user menu. If #F1# or #F4# is used, its original function in user menu is overridden. However, you still can use #Shift+F4# to edit the menu. + The #Show if# condition allows you to hide a menu item that is inapplicable in +the current situation: #Always#, #Has selection#, #Current is a file# or +#Current is a folder#. For the last two an optional ~file mask~@FileMasks@ can +be specified; the item is shown only when the name of the object under the +cursor matches the mask. + When you edit or create a menu item, you should enter the hot key for fast item access, the item title which will be displayed in the menu and the command sequence to execute when this item will be selected. diff --git a/far/FarGer.hlf.m4 b/far/FarGer.hlf.m4 index 512266977e..8df01f97ea 100644 --- a/far/FarGer.hlf.m4 +++ b/far/FarGer.hlf.m4 @@ -1570,8 +1570,8 @@ $ #Menü Befehle# #Benutzermenü# Erlaubt das Bearbeiten des Haupt- oder #bearbeiten# lokalen ~Benutzermenüs~@UserMenu@. Drücken Sie #Einfg# zum Einfügen, #Entf# zum - Löschen und #F4# zum Bearbeiten von - Menüeinträgen. + Löschen, #Strg+H# zum Anzeigen gefilterter Einträge + und #F4# zum Bearbeiten von Menüeinträgen. #Dateiverknüpfung# Zeigt die Liste von ~Dateiverknüpfungen~@FileAssoc@. Drücken Sie #Einfg# zum Einfügen, #Entf# zum @@ -2289,11 +2289,18 @@ must switch to file mode with #Alt+F4# key. Menüpunkte oder Untermenüs können durch die Taste Entf gelöscht werden. Durch Einfg können neue Menüpunkte oder Untermenüs angelegt werden. Durch #F4# können existierende Menüpunkte und Untermenüs bearbeitet werden. Mit Alt+F4 kann ein Menü in Form einer Textdatei bearbeitet werden. + Mit #Strg+H# werden durch die Bedingung "Anzeigen wenn" ausgeblendete Menüeinträge angezeigt. Es ist möglich, Zahlen, Buchstaben und Funktionstasten (#F1#…#F24#) als Hotkeys in Benutzermenüs einzusetzen. Bei Benutzung von #F1# oder #F4# wird deren originale Funktion im Benutzermenü übergangen. #Umsch+F4# können Sie trotzdem weiterhin als Bearbeitungsfunktion nutzen. + Die Bedingung #Anzeigen wenn# ermöglicht das Ausblenden eines Menüeintrags, der +in der aktuellen Situation nicht zutrifft: #Immer#, #Auswahl vorhanden#, +#Aktuell ist eine Datei# oder #Aktuell ist ein Ordner#. Für die letzten beiden +kann eine optionale ~Dateimaske~@FileMasks@ angegeben werden; der Eintrag wird +nur angezeigt, wenn der Name des Objekts unter dem Cursor der Maske entspricht. + Wenn Sie einen Menüpunkt anlegen oder bearbeiten, geben Sie einen Hotkey für einen schnellen Zugriff an, einen Titel, welcher im Menü angezeigt wird sowie eine Befehlssequenz, die ausgeführt wird, wenn der Menüeintrag ausgewählt wird. diff --git a/far/FarHun.hlf.m4 b/far/FarHun.hlf.m4 index cb0d9800e7..44271a1eac 100644 --- a/far/FarHun.hlf.m4 +++ b/far/FarHun.hlf.m4 @@ -1565,8 +1565,8 @@ témakör részletezi a funkciót. #Felhasználói menü# Fő vagy helyi ~felhasználói menüt~@UserMenu@ #szerkesztése# szerkeszthetünk. Az #Ins# billentyű beszúrja, - a #Del# törli, az #F4# pedig szerkeszti a menü - elemeit. + a #Del# törli, a #Ctrl+H# megmutatja a szűrt + elemeket, az #F4# pedig szerkeszti a menü elemeit. #Fájltársítások# ^Megjeleníti a ~fájltársításokat~@FileAssoc@. Az #Ins# billentyűvel beszúrhatjuk, a #Del#-lel törölhetjük, az #F4#-gyel @@ -2292,6 +2292,7 @@ gyorsbillentyű segítségével. Az #Ins# billentyűvel új menüelemeket, almenüket hozhatunk létre, az #F4# bilentyűvel szerkeszthetjük a jellemzőiket, a #Del#-lel pedig törölhetjük a menü elemeit. #Alt+F4#-gyel a menüket szövegfájl formátumban szerkeszthetjük. + A #Ctrl+H# megmutatja a "Megjelenítés, ha" feltétel miatt elrejtett menüpontokat. A felhasználói menü elemeinek gyorsbillentyűjeként megadhatunk számokat, betűket vagy funkcióbillentyűket (#F1#…#F24#), utóbbiakat csak beírás útján @@ -2299,6 +2300,12 @@ fogadja el. Ha az #F1# vagy #F4# eredetileg más művelethez volt rendelve, a felhasználói menü felülbírálja eredeti funkcióikat, bár a #Shift+F4#-gyel ezután is szerkeszthetjük a menüt. + A #Megjelenítés, ha# feltétel lehetővé teszi az adott helyzetben nem illő +menüpont elrejtését: #Mindig#, #Van kijelölés#, #A jelenlegi fájl# vagy +#A jelenlegi mappa#. Az utolsó kettőhöz opcionális ~fájlmaszk~@FileMasks@ +adható meg; a menüpont csak akkor jelenik meg, ha a kurzor alatti objektum neve +illik a maszkra. + Ha a szerkesztett menüelemünk parancs, meg kell adnunk a gyorsbillentyűjét, a nevét (ami majd a menüben jelenik meg) és a végrehajtandó parancs sorát. diff --git a/far/FarPol.hlf.m4 b/far/FarPol.hlf.m4 index 75f172dcc7..64034b70f9 100644 --- a/far/FarPol.hlf.m4 +++ b/far/FarPol.hlf.m4 @@ -1521,8 +1521,8 @@ $ #Menu: menu polecenia# Więcej informacji: ~Porównaj foldery~@CompFolders@. #Edytuj menu# Pozwala na edycję głównego lub lokalnego ~menu użytkownika~@UserMenu@. - #użytkownika# Można wcisnąć #Ins# aby wstawić, #Del# aby usunąć - i #F4# aby edytować pozycję. + #użytkownika# Można wcisnąć #Ins# aby wstawić, #Del# aby usunąć, + #Ctrl+H# aby pokazać odfiltrowane pozycje i #F4# aby edytować pozycję. #Przypisania plików# Wyświetla listę ~powiązań plików~@FileAssoc@. Można wcisnąć #Ins# aby wstawić, #Del# aby usunąć @@ -2216,11 +2216,17 @@ menu. podmenu lub pozycję menu za pomocą klawisza #Ins# lub edytować istniejące podmenu lub pozycję menu za pomocą klawisza #F4#. Należy wcisnąć klawisze #Alt+F4#, aby edytować menu w formie pliku tekstowego. + Naciśnij #Ctrl+H#, aby pokazać pozycje menu ukryte przez warunek "Pokaż, jeżeli". Do klawiszy skrótu można użyć cyfr, liter i klawiszy funkcyjnych (#F1#…#F24#). Jeżeli użyto klawiszy #F1# lub #F4# - to zamieniona zostanie oryginalna funkcja tych klawiszy. Mimo to, skrót #Shift+F4# nadal może być użyty do edycji menu. + Warunek #Pokaż, jeżeli# pozwala ukryć pozycję menu niepasującą do bieżącej +sytuacji: #Zawsze#, #Jest zaznaczenie#, #Bieżący to plik# lub #Bieżący to folder#. +Dla dwóch ostatnich można podać opcjonalną ~maskę pliku~@FileMasks@; pozycja +jest pokazywana tylko wtedy, gdy nazwa obiektu pod kursorem pasuje do maski. + Podczas edycji lub tworzenia nowej pozycji menu, można (a nawet powinno) dodać klawisz skrótu dla szybszego dostępu, tytuł który będzie wyświetlany w menu oraz sekwencję poleceń do wykonania, gdy ta pozycja zostanie wybrana. diff --git a/far/FarRus.hlf.m4 b/far/FarRus.hlf.m4 index eff3cc932c..43f12e56d5 100644 --- a/far/FarRus.hlf.m4 +++ b/far/FarRus.hlf.m4 @@ -1564,7 +1564,7 @@ $ #Меню команд# #Меню пользователя# Позволяет редактировать главное или местное ~меню пользователя~@UserMenu@. Для вставки пункта используется #Ins#, для удаления - #Del#, - для редактирования - #F4#. + #Ctrl+H# для показа отфильтрованных пунктов, для редактирования - #F4#. #Ассоциации файлов# Показывает список ~ассоциаций файлов~@FileAssoc@. Для вставки новой ассоциации может @@ -2266,12 +2266,18 @@ $ #Меню пользователя# вставить новое вложенное меню или пункт меню с помощью #Ins# и редактировать существующее вложенное меню или пункт меню с помощью #F4#. Нажмите #Alt+F4# для редактирования меню в виде текстового файла. + Нажмите #Ctrl+H#, чтобы показать пункты меню, скрытые условием "Показывать если". В качестве горячих клавиш для обращения к пунктам меню могут использоваться цифры, буквы и функциональные клавиши (#F1#…#F24#). Если использованы #F1# и #F4#, их первоначальные функции теряются. В этом случае для редактирования меню может применяться #Shift+F4#. + Условие #Показывать если# позволяет скрыть пункт меню, неприменимый в текущей +ситуации: #Всегда#, #Есть выделение#, #Текущий — файл# или #Текущий — папка#. +Для последних двух можно указать необязательную ~маску файла~@FileMasks@; пункт +показывается, только если имя объекта под курсором совпадает с маской. + При редактировании или создании пункта меню нужно ввести заголовок пункта, который будет отображаться в меню, последовательность команд, выполняющихся при выбора данного пункта меню, и, при необходимости, горячую клавишу для быстрого diff --git a/far/FarSky.hlf.m4 b/far/FarSky.hlf.m4 index 75eb6ab538..66b5bd377c 100644 --- a/far/FarSky.hlf.m4 +++ b/far/FarSky.hlf.m4 @@ -1526,7 +1526,8 @@ $ #Commands menu# #Úprava užívateľského# Umožní úpravu hlavného alebo lokálneho #menu# ~užívateľského menu~@UserMenu@. Môžete použiť #Ins# - pre vloženie, #Del# pre vymazanie a #F4# pre úpravu položiek menu. + pre vloženie, #Del# pre vymazanie, #Ctrl+H# pre zobrazenie + filtrovaných položiek a #F4# pre úpravu položiek menu. #Typy súborov# Zobrazí ~typy súborov~@FileAssoc@. Môžete použiť #Ins# pre vloženie, #Del# pre @@ -2210,11 +2211,18 @@ názov a stlačením klávesy Enter alebo jemu priradenou klávesovou skratkou. Podmenu alebo položku menu môžete zrušiť klávesom #Del#, nové menu alebo položku môžete vložiť klávesom #Ins# a upraviť existujúce menu či položku možno klávesom #F4#. Po stlačení #Alt+F4# budete môcť menu editovať v textovej forme. + Stlačením #Ctrl+H# zobrazíte položky menu skryté podmienkou "Zobraziť, ak". Ako klávesové skratky možno v menu použiť číslice, písmená a funkčné klávesy (#F1#…#F24#). Ak použijete #F1# alebo #F4#, znemožníte tým ich originálnu funkciu. Každopádne na úpravu môžete ešte vždy použiť kombináciu #Shift+F4#. + Podmienka #Zobraziť, ak# umožňuje skryť položku menu nevhodnú v aktuálnej +situácii: #Vždy#, #Existuje výber#, #Aktuálny je súbor# alebo +#Aktuálny je priečinok#. Pre posledné dve možno zadať voliteľnú +~masku súboru~@FileMasks@; položka sa zobrazí, len ak názov objektu pod kurzorom +zodpovedá maske. + Keď upravujete alebo vytvárate položku menu, mali by ste zadať klávesovú skratku pre rýchly prítup, názov príkazu, ktorý sa bude zobrazovať v menu, a postupnosť príkazov ktoré sa majú vykonať pri zvolení tejto položky. diff --git a/far/FarUkr.hlf.m4 b/far/FarUkr.hlf.m4 index d1052dbeaf..ef2c0c1959 100644 --- a/far/FarUkr.hlf.m4 +++ b/far/FarUkr.hlf.m4 @@ -1557,7 +1557,7 @@ $ #Меню команд# #Меню користувача# Дозволяє редагувати головне або місцеве ~меню користувача~@UserMenu@. Для вставки пункту використовується Ins, для видалення - #Del#, - для редагування - #F4#. + #Ctrl+H# для показу відфільтрованих пунктів, для редагування - #F4#. #Асоціації файлів# Показує список ~асоціацій файлів~@FileAssoc@. Для вставки нової асоціації може @@ -2266,12 +2266,18 @@ $ #Меню користувача# за допомогою #Ins# і редагувати існуюче вкладене меню або пункт меню за допомогою #F4#. Натисніть #Alt+F4# для редагування меню у вигляді текстового файла. + Натисніть #Ctrl+H#, щоб показати пункти меню, приховані умовою "Показувати якщо". У якості гарячих клавіш для звернення до пунктів меню можуть використовуватися цифри, літери і функціональні клавіші (#F1#…#F24#). Якщо використані #F1# і #F4#, їх первинні функції втрачаються. У цьому випадку для редагування меню може застосовуватися Shift+F4. + Умова #Показувати якщо# дозволяє приховати пункт меню, непридатний у поточній +ситуації: #Завжди#, #Є виділення#, #Поточний — файл# або #Поточний — папка#. +Для останніх двох можна вказати необов'язкову ~маску файла~@FileMasks@; пункт +показується, тільки якщо ім'я об'єкта під курсором збігається з маскою. + При редагуванні або створенні пункту меню треба ввести гарячу клавішу для швидкого доступу до цього пункту, заголовок пункту, який буде відображатися в меню і послідовність команд diff --git a/far/changelog b/far/changelog index b5bd0b0a85..ec624a3997 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,10 @@ +-------------------------------------------------------------------------------- +olegfedko 2026-09-25 20:00:00+03:00 + +1. User menu items can be hidden by a "Show if" condition: always, has selection, current is a file or current is a folder (with an optional file mask for the last two). Use Ctrl+H in the menu editor to show or hide filtered items. + +2. User menu: keep selection on the item when moving it with Ctrl+Up/Ctrl+Down. + -------------------------------------------------------------------------------- drkns 2026-09-25 18:58:09+01:00 - build 6739 diff --git a/far/farlang.templ.m4 b/far/farlang.templ.m4 index 0f134742a2..d29d16a681 100644 --- a/far/farlang.templ.m4 +++ b/far/farlang.templ.m4 @@ -23032,6 +23032,96 @@ MEditMenuLabel "&Vardas:" "&Volume:" +MEditMenuShowIf +"&Показывать если:" +"&Show if:" +"Zob&razit, pokud:" +"&Anzeigen wenn:" +"&Megjelenítés, ha:" +"Poka&ż, jeżeli:" +"&Mostrar si:" +"&Zobraziť, ak:" +"&Mostra se:" +"&Показувати якщо:" +"Пака&зваць, калі:" +"&Rodyti, jei:" +"&Mostrar se:" + +MEditMenuMask +"Ма&ска:" +"&Mask:" +"&Maska:" +"&Maske:" +"Mas&zk:" +"&Maska:" +"Má&scara:" +"&Maska:" +"Ma&schera:" +"Ма&ска:" +"&Маска:" +"Kauk&ė:" +"Má&scara:" + +MShowIfAlways +"Всегда" +"Always" +"Vždy" +"Immer" +"Mindig" +"Zawsze" +"Siempre" +"Vždy" +"Sempre" +"Завжди" +"Заўсёды" +"Visada" +"Sempre" + +MShowIfHasSelection +"Есть выделение" +"Has selection" +"Existuje výběr" +"Auswahl vorhanden" +"Van kijelölés" +"Jest zaznaczenie" +"Hay selección" +"Existuje výber" +"C'è una selezione" +"Є виділення" +"Ёсць выдзяленне" +"Yra pasirinkimas" +"Há seleção" + +MShowIfCurrentFile +"Текущий — файл" +"Current is a file" +"Aktuální je soubor" +"Aktuell ist eine Datei" +"A jelenlegi fájl" +"Bieżący to plik" +"Actual es un archivo" +"Aktuálny je súbor" +"L'elemento corrente è un file" +"Поточний — файл" +"Бягучы — файл" +"Dabartinis yra failas" +"Atual é um arquivo" + +MShowIfCurrentDir +"Текущий — папка" +"Current is a folder" +"Aktuální je složka" +"Aktuell ist ein Ordner" +"A jelenlegi mappa" +"Bieżący to folder" +"Actual es una carpeta" +"Aktuálny je priečinok" +"L'elemento corrente è una cartella" +"Поточний — папка" +"Бягучы — тэчка" +"Dabartinis yra aplankas" +"Atual é uma pasta" + MEditMenuCommands "&Команды:" "&Commands:" diff --git a/far/usermenu.cpp b/far/usermenu.cpp index e892876f1b..6afaafdb03 100644 --- a/far/usermenu.cpp +++ b/far/usermenu.cpp @@ -67,6 +67,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "log.hpp" #include "codepage.hpp" #include "strmix.hpp" +#include "filemasks.hpp" +#include "common.hpp" // Platform: #include "platform.hpp" @@ -110,8 +112,8 @@ class UserMenu : noncopyable private: void ProcessUserMenu(bool ChooseMenuType, string_view MenuFileName); bool DeleteMenuRecord(menu_container& Menu, const menu_container::iterator& MenuItem) const; - bool EditMenu(menu_container& Menu, menu_container::iterator* MenuItem, bool Create); - int ProcessSingleMenu(menu_container& Menu, int MenuPos, menu_container& MenuRoot, string_view MenuFileName, const string& Title); + bool EditMenu(menu_container& Menu, menu_container::iterator& MenuItem, bool Create); + int ProcessSingleMenu(menu_container& Menu, menu_container& MenuRoot, string_view MenuFileName, const string& Title); void SaveMenu(string_view MenuFileName) const; intptr_t EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, void* Param2); @@ -163,16 +165,135 @@ static int PrepareHotKey(string &strHotKey) } static const auto LocalMenuFileName = L"FarMenu.ini"sv; +static const auto MenuShowIfPrefix = L"@ShowIf:"sv; +static const auto MenuSeparator = L"--"sv; + +enum class MenuItemShowIf +{ + Always, + HasSelection, + CurrentFile, + CurrentDir, +}; + +static constexpr MenuItemShowIf MenuShowIfOrder[] +{ + MenuItemShowIf::Always, + MenuItemShowIf::HasSelection, + MenuItemShowIf::CurrentFile, + MenuItemShowIf::CurrentDir, +}; + +static bool ShowIfUsesMask(MenuItemShowIf const ShowIf) +{ + return ShowIf == MenuItemShowIf::CurrentFile || ShowIf == MenuItemShowIf::CurrentDir; +} + +static lng ShowIfTitleId(MenuItemShowIf const ShowIf) +{ + switch (ShowIf) + { + case MenuItemShowIf::HasSelection: return lng::MShowIfHasSelection; + case MenuItemShowIf::CurrentFile: return lng::MShowIfCurrentFile; + case MenuItemShowIf::CurrentDir: return lng::MShowIfCurrentDir; + case MenuItemShowIf::Always: + default: return lng::MShowIfAlways; + } +} + +static string_view ShowIfToToken(MenuItemShowIf const ShowIf) +{ + switch (ShowIf) + { + case MenuItemShowIf::HasSelection: return L"HasSelection"sv; + case MenuItemShowIf::CurrentFile: return L"CurrentFile"sv; + case MenuItemShowIf::CurrentDir: return L"CurrentDir"sv; + case MenuItemShowIf::Always: + default: return L"Always"sv; + } +} + +static bool TokenToShowIf(string_view const Token, MenuItemShowIf& ShowIf) +{ + for (const auto Value: MenuShowIfOrder) + { + if (equal_icase(Token, ShowIfToToken(Value))) + { + ShowIf = Value; + return true; + } + } + + return false; +} + +static int ShowIfToIndex(MenuItemShowIf const ShowIf) +{ + const auto Found = std::find(std::begin(MenuShowIfOrder), std::end(MenuShowIfOrder), ShowIf); + return Found == std::end(MenuShowIfOrder)? 0 : static_cast(Found - std::begin(MenuShowIfOrder)); +} + +struct MenuFilterContext +{ + bool HasSelection{}; + bool HasCurrent{}; + bool CurrentIsDir{}; + string strCurrentName; +}; struct UserMenu::UserMenuItem { string strHotKey; string strLabel; + MenuItemShowIf ShowIf{ MenuItemShowIf::Always }; + string strMask; std::list Commands; bool Submenu{}; menu_container Menu; }; +static bool ItemVisible(MenuItemShowIf const ShowIf, string_view const Mask, MenuFilterContext const& Ctx, bool const ShowAllItems) +{ + if (ShowAllItems || ShowIf == MenuItemShowIf::Always) + return true; + + if (ShowIf == MenuItemShowIf::HasSelection) + return Ctx.HasSelection; + + if (!Ctx.HasCurrent || Ctx.CurrentIsDir != (ShowIf == MenuItemShowIf::CurrentDir)) + return false; + + if (Mask.empty()) + return true; + + filemasks FMask; + if (!FMask.assign(Mask, FMF_SILENT)) + return true; + + return FMask.check(Ctx.strCurrentName); +} + +static void AdjustSelectedItemForFilter( + UserMenu::menu_container& Menu, + UserMenu::menu_container::iterator& SelectedItem, + MenuFilterContext const& Ctx, + bool const ShowAllItems) +{ + if (SelectedItem != Menu.end() && ItemVisible(SelectedItem->ShowIf, SelectedItem->strMask, Ctx, ShowAllItems)) + return; + + for (auto i = Menu.begin(); i != Menu.end(); ++i) + { + if (ItemVisible(i->ShowIf, i->strMask, Ctx, ShowAllItems)) + { + SelectedItem = i; + return; + } + } + + SelectedItem = Menu.end(); +} + static string SerializeMenu(const UserMenu::menu_container& Menu) { string Result; @@ -183,13 +304,24 @@ static string SerializeMenu(const UserMenu::menu_container& Menu) auto HotkeyStr = pad_right(i.strHotKey + L':', 5); append(Result, HotkeyStr, i.strLabel, Eol); + const string Padding(HotkeyStr.size(), L' '); + + if (i.ShowIf != MenuItemShowIf::Always) + { + append(Result, Padding, MenuShowIfPrefix, L' ', ShowIfToToken(i.ShowIf)); + + if (ShowIfUsesMask(i.ShowIf) && !i.strMask.empty()) + append(Result, L' ', i.strMask); + + append(Result, Eol); + } + if (i.Submenu) { append(Result, L'{', Eol, SerializeMenu(i.Menu), L'}', Eol); } else { - const string Padding(HotkeyStr.size(), L' '); for (const auto& str: i.Commands) { append(Result, Padding, str, Eol); @@ -251,7 +383,26 @@ static void ParseMenu(UserMenu::menu_container& Menu, std::ranges::subrangeCommands.emplace_back(trim_left(MenuStr)); + const auto CommandStr = trim_left(MenuStr); + + if (starts_with_icase(CommandStr, MenuShowIfPrefix)) + { + const auto Rest = trim_left(string_view(CommandStr).substr(MenuShowIfPrefix.size())); + const auto SpacePos = Rest.find_first_of(L" \t"sv); + const auto Token = Rest.substr(0, SpacePos); + + if (MenuItemShowIf ShowIf; TokenToShowIf(Token, ShowIf)) + { + MenuItem->ShowIf = ShowIf; + + if (ShowIfUsesMask(ShowIf) && SpacePos != string_view::npos) + MenuItem->strMask = trim_left(Rest.substr(SpacePos + 1)); + } + } + else + { + MenuItem->Commands.emplace_back(CommandStr); + } } } } @@ -460,7 +611,7 @@ void UserMenu::ProcessUserMenu(bool ChooseMenuType, string_view MenuFileName) } // вызываем меню - ExitCode=ProcessSingleMenu(m_Menu, 0, m_Menu, strMenuFileFullPath, MenuTitle); + ExitCode=ProcessSingleMenu(m_Menu, m_Menu, strMenuFileFullPath, MenuTitle); // ...запишем изменения обратно в файл SaveMenu(strMenuFileFullPath); @@ -535,30 +686,36 @@ void UserMenu::ProcessUserMenu(bool ChooseMenuType, string_view MenuFileName) using fkey_to_pos_map = std::array; // заполнение меню -static void FillUserMenu(VMenu2& FarUserMenu, UserMenu::menu_container& Menu, int MenuPos, fkey_to_pos_map& FuncPos, const subst_context& SubstContext) +static void FillUserMenu( + VMenu2& FarUserMenu, + UserMenu::menu_container& Menu, + UserMenu::menu_container::iterator& SelectedItem, + fkey_to_pos_map& FuncPos, + const subst_context& SubstContext, + MenuFilterContext const& FilterCtx, + bool const ShowAllItems) { SCOPED_ACTION(Dialog::suppress_redraw)(&FarUserMenu); FarUserMenu.clear(); FuncPos.fill(-1); - int NumLines = -1; + + while (SelectedItem != Menu.end() && SelectedItem->strHotKey == MenuSeparator) + ++SelectedItem; FOR_RANGE(Menu, MenuItem) { - ++NumLines; + if (!ItemVisible(MenuItem->ShowIf, MenuItem->strMask, FilterCtx, ShowAllItems)) + continue; + menu_item_ex FarUserMenuItem; int FuncNum=0; // сепаратором является случай, когда хоткей == "--" - if (MenuItem->strHotKey == L"--"sv) + if (MenuItem->strHotKey == MenuSeparator) { FarUserMenuItem.Flags|=LIF_SEPARATOR; FarUserMenuItem.set_name(MenuItem->strLabel); - - if (NumLines==MenuPos) - { - MenuPos++; - } } else { @@ -578,10 +735,10 @@ static void FillUserMenu(VMenu2& FarUserMenu, UserMenu::menu_container& Menu, in { FarUserMenuItem.Flags|=MIF_SUBMENU; } - - FarUserMenuItem.set_select(NumLines==MenuPos); } + FarUserMenuItem.set_select(MenuItem == SelectedItem); + FarUserMenuItem.ComplexUserData = MenuItem; const auto ItemPos = FarUserMenu.AddItem(FarUserMenuItem); @@ -593,37 +750,51 @@ static void FillUserMenu(VMenu2& FarUserMenu, UserMenu::menu_container& Menu, in } // обработка единичного меню -int UserMenu::ProcessSingleMenu(std::list& Menu, int MenuPos, std::list& MenuRoot, string_view const MenuFileName, const string& Title) +int UserMenu::ProcessSingleMenu(std::list& Menu, std::list& MenuRoot, string_view const MenuFileName, const string& Title) { + menu_container::iterator SelectedItem = Menu.begin(); + bool ShowAllItems = false; + for (;;) { string Names[2]; - Global->CtrlObject->Cp()->ActivePanel()->GetCurName(Names[0], Names[1]); + const auto ActivePanel = Global->CtrlObject->Cp()->ActivePanel(); + ActivePanel->GetCurName(Names[0], Names[1]); const auto& strName = Names[0]; const auto& strShortName = Names[1]; const subst_context Context(strName, strShortName); + MenuFilterContext FilterCtx; + FilterCtx.HasSelection = ActivePanel->GetRealSelCount() != 0; + os::fs::attributes CurrentAttributes{}; + FilterCtx.HasCurrent = ActivePanel->GetFileName(FilterCtx.strCurrentName, ActivePanel->GetCurrentPos(), CurrentAttributes); + FilterCtx.CurrentIsDir = FilterCtx.HasCurrent && os::fs::is_directory(CurrentAttributes); + const auto UserMenu = VMenu2::create(Title, {}, ScrY - 4); UserMenu->SetMenuFlags(VMENU_WRAPMODE | VMENU_NOMERGEBORDER); UserMenu->SetHelp(L"UserMenu"sv); UserMenu->SetPosition({ -1, -1, 0, 0 }); - UserMenu->SetBottomTitle(KeysToLocalizedText(KEY_INS, KEY_DEL, KEY_F4, KEY_ALTF4, KEY_CTRLUP, KEY_CTRLDOWN)); + UserMenu->SetBottomTitle(KeysToLocalizedText(KEY_INS, KEY_DEL, KEY_CTRLH, KEY_F4, KEY_ALTF4, KEY_CTRLUP, KEY_CTRLDOWN)); UserMenu->SetMacroMode(MACROAREA_USERMENU); int ReturnCode=1; fkey_to_pos_map FuncPos; - FillUserMenu(*UserMenu, Menu, MenuPos, FuncPos, Context); + AdjustSelectedItemForFilter(Menu, SelectedItem, FilterCtx, ShowAllItems); + FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, FilterCtx, ShowAllItems); const auto ExitCode = UserMenu->Run([&](const Manager::Key& RawKey) { const auto Key=RawKey(); - MenuPos=UserMenu->GetSelectPos(); + const auto MenuPos=UserMenu->GetSelectPos(); // CurrentMenuItem can be nullptr if: // - menu is empty // - menu is not empty, but insidiously consists only of separators const auto CurrentMenuItem = UserMenu->GetComplexUserDataPtr>(MenuPos); + if (CurrentMenuItem && !any_of(Key, KEY_DEL, KEY_NUMDEL)) + SelectedItem = *CurrentMenuItem; + if (Key==KEY_SHIFTF1) { UserMenu->Key(KEY_F1); @@ -665,13 +836,26 @@ int UserMenu::ProcessSingleMenu(std::list& Menu, int MenuPos, std: UserMenu->Close(-1); break; + case KEY_CTRLH: + case KEY_RCTRLH: + ShowAllItems = !ShowAllItems; + AdjustSelectedItemForFilter(Menu, SelectedItem, FilterCtx, ShowAllItems); + FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, FilterCtx, ShowAllItems); + break; + case KEY_NUMDEL: case KEY_DEL: if (CurrentMenuItem) { - DeleteMenuRecord(Menu, *CurrentMenuItem); + const auto ItemToDelete = *CurrentMenuItem; + const auto NextItem = std::next(ItemToDelete); + if (!DeleteMenuRecord(Menu, ItemToDelete)) + break; + + SelectedItem = NextItem; + AdjustSelectedItemForFilter(Menu, SelectedItem, FilterCtx, ShowAllItems); // BUGBUG update dynamically instead of full refill - FillUserMenu(*UserMenu, Menu, MenuPos, FuncPos, Context); + FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, FilterCtx, ShowAllItems); } break; @@ -684,9 +868,17 @@ int UserMenu::ProcessSingleMenu(std::list& Menu, int MenuPos, std: if (!IsNew && !CurrentMenuItem) break; - EditMenu(Menu, CurrentMenuItem, IsNew); - // BUGBUG update dynamically instead of full refill - FillUserMenu(*UserMenu, Menu, MenuPos, FuncPos, Context); + auto TargetItem = CurrentMenuItem? *CurrentMenuItem : SelectedItem; + if (EditMenu(Menu, TargetItem, IsNew)) + { + SelectedItem = TargetItem; + + if (!ItemVisible(SelectedItem->ShowIf, SelectedItem->strMask, FilterCtx, false)) + ShowAllItems = true; + + // BUGBUG update dynamically instead of full refill + FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, FilterCtx, ShowAllItems); + } break; } @@ -710,16 +902,15 @@ int UserMenu::ProcessSingleMenu(std::list& Menu, int MenuPos, std: if (Up) { --Other; - --MenuPos; } else { ++Other; - ++MenuPos; } node_swap(Menu, *CurrentMenuItem, Other); + SelectedItem = *CurrentMenuItem; // BUGBUG update dynamically instead of full refill - FillUserMenu(*UserMenu, Menu, MenuPos, FuncPos, Context); + FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, FilterCtx, ShowAllItems); } break; @@ -799,12 +990,11 @@ int UserMenu::ProcessSingleMenu(std::list& Menu, int MenuPos, std: if ((*CurrentMenuItem)->Submenu) { /* $ 14.07.2000 VVM ! Если закрыли подменю, то остаться. Иначе передать управление выше */ - MenuPos = ProcessSingleMenu((*CurrentMenuItem)->Menu, 0, MenuRoot, MenuFileName, concat(Title, L" » "sv, CurrentLabel)); + const auto SubExitCode = ProcessSingleMenu((*CurrentMenuItem)->Menu, MenuRoot, MenuFileName, concat(Title, L" » "sv, CurrentLabel)); - if (MenuPos!=EC_CLOSE_LEVEL) - return MenuPos; + if (SubExitCode!=EC_CLOSE_LEVEL) + return SubExitCode; - MenuPos = ExitCode; continue; } @@ -895,6 +1085,10 @@ enum EditMenuItems EM_HOTKEY_EDIT, EM_LABEL_TEXT, EM_LABEL_EDIT, + EM_SHOWIF_TEXT, + EM_SHOWIF_COMBO, + EM_MASK_TEXT, + EM_MASK_EDIT, EM_SEPARATOR1, EM_COMMANDS_TEXT, #ifdef PROJECT_DI_MEMOEDIT @@ -926,11 +1120,22 @@ intptr_t UserMenu::EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, v #ifdef PROJECT_DI_MEMOEDIT if (Param1 == EM_MEMOEDIT) #else - if (Param1 >= EM_EDITLINE_0 && Param1 <= EM_EDITLINE_9) + if ((Param1 >= EM_EDITLINE_0 && Param1 <= EM_EDITLINE_9) || any_of(Param1, EM_MASK_EDIT, EM_SHOWIF_COMBO)) #endif m_ItemChanged = true; break; + case DN_LISTCHANGE: + if (Param1 == EM_SHOWIF_COMBO) + { + const auto ShowIf = MenuShowIfOrder[std::clamp(static_cast(std::bit_cast(Param2)), 0, static_cast(std::size(MenuShowIfOrder)) - 1)]; + const auto EnableMask = ShowIfUsesMask(ShowIf); + Dlg->SendMessage(DM_ENABLE, EM_MASK_TEXT, ToPtr(EnableMask)); + Dlg->SendMessage(DM_ENABLE, EM_MASK_EDIT, ToPtr(EnableMask)); + m_ItemChanged = true; + } + break; + case DN_CLOSE: if (Param1==EM_BUTTON_OK) @@ -938,9 +1143,11 @@ intptr_t UserMenu::EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, v bool Result = true; const string_view HotKey = std::bit_cast(Dlg->SendMessage(DM_GETCONSTTEXTPTR, EM_HOTKEY_EDIT, nullptr)); const string_view Label = std::bit_cast(Dlg->SendMessage(DM_GETCONSTTEXTPTR, EM_LABEL_EDIT, nullptr)); + const string_view Mask = std::bit_cast(Dlg->SendMessage(DM_GETCONSTTEXTPTR, EM_MASK_EDIT, nullptr)); + const auto ShowIf = MenuShowIfOrder[std::clamp(static_cast(Dlg->SendMessage(DM_LISTGETCURPOS, EM_SHOWIF_COMBO, nullptr)), 0, static_cast(std::size(MenuShowIfOrder)) - 1)]; int FocusPos=-1; - if (HotKey != L"--"sv) + if (HotKey != MenuSeparator) { if (Label.empty()) { @@ -958,6 +1165,12 @@ intptr_t UserMenu::EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, v } } + if (FocusPos == -1 && ShowIfUsesMask(ShowIf) && !Mask.empty() && !filemasks().assign(Mask)) + { + Dlg->SendMessage(DM_SETFOCUS, EM_MASK_EDIT, nullptr); + return false; + } + if (FocusPos!=-1) { Message(MSG_WARNING, @@ -1002,7 +1215,7 @@ intptr_t UserMenu::EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, v } -bool UserMenu::EditMenu(std::list& Menu, std::list::iterator* MenuItem, bool Create) +bool UserMenu::EditMenu(std::list& Menu, std::list::iterator& MenuItem, bool Create) { m_ItemChanged = false; @@ -1032,10 +1245,10 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: } else { - SubMenu = (*MenuItem)->Submenu; + SubMenu = MenuItem->Submenu; } - const int DLG_X=76, DLG_Y=SubMenu?10:22; + const int DLG_X=76, DLG_Y=SubMenu?12:24; const auto State = SubMenu? DIF_HIDDEN | DIF_DISABLE : DIF_NONE; auto EditDlg = MakeDialogItems( @@ -1045,13 +1258,15 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: { DI_FIXEDIT, {{5, 3 }, {7, 3 }}, DIF_FOCUS, }, { DI_TEXT, {{5, 4 }, {0, 4 }}, DIF_NONE, msg(lng::MEditMenuLabel), }, { DI_EDIT, {{5, 5 }, {DLG_X-6, 5 }}, DIF_NONE, }, - { DI_TEXT, {{-1, 6 }, {0, 6 }}, DIF_SEPARATOR | State, }, - { DI_TEXT, {{5, 7 }, {0, 7 }}, State, msg(lng::MEditMenuCommands), }, + { DI_TEXT, {{5, 6 }, {0, 6 }}, DIF_NONE, msg(lng::MEditMenuShowIf), }, + { DI_COMBOBOX, {{5, 7 }, {36, 7 }}, DIF_DROPDOWNLIST | DIF_LISTNOAMPERSAND | DIF_LISTWRAPMODE, }, + { DI_TEXT, {{40, 7 }, {0, 7 }}, DIF_NONE, msg(lng::MEditMenuMask), }, + { DI_EDIT, {{50, 7 }, {DLG_X-6, 7 }}, DIF_HISTORY, }, + { DI_TEXT, {{-1, 8 }, {0, 8 }}, DIF_SEPARATOR | State, }, + { DI_TEXT, {{5, 9 }, {0, 9 }}, State, msg(lng::MEditMenuCommands), }, #ifdef PROJECT_DI_MEMOEDIT - { DI_MEMOEDIT, {{5, 8 }, {DLG_X-6, 17 }}, DIF_EDITPATH, }, + { DI_MEMOEDIT, {{5, 10 }, {DLG_X-6, 19 }}, DIF_EDITPATH, }, #else - { DI_EDIT, {{5, 8 }, {DLG_X-6, 8 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, - { DI_EDIT, {{5, 9 }, {DLG_X-6, 9 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, { DI_EDIT, {{5, 10 }, {DLG_X-6, 10 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, { DI_EDIT, {{5, 11 }, {DLG_X-6, 11 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, { DI_EDIT, {{5, 12 }, {DLG_X-6, 12 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, @@ -1060,6 +1275,8 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: { DI_EDIT, {{5, 15 }, {DLG_X-6, 15 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, { DI_EDIT, {{5, 16 }, {DLG_X-6, 16 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, { DI_EDIT, {{5, 17 }, {DLG_X-6, 17 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, + { DI_EDIT, {{5, 18 }, {DLG_X-6, 18 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, + { DI_EDIT, {{5, 19 }, {DLG_X-6, 19 }}, DIF_EDITPATH | DIF_EDITPATHEXEC | DIF_EDITOR | State, }, #endif { DI_TEXT, {{-1, DLG_Y-4}, {0, DLG_Y-4}}, DIF_SEPARATOR, }, { DI_BUTTON, {{0, DLG_Y-3}, {0, DLG_Y-3}}, DIF_CENTERGROUP | DIF_DEFAULTBUTTON, msg(lng::MOk), }, @@ -1070,10 +1287,29 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: enum {DI_EDIT_COUNT=EM_SEPARATOR2-EM_COMMANDS_TEXT-1}; #endif + FarListItem ShowIfItems[std::size(MenuShowIfOrder)]{}; + FarList ComboList{ sizeof(ComboList), std::size(ShowIfItems), ShowIfItems }; + + for (size_t i = 0; i != std::size(ShowIfItems); ++i) + ShowIfItems[i].Text = msg(ShowIfTitleId(MenuShowIfOrder[i])).c_str(); + + const auto InitialShowIf = Create? MenuItemShowIf::Always : MenuItem->ShowIf; + ShowIfItems[ShowIfToIndex(InitialShowIf)].Flags |= LIF_SELECTED; + + EditDlg[EM_SHOWIF_COMBO].ListItems = &ComboList; + EditDlg[EM_MASK_EDIT].strHistory = L"Masks"sv; + + if (!ShowIfUsesMask(InitialShowIf)) + { + EditDlg[EM_MASK_TEXT].Flags |= DIF_DISABLE; + EditDlg[EM_MASK_EDIT].Flags |= DIF_DISABLE; + } + if (!Create) { - EditDlg[EM_HOTKEY_EDIT].strData = (*MenuItem)->strHotKey; - EditDlg[EM_LABEL_EDIT].strData = (*MenuItem)->strLabel; + EditDlg[EM_HOTKEY_EDIT].strData = MenuItem->strHotKey; + EditDlg[EM_LABEL_EDIT].strData = MenuItem->strLabel; + EditDlg[EM_MASK_EDIT].strData = MenuItem->strMask; #if defined(PROJECT_DI_MEMOEDIT) /* ... @@ -1090,7 +1326,7 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: EditDlg[EM_MEMOEDIT].strData = strBuffer; //??? #else int CommandNumber=0; - for (const auto& i: (*MenuItem)->Commands) + for (const auto& i: MenuItem->Commands) { EditDlg[EM_EDITLINE_0+CommandNumber].strData = i; if (++CommandNumber == DI_EDIT_COUNT) @@ -1109,17 +1345,17 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: return false; m_MenuModified = true; - auto NewItemIterator = Menu.end(); if (Create) - { - NewItemIterator = Menu.emplace(MenuItem? *MenuItem : Menu.begin(), UserMenuItem()); - MenuItem = &NewItemIterator; - } + MenuItem = Menu.emplace(MenuItem, UserMenuItem()); + + const auto ShowIf = MenuShowIfOrder[std::clamp(static_cast(EditDlg[EM_SHOWIF_COMBO].ListPos), 0, static_cast(std::size(MenuShowIfOrder)) - 1)]; - (*MenuItem)->strHotKey = EditDlg[EM_HOTKEY_EDIT].strData; - (*MenuItem)->strLabel = EditDlg[EM_LABEL_EDIT].strData; - (*MenuItem)->Submenu = SubMenu; + MenuItem->strHotKey = EditDlg[EM_HOTKEY_EDIT].strData; + MenuItem->strLabel = EditDlg[EM_LABEL_EDIT].strData; + MenuItem->ShowIf = ShowIf; + MenuItem->strMask = ShowIfUsesMask(ShowIf)? EditDlg[EM_MASK_EDIT].strData : string{}; + MenuItem->Submenu = SubMenu; if (!SubMenu) { @@ -1138,14 +1374,14 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: CommandNumber = i + 1; } - (*MenuItem)->Commands.clear(); + MenuItem->Commands.clear(); for (const auto i: std::views::iota(0uz, static_cast(DI_EDIT_COUNT))) { if (static_cast(i) >= CommandNumber) break; - (*MenuItem)->Commands.emplace_back(EditDlg[i + EM_EDITLINE_0].strData); + MenuItem->Commands.emplace_back(EditDlg[i + EM_EDITLINE_0].strData); } #endif } From 186bc91dba97c376b5690135bc6f257df925fbf6 Mon Sep 17 00:00:00 2001 From: Oleg Fedko Date: Thu, 8 Oct 2026 17:35:17 +0300 Subject: [PATCH 2/3] "@ShowIf:" changed to "::ShowIf:" --- far/usermenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/far/usermenu.cpp b/far/usermenu.cpp index 59abe681dd..fec473f51c 100644 --- a/far/usermenu.cpp +++ b/far/usermenu.cpp @@ -165,7 +165,7 @@ static int PrepareHotKey(string &strHotKey) } static const auto LocalMenuFileName = L"FarMenu.ini"sv; -static const auto MenuShowIfPrefix = L"@ShowIf:"sv; +static const auto MenuShowIfPrefix = L"::ShowIf:"sv; static const auto MenuSeparator = L"--"sv; enum class MenuItemShowIf From 76afb8ba518ae85f0e8b283b4b3743bf9ff99e15 Mon Sep 17 00:00:00 2001 From: Oleg Fedko Date: Tue, 13 Oct 2026 18:12:03 +0300 Subject: [PATCH 3/3] wip: user menu Show condition - review fixes (pre-upstream-merge) --- CODING.md | 103 +++++++++ .../meta/dialogapi/controls/di_combobox.html | 6 +- .../meta/dialogapi/dmsg/dn_editchange.html | 5 + .../meta/dialogapi/dmsg/dn_listchange.html | 4 +- .../meta/dialogapi/flags/dif_history.html | 4 + .../meta/dialogapi/controls/di_combobox.html | 5 + .../meta/dialogapi/dmsg/dn_editchange.html | 5 + .../meta/dialogapi/dmsg/dn_listchange.html | 3 + .../meta/dialogapi/flags/dif_history.html | 4 + far/FarCze.hlf.m4 | 6 +- far/FarEng.hlf.m4 | 4 +- far/FarGer.hlf.m4 | 6 +- far/FarHun.hlf.m4 | 6 +- far/FarPol.hlf.m4 | 4 +- far/FarRus.hlf.m4 | 11 +- far/FarSky.hlf.m4 | 6 +- far/FarUkr.hlf.m4 | 11 +- far/changelog | 17 +- far/farlang.templ.m4 | 38 ++-- far/usermenu.cpp | 195 +++++++++--------- far/vbuild.m4 | 2 +- 21 files changed, 297 insertions(+), 148 deletions(-) create mode 100644 CODING.md diff --git a/CODING.md b/CODING.md new file mode 100644 index 0000000000..4d25be77f4 --- /dev/null +++ b/CODING.md @@ -0,0 +1,103 @@ +# Coding conventions + +The code in the repository is the source of truth: match the surrounding code +first. This file collects the conventions that are easy to get wrong. + +## Source file layout + +### Include block + +Keep the include block structured with the standard section comments, in this +order: + +1. `// BUGBUG` — `#include "platform.headers.hpp"`, for the files that need the + Windows headers pulled in first. This is a known wart, not a pattern to copy. +2. `// Self` — the file's own header (`.cpp` files only; headers have no `Self`). +3. `// Internal` — other Far core headers. +4. `// Platform` — the `platform.*` OS abstraction headers. +5. `// Common` — `common.hpp` first, then the `common/...` utility headers + (`common/from_string.hpp`, `common/scope_exit.hpp`, ...). +6. `// External` — third-party libraries and their Far wrappers (`format.hpp` + for fmt, `tinyxml.hpp`, `testing.hpp`). + +Keep the section comments even when a section is empty, and separate the block +from the code below with the `//----...` line. + +## Language features + +### Range algorithms + +Prefer the C++20 range algorithms (`std::ranges::find`, `std::ranges::any_of`, +`std::ranges::sort`, ...) over `std::begin`/`std::end` combined with the `std::` +algorithms. The codebase is standardized on `std::ranges`; the range form is +shorter, supports projections, and cannot accidentally mix iterators from +different containers: + +``` +std::ranges::find(Container, Value); // not std::find(std::begin(Container), std::end(Container), Value) +std::ranges::find(Items, Value, &Item::field); +``` + +### Enums + +#### Enumerating values + +When an enum has to be enumerated (to fill a combo box, to map the values to +config keys, to size an array, ...), add a `count` enumerator as the last item +and iterate `0 .. std::to_underlying(Type::count)`: + +``` +enum Mode +{ + first, + second, + third, + + count +}; + +for (const auto i: std::views::iota(0, std::to_underlying(Mode::count))) + use(static_cast(i)); +``` + +`count` is needed only when the values are actually enumerated; other enums +don't have to define it. + +#### switch over an enum + +Handle every enumerator explicitly and terminate the switch with +`default: std::unreachable();`. + +If only some values need special handling, let the rest share a behaviour +through `default:` (`default: break;` means "do nothing"), or merge `default` +with a fallback case: + +``` +switch (Value) +{ +default: +case Type::unknown: return Fallback; +case Type::a: return ...; +} +``` + +Use `[[fallthrough]]` when a case intentionally falls through into the next one. + +## User interface + +### Dialogs and localized labels + +Localized text has a different width in each language, so measure labels at +runtime and derive the offsets from the text width. `HiStrlen` accounts for `&` +hotkeys and double-width characters (and returns `size_t`): + +``` +EditDlg[label].X1 = EditDlg[control].X2 + 2; +EditDlg[field].X1 = EditDlg[label].X1 + static_cast(HiStrlen(msg(lng::Label))) + 1; +``` + +A control with a drop-down arrow (a combo box with a non-empty list, or an edit +with `DIF_HISTORY`) draws the arrow one column to the right of `X2`. When placing +an item immediately to the right, account for that column (`X2 + 3` keeps a +one-column gap); a control right-aligned to the dialog border needs no +adjustment, the arrow fits into the gap before the border. diff --git a/enc/enc_eng/meta/dialogapi/controls/di_combobox.html b/enc/enc_eng/meta/dialogapi/controls/di_combobox.html index 14adbc19b2..9847ac1fe7 100644 --- a/enc/enc_eng/meta/dialogapi/controls/di_combobox.html +++ b/enc/enc_eng/meta/dialogapi/controls/di_combobox.html @@ -153,7 +153,11 @@

Events

Remarks

-

+

If the combo box has a drop-down arrow (a non-empty list, or an item with +DIF_HISTORY), the arrow is drawn one column +to the right of X2, i.e. the item visually occupies X1..X2+1. +When placing another item to the right, take that column into account and leave a +gap.

diff --git a/enc/enc_eng/meta/dialogapi/dmsg/dn_editchange.html b/enc/enc_eng/meta/dialogapi/dmsg/dn_editchange.html index 0b532913d6..a0729a493c 100644 --- a/enc/enc_eng/meta/dialogapi/dmsg/dn_editchange.html +++ b/enc/enc_eng/meta/dialogapi/dmsg/dn_editchange.html @@ -64,6 +64,11 @@

Remarks

  • The DN_EDITCHANGE event will not be sent in a response to the following messages: DM_SETTEXTPTR and DM_SETTEXT. +
  • The DN_EDITCHANGE event is sent even when the item text is set to the +same value (for example, when the current item is re-selected in a +DI_COMBOBOX). To detect a real change, +compare the new text with the previous one; to detect a list position change, use +DN_LISTCHANGE. diff --git a/enc/enc_eng/meta/dialogapi/dmsg/dn_listchange.html b/enc/enc_eng/meta/dialogapi/dmsg/dn_listchange.html index 58285b6082..9eaf1707b9 100644 --- a/enc/enc_eng/meta/dialogapi/dmsg/dn_listchange.html +++ b/enc/enc_eng/meta/dialogapi/dmsg/dn_listchange.html @@ -57,7 +57,9 @@

    Controls

    Remarks

    - +Unlike DN_EDITCHANGE, the DN_LISTCHANGE +event is sent only when the list position actually changes: re-selecting the current +item leaves the position unchanged and does not trigger it.

    Example

    diff --git a/enc/enc_eng/meta/dialogapi/flags/dif_history.html b/enc/enc_eng/meta/dialogapi/flags/dif_history.html index 3dc0d34cdc..50f1e4dbcc 100644 --- a/enc/enc_eng/meta/dialogapi/flags/dif_history.html +++ b/enc/enc_eng/meta/dialogapi/flags/dif_history.html @@ -109,6 +109,10 @@

    Remarks

  • +
  • An edit control with DIF_HISTORY shows a drop-down arrow, which is drawn one + column to the right of X2 (X2+1). When placing another item immediately to the + right, take that column into account; when the edit is right-aligned to the dialog border, + the arrow fits into the gap before the border and no adjustment is needed. diff --git a/enc/enc_rus/meta/dialogapi/controls/di_combobox.html b/enc/enc_rus/meta/dialogapi/controls/di_combobox.html index 8356bea962..9bc11d70ff 100644 --- a/enc/enc_rus/meta/dialogapi/controls/di_combobox.html +++ b/enc/enc_rus/meta/dialogapi/controls/di_combobox.html @@ -156,6 +156,11 @@

    Замечания

    Ассоциированные данные уничтожаются (если было выделение по памяти)

    +

    +Если у комбобокса есть стрелка раскрытия списка (непустой список или элемент с +флагом DIF_HISTORY), стрелка рисуется на +одну колонку правее X2, т.е. элемент занимает X1..X2+1. +Размещая правее другой элемент, учитывайте эту колонку и оставляйте отступ.

    Смотрите также:
    diff --git a/enc/enc_rus/meta/dialogapi/dmsg/dn_editchange.html b/enc/enc_rus/meta/dialogapi/dmsg/dn_editchange.html index fe42d21f3a..cf4f44e63c 100644 --- a/enc/enc_rus/meta/dialogapi/dmsg/dn_editchange.html +++ b/enc/enc_rus/meta/dialogapi/dmsg/dn_editchange.html @@ -56,6 +56,11 @@

    Элементы

    Замечания

    В Dialog API нельзя принудительно изменить состояние элемента, изменяя элементы структуры FarDialogItem. +

    +Событие DN_EDITCHANGE посылается и тогда, когда элементу устанавливается то же самое +значение (например, при повторном выборе текущего элемента в DI_COMBOBOX). +Чтобы отследить реальное изменение, сравните новое значение с предыдущим; для отслеживания смены +позиции в списке используйте DN_LISTCHANGE.

    Пример

    diff --git a/enc/enc_rus/meta/dialogapi/dmsg/dn_listchange.html b/enc/enc_rus/meta/dialogapi/dmsg/dn_listchange.html index 8d69fd195d..8dc9027517 100644 --- a/enc/enc_rus/meta/dialogapi/dmsg/dn_listchange.html +++ b/enc/enc_rus/meta/dialogapi/dmsg/dn_listchange.html @@ -52,6 +52,9 @@

    Элементы

    Замечания

    +В отличие от DN_EDITCHANGE, событие DN_LISTCHANGE +посылается только при реальном изменении позиции в списке: повторный выбор текущего элемента +позицию не меняет и это событие не вызывает.

    Пример

    diff --git a/enc/enc_rus/meta/dialogapi/flags/dif_history.html b/enc/enc_rus/meta/dialogapi/flags/dif_history.html index 02ceaacff8..c5dc6e7ed6 100644 --- a/enc/enc_rus/meta/dialogapi/flags/dif_history.html +++ b/enc/enc_rus/meta/dialogapi/flags/dif_history.html @@ -58,6 +58,10 @@

    Замечания

    L"NewFolder"создание каталогов
  • +
  • Строка ввода с флагом DIF_HISTORY имеет стрелку раскрытия списка, которая + рисуется на одну колонку правее X2 (X2+1). Если справа вплотную размещается + другой элемент, эту колонку нужно учитывать; если строка выровнена по правой границе, + стрелка помещается в промежутке перед границей и ничего подгонять не нужно. diff --git a/far/FarCze.hlf.m4 b/far/FarCze.hlf.m4 index 48a0ba6552..cf90279f8c 100644 --- a/far/FarCze.hlf.m4 +++ b/far/FarCze.hlf.m4 @@ -2249,15 +2249,15 @@ přiřazenou danému příkazu. podmenu, nebo položku v menu můžete klávesou Ins. Upravit existující podmenu, nebo položku v menu můžete klávesou F4. Pokud chcete menu upravovat v podobě textového souboru stiskněte Alt+F4. - Stiskněte #Ctrl+H# pro zobrazení položek menu skrytých podmínkou "Zobrazit, pokud". + Stiskněte #Ctrl+H# pro zobrazení položek menu skrytých polem "Zobrazit". V uživatelském menu je možné použít jako zkratkové klávesy číslice, písmena a funkční klávezy (#F1#…#F24#). Pokud jsou použity #F1# nebo #F4#, je jejich původní funkce v menu potlačena. Nicméně, můžete pro úpravu menu použít #Shift+F4#. - Podmínka #Zobrazit, pokud# umožňuje skrýt položku menu nevhodnou v aktuální -situaci: #Vždy#, #Existuje výběr#, #Aktuální je soubor# nebo #Aktuální je složka#. + Pole #Zobrazit# zobrazí položku menu pouze tehdy, když je v aktuální +situaci vhodná: #Vždy#, #Existuje výběr#, #Aktuální je soubor# nebo #Aktuální je složka#. Pro poslední dvě lze zadat volitelnou ~masku souboru~@FileMasks@; položka se zobrazí, pouze pokud název objektu pod kurzorem odpovídá masce. diff --git a/far/FarEng.hlf.m4 b/far/FarEng.hlf.m4 index 6ac0681814..6a762fbd5d 100644 --- a/far/FarEng.hlf.m4 +++ b/far/FarEng.hlf.m4 @@ -2215,13 +2215,13 @@ You can also press the hot key assigned to the required menu item. You can delete a submenu or menu item with the #Del# key, insert new submenu or menu item with the #Ins# key or edit an existing submenu or menu item with the #F4# key. Press #Alt+F4# to edit the menu in text file form. - Press #Ctrl+H# to show menu items hidden by the "Show if" condition. + Press #Ctrl+H# to show menu items hidden by the #Show# field. It is possible to use digits, letters and function keys (#F1#…#F24#) as hot keys in user menu. If #F1# or #F4# is used, its original function in user menu is overridden. However, you still can use #Shift+F4# to edit the menu. - The #Show if# condition allows you to hide a menu item that is inapplicable in + The #Show# field makes a menu item visible only when it is applicable in the current situation: #Always#, #Has selection#, #Current is a file# or #Current is a folder#. For the last two an optional ~file mask~@FileMasks@ can be specified; the item is shown only when the name of the object under the diff --git a/far/FarGer.hlf.m4 b/far/FarGer.hlf.m4 index 620d9c7a1a..22a75f55d1 100644 --- a/far/FarGer.hlf.m4 +++ b/far/FarGer.hlf.m4 @@ -2289,14 +2289,14 @@ must switch to file mode with #Alt+F4# key. Menüpunkte oder Untermenüs können durch die Taste Entf gelöscht werden. Durch Einfg können neue Menüpunkte oder Untermenüs angelegt werden. Durch #F4# können existierende Menüpunkte und Untermenüs bearbeitet werden. Mit Alt+F4 kann ein Menü in Form einer Textdatei bearbeitet werden. - Mit #Strg+H# werden durch die Bedingung "Anzeigen wenn" ausgeblendete Menüeinträge angezeigt. + Mit #Strg+H# werden durch das Feld "Anzeigen" ausgeblendete Menüeinträge angezeigt. Es ist möglich, Zahlen, Buchstaben und Funktionstasten (#F1#…#F24#) als Hotkeys in Benutzermenüs einzusetzen. Bei Benutzung von #F1# oder #F4# wird deren originale Funktion im Benutzermenü übergangen. #Umsch+F4# können Sie trotzdem weiterhin als Bearbeitungsfunktion nutzen. - Die Bedingung #Anzeigen wenn# ermöglicht das Ausblenden eines Menüeintrags, der -in der aktuellen Situation nicht zutrifft: #Immer#, #Auswahl vorhanden#, + Das Feld #Anzeigen# macht einen Menüeintrag nur dann sichtbar, wenn er +in der aktuellen Situation zutrifft: #Immer#, #Auswahl vorhanden#, #Aktuell ist eine Datei# oder #Aktuell ist ein Ordner#. Für die letzten beiden kann eine optionale ~Dateimaske~@FileMasks@ angegeben werden; der Eintrag wird nur angezeigt, wenn der Name des Objekts unter dem Cursor der Maske entspricht. diff --git a/far/FarHun.hlf.m4 b/far/FarHun.hlf.m4 index ecf628edba..89b4633f48 100644 --- a/far/FarHun.hlf.m4 +++ b/far/FarHun.hlf.m4 @@ -2292,7 +2292,7 @@ gyorsbillentyű segítségével. Az #Ins# billentyűvel új menüelemeket, almenüket hozhatunk létre, az #F4# bilentyűvel szerkeszthetjük a jellemzőiket, a #Del#-lel pedig törölhetjük a menü elemeit. #Alt+F4#-gyel a menüket szövegfájl formátumban szerkeszthetjük. - A #Ctrl+H# megmutatja a "Megjelenítés, ha" feltétel miatt elrejtett menüpontokat. + A #Ctrl+H# megmutatja a "Megjelenítés" mező miatt elrejtett menüpontokat. A felhasználói menü elemeinek gyorsbillentyűjeként megadhatunk számokat, betűket vagy funkcióbillentyűket (#F1#…#F24#), utóbbiakat csak beírás útján @@ -2300,8 +2300,8 @@ fogadja el. Ha az #F1# vagy #F4# eredetileg más művelethez volt rendelve, a felhasználói menü felülbírálja eredeti funkcióikat, bár a #Shift+F4#-gyel ezután is szerkeszthetjük a menüt. - A #Megjelenítés, ha# feltétel lehetővé teszi az adott helyzetben nem illő -menüpont elrejtését: #Mindig#, #Van kijelölés#, #A jelenlegi fájl# vagy + A #Megjelenítés# mező csak akkor teszi láthatóvá a menüpontot, ha az illik az +adott helyzethez: #Mindig#, #Van kijelölés#, #A jelenlegi fájl# vagy #A jelenlegi mappa#. Az utolsó kettőhöz opcionális ~fájlmaszk~@FileMasks@ adható meg; a menüpont csak akkor jelenik meg, ha a kurzor alatti objektum neve illik a maszkra. diff --git a/far/FarPol.hlf.m4 b/far/FarPol.hlf.m4 index 2cf8a91c06..8379a84b61 100644 --- a/far/FarPol.hlf.m4 +++ b/far/FarPol.hlf.m4 @@ -2216,13 +2216,13 @@ menu. podmenu lub pozycję menu za pomocą klawisza #Ins# lub edytować istniejące podmenu lub pozycję menu za pomocą klawisza #F4#. Należy wcisnąć klawisze #Alt+F4#, aby edytować menu w formie pliku tekstowego. - Naciśnij #Ctrl+H#, aby pokazać pozycje menu ukryte przez warunek "Pokaż, jeżeli". + Naciśnij #Ctrl+H#, aby pokazać pozycje menu ukryte przez pole "Pokaż". Do klawiszy skrótu można użyć cyfr, liter i klawiszy funkcyjnych (#F1#…#F24#). Jeżeli użyto klawiszy #F1# lub #F4# - to zamieniona zostanie oryginalna funkcja tych klawiszy. Mimo to, skrót #Shift+F4# nadal może być użyty do edycji menu. - Warunek #Pokaż, jeżeli# pozwala ukryć pozycję menu niepasującą do bieżącej + Pole #Pokaż# czyni pozycję menu widoczną tylko wtedy, gdy pasuje ona do bieżącej sytuacji: #Zawsze#, #Jest zaznaczenie#, #Bieżący to plik# lub #Bieżący to folder#. Dla dwóch ostatnich można podać opcjonalną ~maskę pliku~@FileMasks@; pozycja jest pokazywana tylko wtedy, gdy nazwa obiektu pod kursorem pasuje do maski. diff --git a/far/FarRus.hlf.m4 b/far/FarRus.hlf.m4 index 4b58797a4f..66692f7813 100644 --- a/far/FarRus.hlf.m4 +++ b/far/FarRus.hlf.m4 @@ -2266,17 +2266,18 @@ $ #Меню пользователя# вставить новое вложенное меню или пункт меню с помощью #Ins# и редактировать существующее вложенное меню или пункт меню с помощью #F4#. Нажмите #Alt+F4# для редактирования меню в виде текстового файла. - Нажмите #Ctrl+H#, чтобы показать пункты меню, скрытые условием "Показывать если". + Нажмите #Ctrl+H#, чтобы показать пункты меню, скрытые полем "Показывать". В качестве горячих клавиш для обращения к пунктам меню могут использоваться цифры, буквы и функциональные клавиши (#F1#…#F24#). Если использованы #F1# и #F4#, их первоначальные функции теряются. В этом случае для редактирования меню может применяться #Shift+F4#. - Условие #Показывать если# позволяет скрыть пункт меню, неприменимый в текущей -ситуации: #Всегда#, #Есть выделение#, #Текущий — файл# или #Текущий — папка#. -Для последних двух можно указать необязательную ~маску файла~@FileMasks@; пункт -показывается, только если имя объекта под курсором совпадает с маской. + Поле #Показывать# делает пункт меню видимым, только когда он применим в +текущей ситуации: #Всегда#, #Есть выделение#, #Текущий - файл# или +#Текущий - папка#. Для последних двух можно указать необязательную +~маску файла~@FileMasks@; пункт показывается, только если имя объекта под +курсором совпадает с маской. При редактировании или создании пункта меню нужно ввести заголовок пункта, который будет отображаться в меню, последовательность команд, выполняющихся при diff --git a/far/FarSky.hlf.m4 b/far/FarSky.hlf.m4 index 0dc49729c3..026793a929 100644 --- a/far/FarSky.hlf.m4 +++ b/far/FarSky.hlf.m4 @@ -2211,14 +2211,14 @@ názov a stlačením klávesy Enter alebo jemu priradenou klávesovou skratkou. Podmenu alebo položku menu môžete zrušiť klávesom #Del#, nové menu alebo položku môžete vložiť klávesom #Ins# a upraviť existujúce menu či položku možno klávesom #F4#. Po stlačení #Alt+F4# budete môcť menu editovať v textovej forme. - Stlačením #Ctrl+H# zobrazíte položky menu skryté podmienkou "Zobraziť, ak". + Stlačením #Ctrl+H# zobrazíte položky menu skryté poľom "Zobraziť". Ako klávesové skratky možno v menu použiť číslice, písmená a funkčné klávesy (#F1#…#F24#). Ak použijete #F1# alebo #F4#, znemožníte tým ich originálnu funkciu. Každopádne na úpravu môžete ešte vždy použiť kombináciu #Shift+F4#. - Podmienka #Zobraziť, ak# umožňuje skryť položku menu nevhodnú v aktuálnej -situácii: #Vždy#, #Existuje výber#, #Aktuálny je súbor# alebo + Pole #Zobraziť# zobrazí položku menu len vtedy, keď je v aktuálnej +situácii vhodná: #Vždy#, #Existuje výber#, #Aktuálny je súbor# alebo #Aktuálny je priečinok#. Pre posledné dve možno zadať voliteľnú ~masku súboru~@FileMasks@; položka sa zobrazí, len ak názov objektu pod kurzorom zodpovedá maske. diff --git a/far/FarUkr.hlf.m4 b/far/FarUkr.hlf.m4 index 4b4fb3953b..f27b70d38c 100644 --- a/far/FarUkr.hlf.m4 +++ b/far/FarUkr.hlf.m4 @@ -2266,17 +2266,18 @@ $ #Меню користувача# за допомогою #Ins# і редагувати існуюче вкладене меню або пункт меню за допомогою #F4#. Натисніть #Alt+F4# для редагування меню у вигляді текстового файла. - Натисніть #Ctrl+H#, щоб показати пункти меню, приховані умовою "Показувати якщо". + Натисніть #Ctrl+H#, щоб показати пункти меню, приховані полем "Показувати". У якості гарячих клавіш для звернення до пунктів меню можуть використовуватися цифри, літери і функціональні клавіші (#F1#…#F24#). Якщо використані #F1# і #F4#, їх первинні функції втрачаються. У цьому випадку для редагування меню може застосовуватися Shift+F4. - Умова #Показувати якщо# дозволяє приховати пункт меню, непридатний у поточній -ситуації: #Завжди#, #Є виділення#, #Поточний — файл# або #Поточний — папка#. -Для останніх двох можна вказати необов'язкову ~маску файла~@FileMasks@; пункт -показується, тільки якщо ім'я об'єкта під курсором збігається з маскою. + Поле #Показувати# робить пункт меню видимим, тільки коли він придатний у +поточній ситуації: #Завжди#, #Є виділення#, #Поточний - файл# або +#Поточний - папка#. Для останніх двох можна вказати необов'язкову +~маску файла~@FileMasks@; пункт показується, тільки якщо ім'я об'єкта під +курсором збігається з маскою. При редагуванні або створенні пункту меню треба ввести гарячу клавішу для швидкого доступу до цього пункту, заголовок пункту, diff --git a/far/changelog b/far/changelog index abb70dd98d..aad15efb16 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,13 @@ +-------------------------------------------------------------------------------- +olegfedko 2026-10-13 17:00:00+03:00 - build 6742 + +1. User menu items can be hidden by a "Show" condition: + * always + * has selection + * current is a file (with an optional file mask) + * current is a folder (with an optional file mask) + Use Ctrl+H in the menu to show or hide filtered items. + -------------------------------------------------------------------------------- drkns 2026-10-02 17:19:44+01:00 - build 6741 @@ -13,13 +23,6 @@ BitoLabs 2026-09-26 19:58:45+01:00 - build 6740 * When passive panel is in non-file mode (Info, etc), SameFolder had no effect * When passive panel is hidden (Ctrl+F1/F2), SameFolder did not un-hide it --------------------------------------------------------------------------------- -olegfedko 2026-09-25 20:00:00+03:00 - -1. User menu items can be hidden by a "Show if" condition: always, has selection, current is a file or current is a folder (with an optional file mask for the last two). Use Ctrl+H in the menu editor to show or hide filtered items. - -2. User menu: keep selection on the item when moving it with Ctrl+Up/Ctrl+Down. - -------------------------------------------------------------------------------- drkns 2026-09-25 18:58:09+01:00 - build 6739 diff --git a/far/farlang.templ.m4 b/far/farlang.templ.m4 index d29d16a681..09dcb8bb10 100644 --- a/far/farlang.templ.m4 +++ b/far/farlang.templ.m4 @@ -23033,19 +23033,19 @@ MEditMenuLabel "&Volume:" MEditMenuShowIf -"&Показывать если:" -"&Show if:" -"Zob&razit, pokud:" -"&Anzeigen wenn:" -"&Megjelenítés, ha:" -"Poka&ż, jeżeli:" -"&Mostrar si:" -"&Zobraziť, ak:" -"&Mostra se:" -"&Показувати якщо:" -"Пака&зваць, калі:" -"&Rodyti, jei:" -"&Mostrar se:" +"&Показывать:" +"&Show:" +"Zob&razit:" +"&Anzeigen:" +"&Megjelenítés:" +"Poka&ż:" +"&Mostrar:" +"&Zobraziť:" +"&Mostra:" +"&Показувати:" +"Пака&зваць:" +"&Rodyti:" +"&Mostrar:" MEditMenuMask "Ма&ска:" @@ -23093,7 +23093,7 @@ MShowIfHasSelection "Há seleção" MShowIfCurrentFile -"Текущий — файл" +"Текущий - файл" "Current is a file" "Aktuální je soubor" "Aktuell ist eine Datei" @@ -23102,13 +23102,13 @@ MShowIfCurrentFile "Actual es un archivo" "Aktuálny je súbor" "L'elemento corrente è un file" -"Поточний — файл" -"Бягучы — файл" +"Поточний - файл" +"Бягучы - файл" "Dabartinis yra failas" "Atual é um arquivo" MShowIfCurrentDir -"Текущий — папка" +"Текущий - папка" "Current is a folder" "Aktuální je složka" "Aktuell ist ein Ordner" @@ -23117,8 +23117,8 @@ MShowIfCurrentDir "Actual es una carpeta" "Aktuálny je priečinok" "L'elemento corrente è una cartella" -"Поточний — папка" -"Бягучы — тэчка" +"Поточний - папка" +"Бягучы - каталог" "Dabartinis yra aplankas" "Atual é uma pasta" diff --git a/far/usermenu.cpp b/far/usermenu.cpp index fec473f51c..b99df75ebb 100644 --- a/far/usermenu.cpp +++ b/far/usermenu.cpp @@ -68,7 +68,6 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "codepage.hpp" #include "strmix.hpp" #include "filemasks.hpp" -#include "common.hpp" // Platform: #include "platform.hpp" @@ -76,6 +75,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "platform.fs.hpp" // Common: +#include "common.hpp" #include "common/from_string.hpp" #include "common/scope_exit.hpp" @@ -112,7 +112,7 @@ class UserMenu : noncopyable private: void ProcessUserMenu(bool ChooseMenuType, string_view MenuFileName); bool DeleteMenuRecord(menu_container& Menu, const menu_container::iterator& MenuItem) const; - bool EditMenu(menu_container& Menu, menu_container::iterator& MenuItem, bool Create); + bool EditMenu(menu_container& Menu, menu_container::iterator* MenuItem, bool Create); int ProcessSingleMenu(menu_container& Menu, menu_container& MenuRoot, string_view MenuFileName, const string& Title); void SaveMenu(string_view MenuFileName) const; intptr_t EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, void* Param2); @@ -168,58 +168,53 @@ static const auto LocalMenuFileName = L"FarMenu.ini"sv; static const auto MenuShowIfPrefix = L"::ShowIf:"sv; static const auto MenuSeparator = L"--"sv; -enum class MenuItemShowIf +enum class MenuItemVisibility { Always, HasSelection, CurrentFile, CurrentDir, -}; -static constexpr MenuItemShowIf MenuShowIfOrder[] -{ - MenuItemShowIf::Always, - MenuItemShowIf::HasSelection, - MenuItemShowIf::CurrentFile, - MenuItemShowIf::CurrentDir, + count }; -static bool ShowIfUsesMask(MenuItemShowIf const ShowIf) +static bool VisibilityHasMask(MenuItemVisibility const Visibility) { - return ShowIf == MenuItemShowIf::CurrentFile || ShowIf == MenuItemShowIf::CurrentDir; + return Visibility == MenuItemVisibility::CurrentFile || Visibility == MenuItemVisibility::CurrentDir; } -static lng ShowIfTitleId(MenuItemShowIf const ShowIf) +static lng VisibilityTitleId(MenuItemVisibility const Visibility) { - switch (ShowIf) + switch (Visibility) { - case MenuItemShowIf::HasSelection: return lng::MShowIfHasSelection; - case MenuItemShowIf::CurrentFile: return lng::MShowIfCurrentFile; - case MenuItemShowIf::CurrentDir: return lng::MShowIfCurrentDir; - case MenuItemShowIf::Always: - default: return lng::MShowIfAlways; + case MenuItemVisibility::Always: return lng::MShowIfAlways; + case MenuItemVisibility::HasSelection: return lng::MShowIfHasSelection; + case MenuItemVisibility::CurrentFile: return lng::MShowIfCurrentFile; + case MenuItemVisibility::CurrentDir: return lng::MShowIfCurrentDir; + default: std::unreachable(); } } -static string_view ShowIfToToken(MenuItemShowIf const ShowIf) +static string_view VisibilityToToken(MenuItemVisibility const Visibility) { - switch (ShowIf) + switch (Visibility) { - case MenuItemShowIf::HasSelection: return L"HasSelection"sv; - case MenuItemShowIf::CurrentFile: return L"CurrentFile"sv; - case MenuItemShowIf::CurrentDir: return L"CurrentDir"sv; - case MenuItemShowIf::Always: - default: return L"Always"sv; + case MenuItemVisibility::Always: return L"Always"sv; + case MenuItemVisibility::HasSelection: return L"HasSelection"sv; + case MenuItemVisibility::CurrentFile: return L"CurrentFile"sv; + case MenuItemVisibility::CurrentDir: return L"CurrentDir"sv; + default: std::unreachable(); } } -static bool TokenToShowIf(string_view const Token, MenuItemShowIf& ShowIf) +static bool TokenToVisibility(string_view const Token, MenuItemVisibility& Visibility) { - for (const auto Value: MenuShowIfOrder) + for (const auto Value: std::views::iota(0, std::to_underlying(MenuItemVisibility::count))) { - if (equal_icase(Token, ShowIfToToken(Value))) + const auto Candidate = static_cast(Value); + if (equal_icase(Token, VisibilityToToken(Candidate))) { - ShowIf = Value; + Visibility = Candidate; return true; } } @@ -227,16 +222,16 @@ static bool TokenToShowIf(string_view const Token, MenuItemShowIf& ShowIf) return false; } -static int ShowIfToIndex(MenuItemShowIf const ShowIf) +[[nodiscard]] static MenuItemVisibility VisibilityFromIndex(int const Index) { - const auto Found = std::find(std::begin(MenuShowIfOrder), std::end(MenuShowIfOrder), ShowIf); - return Found == std::end(MenuShowIfOrder)? 0 : static_cast(Found - std::begin(MenuShowIfOrder)); + const auto Valid = in_closed_range(0, Index, std::to_underlying(MenuItemVisibility::count) - 1); + assert(Valid); + return Valid? static_cast(Index) : MenuItemVisibility::Always; } -struct MenuFilterContext +struct MenuContext { bool HasSelection{}; - bool HasCurrent{}; bool CurrentIsDir{}; string strCurrentName; }; @@ -245,22 +240,22 @@ struct UserMenu::UserMenuItem { string strHotKey; string strLabel; - MenuItemShowIf ShowIf{ MenuItemShowIf::Always }; + MenuItemVisibility Visibility{ MenuItemVisibility::Always }; string strMask; std::list Commands; bool Submenu{}; menu_container Menu; }; -static bool ItemVisible(MenuItemShowIf const ShowIf, string_view const Mask, MenuFilterContext const& Ctx, bool const ShowAllItems) +static bool IsItemVisible(MenuItemVisibility const Visibility, string_view const Mask, MenuContext const& Ctx, bool const ShowAllItems) { - if (ShowAllItems || ShowIf == MenuItemShowIf::Always) + if (ShowAllItems || Visibility == MenuItemVisibility::Always) return true; - if (ShowIf == MenuItemShowIf::HasSelection) + if (Visibility == MenuItemVisibility::HasSelection) return Ctx.HasSelection; - if (!Ctx.HasCurrent || Ctx.CurrentIsDir != (ShowIf == MenuItemShowIf::CurrentDir)) + if (Ctx.strCurrentName.empty() || Ctx.CurrentIsDir != (Visibility == MenuItemVisibility::CurrentDir)) return false; if (Mask.empty()) @@ -276,15 +271,15 @@ static bool ItemVisible(MenuItemShowIf const ShowIf, string_view const Mask, Men static void AdjustSelectedItemForFilter( UserMenu::menu_container& Menu, UserMenu::menu_container::iterator& SelectedItem, - MenuFilterContext const& Ctx, + MenuContext const& Ctx, bool const ShowAllItems) { - if (SelectedItem != Menu.end() && ItemVisible(SelectedItem->ShowIf, SelectedItem->strMask, Ctx, ShowAllItems)) + if (SelectedItem != Menu.end() && IsItemVisible(SelectedItem->Visibility, SelectedItem->strMask, Ctx, ShowAllItems)) return; for (auto i = Menu.begin(); i != Menu.end(); ++i) { - if (ItemVisible(i->ShowIf, i->strMask, Ctx, ShowAllItems)) + if (IsItemVisible(i->Visibility, i->strMask, Ctx, ShowAllItems)) { SelectedItem = i; return; @@ -306,11 +301,11 @@ static string SerializeMenu(const UserMenu::menu_container& Menu) const string Padding(HotkeyStr.size(), L' '); - if (i.ShowIf != MenuItemShowIf::Always) + if (i.Visibility != MenuItemVisibility::Always) { - append(Result, Padding, MenuShowIfPrefix, L' ', ShowIfToToken(i.ShowIf)); + append(Result, Padding, MenuShowIfPrefix, L' ', VisibilityToToken(i.Visibility)); - if (ShowIfUsesMask(i.ShowIf) && !i.strMask.empty()) + if (VisibilityHasMask(i.Visibility) && !i.strMask.empty()) append(Result, L' ', i.strMask); append(Result, Eol); @@ -391,11 +386,11 @@ static void ParseMenu(UserMenu::menu_container& Menu, std::ranges::subrangeShowIf = ShowIf; + MenuItem->Visibility = Visibility; - if (ShowIfUsesMask(ShowIf) && SpacePos != string_view::npos) + if (VisibilityHasMask(Visibility) && SpacePos != string_view::npos) MenuItem->strMask = trim_left(Rest.substr(SpacePos + 1)); } } @@ -692,7 +687,7 @@ static void FillUserMenu( UserMenu::menu_container::iterator& SelectedItem, fkey_to_pos_map& FuncPos, const subst_context& SubstContext, - MenuFilterContext const& FilterCtx, + MenuContext const& Ctx, bool const ShowAllItems) { SCOPED_ACTION(Dialog::suppress_redraw)(&FarUserMenu); @@ -703,9 +698,14 @@ static void FillUserMenu( while (SelectedItem != Menu.end() && SelectedItem->strHotKey == MenuSeparator) ++SelectedItem; + bool HasFilteredItems = false; + FOR_RANGE(Menu, MenuItem) { - if (!ItemVisible(MenuItem->ShowIf, MenuItem->strMask, FilterCtx, ShowAllItems)) + if (MenuItem->Visibility != MenuItemVisibility::Always) + HasFilteredItems = true; + + if (!IsItemVisible(MenuItem->Visibility, MenuItem->strMask, Ctx, ShowAllItems)) continue; menu_item_ex FarUserMenuItem; @@ -747,6 +747,10 @@ static void FillUserMenu( FuncPos[FuncNum-1]=ItemPos; } } + + FarUserMenu.SetBottomTitle(HasFilteredItems? + KeysToLocalizedText(KEY_INS, KEY_DEL, KEY_CTRLH, KEY_F4, KEY_ALTF4, KEY_CTRLUP, KEY_CTRLDOWN) : + KeysToLocalizedText(KEY_INS, KEY_DEL, KEY_F4, KEY_ALTF4, KEY_CTRLUP, KEY_CTRLDOWN)); } // обработка единичного меню @@ -764,25 +768,24 @@ int UserMenu::ProcessSingleMenu(std::list& Menu, std::listGetRealSelCount() != 0; + MenuContext MenuCtx; + MenuCtx.HasSelection = ActivePanel->GetRealSelCount() != 0; os::fs::attributes CurrentAttributes{}; - FilterCtx.HasCurrent = ActivePanel->GetFileName(FilterCtx.strCurrentName, ActivePanel->GetCurrentPos(), CurrentAttributes); - FilterCtx.CurrentIsDir = FilterCtx.HasCurrent && os::fs::is_directory(CurrentAttributes); + ActivePanel->GetFileName(MenuCtx.strCurrentName, ActivePanel->GetCurrentPos(), CurrentAttributes); + MenuCtx.CurrentIsDir = os::fs::is_directory(CurrentAttributes); const auto UserMenu = VMenu2::create(Title, {}, ScrY - 4); UserMenu->SetMenuFlags(VMENU_WRAPMODE | VMENU_NOMERGEBORDER); UserMenu->SetHelp(L"UserMenu"sv); UserMenu->SetPosition({ -1, -1, 0, 0 }); - UserMenu->SetBottomTitle(KeysToLocalizedText(KEY_INS, KEY_DEL, KEY_CTRLH, KEY_F4, KEY_ALTF4, KEY_CTRLUP, KEY_CTRLDOWN)); UserMenu->SetMacroMode(MACROAREA_USERMENU); int ReturnCode=1; fkey_to_pos_map FuncPos; - AdjustSelectedItemForFilter(Menu, SelectedItem, FilterCtx, ShowAllItems); - FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, FilterCtx, ShowAllItems); + AdjustSelectedItemForFilter(Menu, SelectedItem, MenuCtx, ShowAllItems); + FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, MenuCtx, ShowAllItems); const auto ExitCode = UserMenu->Run([&](const Manager::Key& RawKey) { @@ -839,8 +842,8 @@ int UserMenu::ProcessSingleMenu(std::list& Menu, std::list& Menu, std::list& Menu, std::listShowIf, SelectedItem->strMask, FilterCtx, false)) + if (!IsItemVisible(SelectedItem->Visibility, SelectedItem->strMask, MenuCtx, false)) ShowAllItems = true; // BUGBUG update dynamically instead of full refill - FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, FilterCtx, ShowAllItems); + FillUserMenu(*UserMenu, Menu, SelectedItem, FuncPos, Context, MenuCtx, ShowAllItems); } break; } @@ -910,7 +913,7 @@ int UserMenu::ProcessSingleMenu(std::list& Menu, std::list= EM_EDITLINE_0 && Param1 <= EM_EDITLINE_9) || any_of(Param1, EM_MASK_EDIT, EM_SHOWIF_COMBO)) + if ((Param1 >= EM_EDITLINE_0 && Param1 <= EM_EDITLINE_9) || Param1 == EM_MASK_EDIT) #endif m_ItemChanged = true; break; @@ -1128,8 +1131,7 @@ intptr_t UserMenu::EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, v case DN_LISTCHANGE: if (Param1 == EM_SHOWIF_COMBO) { - const auto ShowIf = MenuShowIfOrder[std::clamp(static_cast(std::bit_cast(Param2)), 0, static_cast(std::size(MenuShowIfOrder)) - 1)]; - const auto EnableMask = ShowIfUsesMask(ShowIf); + const auto EnableMask = VisibilityHasMask(VisibilityFromIndex(static_cast(std::bit_cast(Param2)))); Dlg->SendMessage(DM_ENABLE, EM_MASK_TEXT, ToPtr(EnableMask)); Dlg->SendMessage(DM_ENABLE, EM_MASK_EDIT, ToPtr(EnableMask)); m_ItemChanged = true; @@ -1145,7 +1147,7 @@ intptr_t UserMenu::EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, v HotKey = get_dialog_item_text(Dlg, EM_HOTKEY_EDIT), Label = get_dialog_item_text(Dlg, EM_LABEL_EDIT), Mask = get_dialog_item_text(Dlg, EM_MASK_EDIT); - const auto ShowIf = MenuShowIfOrder[std::clamp(static_cast(Dlg->SendMessage(DM_LISTGETCURPOS, EM_SHOWIF_COMBO, nullptr)), 0, static_cast(std::size(MenuShowIfOrder)) - 1)]; + const auto Visibility = VisibilityFromIndex(static_cast(Dlg->SendMessage(DM_LISTGETCURPOS, EM_SHOWIF_COMBO, nullptr))); int FocusPos=-1; if (HotKey != MenuSeparator) @@ -1166,7 +1168,7 @@ intptr_t UserMenu::EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, v } } - if (FocusPos == -1 && ShowIfUsesMask(ShowIf) && !Mask.empty() && !filemasks().assign(Mask)) + if (FocusPos == -1 && VisibilityHasMask(Visibility) && !Mask.empty() && !filemasks().assign(Mask)) { Dlg->SendMessage(DM_SETFOCUS, EM_MASK_EDIT, nullptr); return false; @@ -1216,7 +1218,7 @@ intptr_t UserMenu::EditMenuDlgProc(Dialog* Dlg, intptr_t Msg, intptr_t Param1, v } -bool UserMenu::EditMenu(std::list& Menu, std::list::iterator& MenuItem, bool Create) +bool UserMenu::EditMenu(std::list& Menu, std::list::iterator* MenuItem, bool Create) { m_ItemChanged = false; @@ -1246,7 +1248,7 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: } else { - SubMenu = MenuItem->Submenu; + SubMenu = (*MenuItem)->Submenu; } const int DLG_X=76, DLG_Y=SubMenu?12:24; @@ -1288,19 +1290,22 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: enum {DI_EDIT_COUNT=EM_SEPARATOR2-EM_COMMANDS_TEXT-1}; #endif - FarListItem ShowIfItems[std::size(MenuShowIfOrder)]{}; - FarList ComboList{ sizeof(ComboList), std::size(ShowIfItems), ShowIfItems }; + std::array VisibilityItems{}; + FarList ComboList{ sizeof(ComboList), VisibilityItems.size(), VisibilityItems.data() }; - for (size_t i = 0; i != std::size(ShowIfItems); ++i) - ShowIfItems[i].Text = msg(ShowIfTitleId(MenuShowIfOrder[i])).c_str(); + for (size_t i = 0; i != VisibilityItems.size(); ++i) + VisibilityItems[i].Text = msg(VisibilityTitleId(static_cast(i))).c_str(); - const auto InitialShowIf = Create? MenuItemShowIf::Always : MenuItem->ShowIf; - ShowIfItems[ShowIfToIndex(InitialShowIf)].Flags |= LIF_SELECTED; + const auto InitialVisibility = Create? MenuItemVisibility::Always : (*MenuItem)->Visibility; + VisibilityItems[std::to_underlying(InitialVisibility)].Flags |= LIF_SELECTED; EditDlg[EM_SHOWIF_COMBO].ListItems = &ComboList; EditDlg[EM_MASK_EDIT].strHistory = L"Masks"sv; - if (!ShowIfUsesMask(InitialShowIf)) + EditDlg[EM_MASK_TEXT].X1 = EditDlg[EM_SHOWIF_COMBO].X2 + 3; + EditDlg[EM_MASK_EDIT].X1 = EditDlg[EM_MASK_TEXT].X1 + static_cast(HiStrlen(msg(lng::MEditMenuMask))) + 1; + + if (!VisibilityHasMask(InitialVisibility)) { EditDlg[EM_MASK_TEXT].Flags |= DIF_DISABLE; EditDlg[EM_MASK_EDIT].Flags |= DIF_DISABLE; @@ -1308,9 +1313,9 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: if (!Create) { - EditDlg[EM_HOTKEY_EDIT].strData = MenuItem->strHotKey; - EditDlg[EM_LABEL_EDIT].strData = MenuItem->strLabel; - EditDlg[EM_MASK_EDIT].strData = MenuItem->strMask; + EditDlg[EM_HOTKEY_EDIT].strData = (*MenuItem)->strHotKey; + EditDlg[EM_LABEL_EDIT].strData = (*MenuItem)->strLabel; + EditDlg[EM_MASK_EDIT].strData = (*MenuItem)->strMask; #if defined(PROJECT_DI_MEMOEDIT) /* ... @@ -1327,7 +1332,7 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: EditDlg[EM_MEMOEDIT].strData = strBuffer; //??? #else int CommandNumber=0; - for (const auto& i: MenuItem->Commands) + for (const auto& i: (*MenuItem)->Commands) { EditDlg[EM_EDITLINE_0+CommandNumber].strData = i; if (++CommandNumber == DI_EDIT_COUNT) @@ -1346,17 +1351,21 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: return false; m_MenuModified = true; + auto NewItemIterator = Menu.end(); if (Create) - MenuItem = Menu.emplace(MenuItem, UserMenuItem()); + { + NewItemIterator = Menu.emplace(MenuItem? *MenuItem : Menu.begin(), UserMenuItem()); + *MenuItem = NewItemIterator; + } - const auto ShowIf = MenuShowIfOrder[std::clamp(static_cast(EditDlg[EM_SHOWIF_COMBO].ListPos), 0, static_cast(std::size(MenuShowIfOrder)) - 1)]; + const auto Visibility = VisibilityFromIndex(static_cast(EditDlg[EM_SHOWIF_COMBO].ListPos)); - MenuItem->strHotKey = EditDlg[EM_HOTKEY_EDIT].strData; - MenuItem->strLabel = EditDlg[EM_LABEL_EDIT].strData; - MenuItem->ShowIf = ShowIf; - MenuItem->strMask = ShowIfUsesMask(ShowIf)? EditDlg[EM_MASK_EDIT].strData : string{}; - MenuItem->Submenu = SubMenu; + (*MenuItem)->strHotKey = EditDlg[EM_HOTKEY_EDIT].strData; + (*MenuItem)->strLabel = EditDlg[EM_LABEL_EDIT].strData; + (*MenuItem)->Visibility = Visibility; + (*MenuItem)->strMask = VisibilityHasMask(Visibility)? EditDlg[EM_MASK_EDIT].strData : string{}; + (*MenuItem)->Submenu = SubMenu; if (!SubMenu) { @@ -1375,14 +1384,14 @@ bool UserMenu::EditMenu(std::list& Menu, std::list:: CommandNumber = i + 1; } - MenuItem->Commands.clear(); + (*MenuItem)->Commands.clear(); for (const auto i: std::views::iota(0uz, static_cast(DI_EDIT_COUNT))) { if (static_cast(i) >= CommandNumber) break; - MenuItem->Commands.emplace_back(EditDlg[i + EM_EDITLINE_0].strData); + (*MenuItem)->Commands.emplace_back(EditDlg[i + EM_EDITLINE_0].strData); } #endif } diff --git a/far/vbuild.m4 b/far/vbuild.m4 index 90e7a4f2c0..55f2572634 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6741 +6742