diff options
author | elfmz <fenix1905@tut.by> | 2021-12-31 00:27:16 +0300 |
---|---|---|
committer | elfmz <fenix1905@tut.by> | 2021-12-31 02:27:02 +0300 |
commit | a14dc1a81c797928d4f1b7d6a6b46ecc63f98308 (patch) | |
tree | c27c61ac33582bc4d469c6608cd042add388f230 /far2l/src/usermenu.cpp | |
parent | d5f1bf245e96834d44390d1723cfef3dfbb1fb02 (diff) |
shuffle a bit far2l sources
Diffstat (limited to 'far2l/src/usermenu.cpp')
-rw-r--r-- | far2l/src/usermenu.cpp | 1207 |
1 files changed, 1207 insertions, 0 deletions
diff --git a/far2l/src/usermenu.cpp b/far2l/src/usermenu.cpp new file mode 100644 index 00000000..cac54553 --- /dev/null +++ b/far2l/src/usermenu.cpp @@ -0,0 +1,1207 @@ +/* +usermenu.cpp + +User menu и есть +*/ +/* +Copyright (c) 1996 Eugene Roshal +Copyright (c) 2000 Far Group +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "headers.hpp" + + +#include "lang.hpp" +#include "keys.hpp" +#include "filepanels.hpp" +#include "panel.hpp" +#include "cmdline.hpp" +#include "vmenu.hpp" +#include "dialog.hpp" +#include "fileedit.hpp" +#include "ctrlobj.hpp" +#include "manager.hpp" +#include "constitle.hpp" +#include "message.hpp" +#include "usermenu.hpp" +#include "filetype.hpp" +#include "fnparce.hpp" +#include "execute.hpp" +#include "pathmix.hpp" +#include "strmix.hpp" +#include "panelmix.hpp" +#include "filestr.hpp" +#include "mix.hpp" +#include "savescr.hpp" +#include "syslog.hpp" +#include "interf.hpp" +#include "cache.hpp" + +#if defined(PROJECT_DI_MEMOEDIT) +/* + Идея в следующем. + 1. Строки в реестре храняться как и раньше, т.к. CommandXXX + 2. Для DI_MEMOEDIT мы из только преобразовываем в один массив +*/ +#endif + +static std::unique_ptr<ConfigReader> s_cfg_reader; + +// Коды выхода из меню (Exit codes) +enum +{ + EC_CLOSE_LEVEL = -1, // Выйти из меню на один уровень вверх + EC_CLOSE_MENU = -2, // Выйти из меню по SHIFT+F10 + EC_PARENT_MENU = -3, // Показать меню родительского каталога + EC_MAIN_MENU = -4, // Показать главное меню + EC_COMMAND_SELECTED = -5, // Выбрана команда - закрыть меню и обновить папку +}; + +static int PrepareHotKey(FARString &strHotKey) +{ + int FuncNum=0; + + if (strHotKey.GetLength() > 1) + { + // если хоткей больше 1 символа, считаем это случаем "F?", причем при кривизне всегда будет "F1" + FuncNum=_wtoi(strHotKey.CPtr()+1); + + if (FuncNum < 1 || FuncNum > 24) + { + FuncNum=1; + strHotKey=L"F1"; + } + } + else + { + // при наличии "&" продублируем + if (strHotKey.At(0) == L'&') + strHotKey += L"&"; + } + + return FuncNum; +} + +static void MenuRegToFile(const wchar_t *MenuKey, File& MenuFile, CachedWrite& CW, bool SingleItemMenu=false) +{ + for (int i=0;;i++) + { + const std::string &strItemKey = StrPrintf("%ls/Item%d", MenuKey, i); + s_cfg_reader->SelectSection(strItemKey); + FARString strLabel; + if (!s_cfg_reader->GetString(strLabel, "Label", L"")) { + break; + } + + FARString strHotKey = s_cfg_reader->GetString("HotKey", L""); + bool SubMenu = s_cfg_reader->GetInt("Submenu", 0) != 0; + CW.Write(strHotKey.CPtr(), static_cast<DWORD>(strHotKey.GetLength()*sizeof(WCHAR))); + CW.Write(L": ", 3*sizeof(WCHAR)); + CW.Write(strLabel.CPtr(), static_cast<DWORD>(strLabel.GetLength()*sizeof(WCHAR))); + CW.Write(L"\r\n", 2*sizeof(WCHAR)); + + if (SubMenu) + { + CW.Write(L"{\r\n", 3*sizeof(WCHAR)); + MenuRegToFile(FARString(strItemKey), MenuFile, CW, false); + CW.Write(L"}\r\n", 3*sizeof(WCHAR)); + } + else + { + for (int i=0;; i++) + { + FARString strCommand; + if (!s_cfg_reader->GetString(strCommand, StrPrintf("Command%d", i), L"")) { + break; + } + + CW.Write(L" ", 4*sizeof(WCHAR)); + CW.Write(strCommand.CPtr(), static_cast<DWORD>(strCommand.GetLength()*sizeof(WCHAR))); + CW.Write(L"\r\n", 2*sizeof(WCHAR)); + } + } + } +} + +void MenuFileToReg(const wchar_t *MenuKey, File& MenuFile, GetFileString& GetStr, bool SingleItemMenu = false, UINT MenuCP = CP_WIDE_LE) +{ + INT64 Pos = 0; + MenuFile.GetPointer(Pos); + if(!Pos) + { + if (!GetFileFormat(MenuFile,MenuCP)) + MenuCP=CP_UTF8; + } + + LPWSTR MenuStr = nullptr; + int MenuStrLength = 0; + int KeyNumber=-1,CommandNumber=0; + + while(GetStr.GetString(&MenuStr, MenuCP, MenuStrLength)) + { + FARString strItemKey; + + if (!SingleItemMenu) + strItemKey.Format(L"%ls/Item%d",MenuKey,KeyNumber); + else + strItemKey=MenuKey; + + RemoveTrailingSpaces(MenuStr); + + if (!*MenuStr) + continue; + + if (*MenuStr==L'{' && KeyNumber>=0) + { + MenuFileToReg(strItemKey,MenuFile, GetStr, false,MenuCP); + continue; + } + + if (*MenuStr==L'}') + break; + + if (!IsSpace(*MenuStr)) + { + wchar_t *ChPtr=nullptr; + + if (!(ChPtr=wcschr(MenuStr,L':'))) + continue; + + if (!SingleItemMenu) + { + strItemKey.Format(L"%ls/Item%d",MenuKey,++KeyNumber); + } + else + { + strItemKey=MenuKey; + ++KeyNumber; + } + + *ChPtr=0; + FARString strHotKey=MenuStr; + FARString strLabel=ChPtr+1; + RemoveLeadingSpaces(strLabel); + bool SubMenu=(GetStr.PeekString(&MenuStr, MenuCP, MenuStrLength) && *MenuStr==L'{'); + + // Support for old 1.x separator format + if(MenuCP==CP_OEMCP && strHotKey==L"-" && strLabel.IsEmpty()) + { + strHotKey+=L"-"; + } + + ConfigWriter cfg_writer(strItemKey.GetMB()); + cfg_writer.SetString("HotKey", strHotKey); + cfg_writer.SetString("Label", strLabel); + cfg_writer.SetInt("Submenu", SubMenu); + //CloseSameRegKey(); + CommandNumber=0; + } + else + { + if (KeyNumber>=0) + { + RemoveLeadingSpaces(MenuStr); + const std::string &strLineName = StrPrintf("Command%d", CommandNumber); + ++CommandNumber; + ConfigWriter(strItemKey.GetMB()).SetString(strLineName, MenuStr); + } + } + + ConfigReaderScope::Update(s_cfg_reader); + + SingleItemMenu=false; + } +} + +UserMenu::UserMenu(bool ChoiceMenuType) + : grs(s_cfg_reader) +{ + ProcessUserMenu(ChoiceMenuType); +} + +UserMenu::~UserMenu() +{ +} + +void UserMenu::ProcessUserMenu(bool ChoiceMenuType) +{ + // Путь к текущему каталогу с файлом LocalMenuFileName + FARString strMenuFilePath; + CtrlObject->CmdLine->GetCurDir(strMenuFilePath); + // по умолчанию меню - это FarMenu.ini + MenuMode=MM_LOCAL; + FARString strLocalMenuKey; + strLocalMenuKey.Format(L"UserMenu/LocalMenu%u",GetProcessUptimeMSec()); + { ConfigWriter(strLocalMenuKey.GetMB()).RemoveSection(); } + ConfigReaderScope::Update(s_cfg_reader); + MenuModified=MenuNeedRefresh=false; + + if (ChoiceMenuType) + { + int EditChoice=Message(0,3,MSG(MUserMenuTitle),MSG(MChooseMenuType),MSG(MChooseMenuMain),MSG(MChooseMenuLocal),MSG(MCancel)); + + if (EditChoice<0 || EditChoice==2) + return; + + if (!EditChoice) + { + MenuMode=MM_FAR; + strMenuFilePath = g_strFarPath; + } + } + + // основной цикл обработки + const wchar_t *LocalMenuFileName=L"FarMenu.ini"; + bool FirstRun=true; + int ExitCode = 0; + + while ((ExitCode != EC_CLOSE_LEVEL) && (ExitCode != EC_CLOSE_MENU) && (ExitCode != EC_COMMAND_SELECTED)) + { + FARString strMenuFileFullPath = strMenuFilePath; + AddEndSlash(strMenuFileFullPath); + strMenuFileFullPath += LocalMenuFileName; + + if (MenuMode != MM_MAIN) + { + // Пытаемся открыть файл на локальном диске + File MenuFile; + bool FileOpened = PathCanHoldRegularFile(strMenuFilePath) ? MenuFile.Open(strMenuFileFullPath,GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING) : false; + if (FileOpened) + { + // сливаем содержимое в реестр "на запасной путь" и оттуда будем пользовать + GetFileString GetStr(MenuFile); + MenuFileToReg(strLocalMenuKey, MenuFile, GetStr); + MenuFile.Close(); + } + else + { + // Файл не открылся. Смотрим дальше. + if (MenuMode == MM_FAR) // был в %FARHOME%? + { + MenuMode=MM_MAIN; // ...реестр + } + else + { + if (!ChoiceMenuType) + { + if (!FirstRun) + { + // подымаемся выше... + size_t pos; + + if (FindLastSlash(pos,strMenuFilePath)) + { + strMenuFilePath.Truncate(pos--); + + if (strMenuFilePath.At(pos) != L':') + continue; + } + } + + FirstRun=false; + strMenuFilePath = g_strFarPath; + MenuMode=MM_FAR; + continue; + } + } + } + } + + FARString strMenuRootKey = (MenuMode==MM_MAIN) ? L"UserMenu/MainMenu" : strLocalMenuKey; + int PrevMacroMode=CtrlObject->Macro.GetMode(); + int _CurrentFrame=FrameManager->GetCurrentFrame()->GetType(); + CtrlObject->Macro.SetMode(MACRO_USERMENU); + // вызываем меню + ExitCode=ProcessSingleMenu(strMenuRootKey, 0,strMenuRootKey); + + if (_CurrentFrame == FrameManager->GetCurrentFrame()->GetType()) //??? + CtrlObject->Macro.SetMode(PrevMacroMode); + + // обработка локального меню... + if (MenuMode != MM_MAIN) + { + // ...запишем изменения обратно в файл + if (MenuModified) + { + DWORD FileAttr=apiGetFileAttributes(strMenuFileFullPath); + + if (FileAttr != INVALID_FILE_ATTRIBUTES) + { + if (FileAttr & FILE_ATTRIBUTE_READONLY) + { + int AskOverwrite; + AskOverwrite=Message(MSG_WARNING,2,MSG(MUserMenuTitle),LocalMenuFileName,MSG(MEditRO),MSG(MEditOvr),MSG(MYes),MSG(MNo)); + + if (!AskOverwrite) + apiSetFileAttributes(strMenuFileFullPath,FileAttr & ~FILE_ATTRIBUTE_READONLY); + } + + if (FileAttr & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) + apiSetFileAttributes(strMenuFileFullPath,FILE_ATTRIBUTE_NORMAL); + } + + File MenuFile; + // Don't use CreationDisposition=CREATE_ALWAYS here - it's kills alternate streams + if (MenuFile.Open(strMenuFileFullPath,GENERIC_WRITE, FILE_SHARE_READ, nullptr, FileAttr==INVALID_FILE_ATTRIBUTES?CREATE_NEW:TRUNCATE_EXISTING)) + { + CachedWrite CW(MenuFile); + WCHAR Data = SIGN_WIDE_LE; + CW.Write(&Data, sizeof(WCHAR)); + MenuRegToFile(strLocalMenuKey,MenuFile, CW); + CW.Flush(); + UINT64 Size = 0; + MenuFile.GetSize(Size); + MenuFile.Close(); + + // если файл FarMenu.ini пуст, то удалим его + if (Size<3) // 2 for BOM + { + apiDeleteFile(strMenuFileFullPath); + } + else if (FileAttr!=INVALID_FILE_ATTRIBUTES) + { + apiSetFileAttributes(strMenuFileFullPath,FileAttr); + } + } + } + + // ...почистим реестр. + { ConfigWriter(strLocalMenuKey.GetMB()).RemoveSection(); } + ConfigReaderScope::Update(s_cfg_reader); + } + + // что было после вызова меню? + switch (ExitCode) + { + // Показать меню родительского каталога + case EC_PARENT_MENU: + { + if (MenuMode == MM_LOCAL) + { + size_t pos; + + if (FindLastSlash(pos,strMenuFilePath)) + { + strMenuFilePath.Truncate(pos--); + + if (strMenuFilePath.At(pos)!=L':') + continue; + } + + strMenuFilePath = g_strFarPath; + MenuMode=MM_FAR; + } + else + { + MenuMode=MM_MAIN; + } + + break; + } + // Показать главное меню + case EC_MAIN_MENU: + { + // $ 14.07.2000 VVM: Shift+F2 переключает Главное меню/локальное в цикле + switch (MenuMode) + { + case MM_LOCAL: + { + strMenuFilePath = g_strFarPath; + MenuMode=MM_FAR; + break; + } + case MM_FAR: + { + MenuMode=MM_MAIN; + break; + } + default: // MM_MAIN + { + CtrlObject->CmdLine->GetCurDir(strMenuFilePath); + MenuMode=MM_LOCAL; + } + } + + break; + } + } + } + + if (FrameManager->IsPanelsActive() && (ExitCode == EC_COMMAND_SELECTED || MenuModified)) + ShellUpdatePanels(CtrlObject->Cp()->ActivePanel,FALSE); +} + +// заполнение меню +static int FillUserMenu(VMenu& UserMenu,const wchar_t *MenuKey,int MenuPos,int *FuncPos,const wchar_t *Name) +{ + UserMenu.DeleteItems(); + int NumLines=0; + + for (NumLines=0;; NumLines++) + { + s_cfg_reader->SelectSectionFmt("%ls/Item%d", MenuKey, NumLines); + if (!s_cfg_reader->HasSection()) + break; + + MenuItemEx UserMenuItem; + UserMenuItem.Clear(); + FARString strHotKey = s_cfg_reader->GetString("HotKey", L""); + FARString strLabel = s_cfg_reader->GetString("Label", L""); + int FuncNum=0; + + // сепаратором является случай, когда хоткей == "--" + if (!StrCmp(strHotKey,L"--")) + { + UserMenuItem.Flags|=LIF_SEPARATOR; + UserMenuItem.Flags&=~LIF_SELECTED; + UserMenuItem.strName=strLabel; + + if (NumLines==MenuPos) + { + MenuPos++; + } + } + else + { + SubstFileName(strLabel,Name,nullptr,nullptr,TRUE); + apiExpandEnvironmentStrings(strLabel, strLabel); + FuncNum=PrepareHotKey(strHotKey); + int Offset=strHotKey.At(0)==L'&'?5:4; + FormatString FString; + FString<<((!strHotKey.IsEmpty() && !FuncNum)?L"&":L"")<<fmt::LeftAlign()<<fmt::Width(Offset)<<fmt::Precision(Offset)<<strHotKey; + UserMenuItem.strName=std::move(FString.strValue()); + UserMenuItem.strName+=strLabel; + + if (s_cfg_reader->GetInt("Submenu", 0) != 0) + { + UserMenuItem.Flags|=MIF_SUBMENU; + } + + UserMenuItem.SetSelect(NumLines==MenuPos); + UserMenuItem.Flags &= ~LIF_SEPARATOR; + } + + int ItemPos=UserMenu.AddItem(&UserMenuItem); + + if (FuncNum>0) + { + FuncPos[FuncNum-1]=ItemPos; + } + } + + MenuItemEx UserMenuItem; + UserMenuItem.Clear(); + UserMenuItem.SetSelect(NumLines==MenuPos); + UserMenu.AddItem(&UserMenuItem); + return NumLines; +} + +// обработка единичного меню +int UserMenu::ProcessSingleMenu(const wchar_t *MenuKey,int MenuPos,const wchar_t *MenuRootKey,const wchar_t *Title) +{ + MenuItemEx UserMenuItem; + + for (;;) + { + UserMenuItem.Clear(); + int NumLine=0,ExitCode,FuncPos[24]; + + // очистка F-хоткеев + for (size_t I=0 ; I < ARRAYSIZE(FuncPos) ; I++) + FuncPos[I]=-1; + + FARString strName; + CtrlObject->Cp()->ActivePanel->GetCurName(strName); + /* $ 24.07.2000 VVM + При показе главного меню в заголовок добавляет тип - FAR/Registry */ + FARString strMenuTitle; + + if (Title && *Title) + strMenuTitle = Title; + else + { + switch (MenuMode) + { + case MM_LOCAL: + strMenuTitle = MSG(MLocalMenuTitle); + break; + case MM_FAR: + { + strMenuTitle=MSG(MMainMenuTitle); + strMenuTitle+=L" ("; + strMenuTitle+=MSG(MMainMenuFAR); + strMenuTitle+=L")"; + } + break; + default: + { + strMenuTitle=MSG(MMainMenuTitle); + const wchar_t *Ptr=MSG(MMainMenuREG); + + if (*Ptr) + { + strMenuTitle+=L" ("; + strMenuTitle+=Ptr; + strMenuTitle+=L")"; + } + } + } + } + + { + VMenu UserMenu(strMenuTitle,nullptr,0,ScrY-4); + UserMenu.SetFlags(VMENU_WRAPMODE); + UserMenu.SetHelp(L"UserMenu"); + UserMenu.SetPosition(-1,-1,0,0); + UserMenu.SetBottomTitle(MSG(MMainMenuBottomTitle)); + //NumLine=FillUserMenu(UserMenu,MenuKey,MenuPos,FuncPos,Name); + MenuNeedRefresh=true; + + while (!UserMenu.Done()) + { + if (MenuNeedRefresh) + { + UserMenu.Hide(); // спрячем + // "изнасилуем" (перезаполним :-) + NumLine=FillUserMenu(UserMenu,MenuKey,MenuPos,FuncPos,strName); + // заставим манагер менюхи корректно отрисовать ширину и + // высоту, а заодно и скорректировать вертикальные позиции + UserMenu.SetPosition(-1,-1,-1,-1); + UserMenu.Show(); + MenuNeedRefresh=false; + } + + int Key=UserMenu.ReadInput(); + MenuPos=UserMenu.GetSelectPos(); + + if ((unsigned int)Key>=KEY_F1 && (unsigned int)Key<=KEY_F24) + { + int FuncItemPos; + + if ((FuncItemPos=FuncPos[Key-KEY_F1])!=-1) + { + UserMenu.Modal::SetExitCode(FuncItemPos); + continue; + } + } + else if (Key == L' ') // исключаем пробел из "хоткеев"! + continue; + + switch (Key) + { + /* $ 24.08.2001 VVM + Стрелки вправо/влево открывают/закрывают подменю соответственно */ + case KEY_RIGHT: + case KEY_NUMPAD6: + case KEY_MSWHEEL_RIGHT: + { + s_cfg_reader->SelectSectionFmt("%ls/Item%d", MenuKey, MenuPos); + if (s_cfg_reader->GetInt("Submenu", 0) != 0) + UserMenu.SetExitCode(MenuPos); + + break; + } + case KEY_LEFT: + case KEY_NUMPAD4: + case KEY_MSWHEEL_LEFT: + + if (Title && *Title) + UserMenu.SetExitCode(-1); + + break; + case KEY_NUMDEL: + case KEY_DEL: + + if (MenuPos<NumLine) + DeleteMenuRecord(MenuKey,MenuPos); + + break; + case KEY_INS: + case KEY_F4: + case KEY_SHIFTF4: + case KEY_NUMPAD0: + + if (Key != KEY_INS && Key != KEY_NUMPAD0 && MenuPos>=NumLine) + break; + + EditMenu(MenuKey,MenuPos,NumLine,Key == KEY_INS || Key == KEY_NUMPAD0); + break; + case KEY_CTRLUP: + case KEY_CTRLDOWN: + { + int Pos=UserMenu.GetSelectPos(); + + if (Pos!=UserMenu.GetItemCount()-1) + { + if (!(Key==KEY_CTRLUP && !Pos) && !(Key==KEY_CTRLDOWN && Pos==UserMenu.GetItemCount()-2)) + { + MenuPos=Pos+(Key==KEY_CTRLUP?-1:+1); + MoveMenuItem(MenuKey,Pos,MenuPos); + } + } + } + break; + //case KEY_ALTSHIFTF4: // редактировать только текущий пункт (если субменю - то все субменю) + case KEY_CTRLF4: // редактировать все меню + { + (*FrameManager)[0]->Unlock(); + FARString strMenuFileName; + File MenuFile; + if (!FarMkTempEx(strMenuFileName) || (!MenuFile.Open(strMenuFileName, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS))) + { + break; + } + + FARString strCurrentKey; + + if (Key==KEY_ALTSHIFTF4) + strCurrentKey.Format(L"%ls/Item%d",MenuKey,MenuPos); + else + strCurrentKey=MenuRootKey; + CachedWrite CW(MenuFile); + WCHAR Data = SIGN_WIDE_LE; + CW.Write(&Data, sizeof(WCHAR)); + MenuRegToFile(strCurrentKey, MenuFile, CW, Key==KEY_ALTSHIFTF4); + CW.Flush(); + MenuNeedRefresh=true; + MenuFile.Close(); + { + ConsoleTitle *OldTitle=new ConsoleTitle; + FARString strFileName = strMenuFileName; + FileEditor ShellEditor(strFileName,CP_WIDE_LE,FFILEEDIT_DISABLEHISTORY,-1,-1,nullptr); + delete OldTitle; + ShellEditor.SetDynamicallyBorn(false); + FrameManager->EnterModalEV(); + FrameManager->ExecuteModal(); + FrameManager->ExitModalEV(); + + if (!ShellEditor.IsFileChanged() || (!MenuFile.Open(strMenuFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING))) + { + apiDeleteFile(strMenuFileName); + + if (Key == KEY_ALTSHIFTF4) // для тукущего пункта меню закрывать ненадо + break; + + return 0; + } + } + { ConfigWriter(strCurrentKey.GetMB()).RemoveSection(); } + ConfigReaderScope::Update(s_cfg_reader); + GetFileString GetStr(MenuFile); + MenuFileToReg(strCurrentKey, MenuFile, GetStr, Key==KEY_ALTSHIFTF4); + MenuFile.Close(); + apiDeleteFile(strMenuFileName); + MenuModified=true; + UserMenu.Hide(); + + if (Key == KEY_ALTSHIFTF4) // для тукущего пункта меню закрывать ненадо + break; + + return 0; // Закрыть меню + } + /* $ 28.06.2000 tran + выход из пользовательского меню по ShiftF10 из любого уровня + вложенности просто задаем ExitCode -1, и возвращаем FALSE - + по FALSE оно и выйдет откуда угодно */ + case KEY_SHIFTF10: + //UserMenu.SetExitCode(-1); + return(EC_CLOSE_MENU); + case KEY_SHIFTF2: // Показать главное меню + return(EC_MAIN_MENU); + case KEY_BS: // Показать меню из родительского каталога только в MM_LOCAL режиме + + if (MenuMode != MM_MAIN) + return(EC_PARENT_MENU); + + default: + UserMenu.ProcessInput(); + + if (Key == KEY_F1) + MenuNeedRefresh=true; + + break; + } // switch(Key) + } // while (!UserMenu.Done()) + + ExitCode=UserMenu.Modal::GetExitCode(); + } + + if (ExitCode<0 || ExitCode>=NumLine) + return(EC_CLOSE_LEVEL); // вверх на один уровень + + FARString strCurrentKey; + strCurrentKey.Format(L"%ls/Item%d",MenuKey,ExitCode); + s_cfg_reader->SelectSection(strCurrentKey); + int SubMenu = s_cfg_reader->GetInt("Submenu", 0); + + if (SubMenu) + { + /* $ 20.08.2001 VVM + При вложенных меню показывает заголовки предыдущих */ + FARString strSubMenuKey, strSubMenuTitle, strSubMenuLabel; + strSubMenuKey.Format(L"%ls/Item%d",MenuKey,ExitCode); + s_cfg_reader->SelectSection(strSubMenuKey); + if (s_cfg_reader->GetString(strSubMenuLabel, "Label", L"")) + { + SubstFileName(strSubMenuLabel,strName,nullptr,nullptr,TRUE); + apiExpandEnvironmentStrings(strSubMenuLabel, strSubMenuLabel); + size_t pos; + + if (strSubMenuLabel.Pos(pos,L'&')) + strSubMenuLabel.LShift(1,pos); + + if (Title && *Title) + { + strSubMenuTitle=Title; + strSubMenuTitle+=L" -> "; + strSubMenuTitle+=strSubMenuLabel; + } + else + strSubMenuTitle = strSubMenuLabel; + } + + /* $ 14.07.2000 VVM ! Если закрыли подменю, то остаться. Инече передать управление выше */ + MenuPos=ProcessSingleMenu(strSubMenuKey,0,MenuRootKey,strSubMenuTitle); + + if (MenuPos!=EC_CLOSE_LEVEL) + return(MenuPos); + + MenuPos=ExitCode; + continue; + } + + /* $ 01.05.2001 IS Отключим до лучших времен */ + //int LeftVisible,RightVisible,PanelsHidden=0; + int CurLine=0; + FARString strCmdLineDir; + CtrlObject->CmdLine->GetCurDir(strCmdLineDir); + FARString strOldCmdLine; + CtrlObject->CmdLine->GetString(strOldCmdLine); + int OldCmdLineCurPos = CtrlObject->CmdLine->GetCurPos(); + int OldCmdLineLeftPos = CtrlObject->CmdLine->GetLeftPos(); + int OldCmdLineSelStart, OldCmdLineSelEnd; + CtrlObject->CmdLine->GetSelection(OldCmdLineSelStart,OldCmdLineSelEnd); + CtrlObject->CmdLine->LockUpdatePanel(TRUE); + + // Цикл исполнения команд меню (CommandX) + for (;;) + { + FormatString strLineName; + FARString strCommand, strListName, strAnotherListName; + strLineName<<L"Command"<<CurLine; + + s_cfg_reader->SelectSection(strCurrentKey); + if (!s_cfg_reader->GetString(strCommand, FARString(strLineName).GetMB(), L"")) + break; + + if (!((!StrCmpNI(strCommand,L"REM",3) && IsSpaceOrEos(strCommand.At(3))) || !StrCmpNI(strCommand,L"::",2))) + { + /* + Осталось корректно обработать ситуацию, например: + if exist !#!\!^!.! far:edit < diff -c -p !#!\!^!.! !\!.! + Т.е. сначала "вычислить" кусок "if exist !#!\!^!.!", ну а если + выполнится, то делать дальше. + Или еще пример, + if exist ..\a.bat D:\FAR\170\DIFF.MY\mkdiff.bat !?&Номер патча?! + ЭТО выполняется всегда, т.к. парсинг всей строки идет, а надо + проверить фазу "if exist ..\a.bat", а уж потом делать выводы... + */ + { + /* $ 01.05.2001 IS Отключим до лучших времен */ + /* + if (!PanelsHidden) + { + LeftVisible=CtrlObject->Cp()->LeftPanel->IsVisible(); + RightVisible=CtrlObject->Cp()->RightPanel->IsVisible(); + CtrlObject->Cp()->LeftPanel->Hide(); + CtrlObject->Cp()->RightPanel->Hide(); + CtrlObject->Cp()->LeftPanel->SetUpdateMode(FALSE); + CtrlObject->Cp()->RightPanel->SetUpdateMode(FALSE); + PanelsHidden=TRUE; + } + */ + //; + /*int PreserveLFN=*/SubstFileName(strCommand,strName,&strListName,&strAnotherListName, FALSE, strCmdLineDir); + bool ListFileUsed=!strListName.IsEmpty()||!strAnotherListName.IsEmpty(); + + { + //PreserveLongName PreserveName(PreserveLFN); + RemoveExternalSpaces(strCommand); + + if (!strCommand.IsEmpty()) + { + bool isSilent=false; + + if (strCommand.At(0) == L'@') + { + strCommand.LShift(1); + isSilent=true; + } + + ProcessOSAliases(strCommand); + // TODO: Ахтунг. В режиме isSilent имеем проблемы с командами, которые выводят что-то на экран + // Здесь необходимо переделка, например, перед исполнением подсунуть временный экранный буфер, а потом его содержимое подсунуть в ScreenBuf... + + if (!isSilent) + { + CtrlObject->CmdLine->ExecString(strCommand,FALSE, 0, 0, ListFileUsed); + } + else + { + SaveScreen SaveScr; + CtrlObject->Cp()->LeftPanel->CloseFile(); + CtrlObject->Cp()->RightPanel->CloseFile(); + Execute(strCommand, 0, 0, 0, ListFileUsed, true); + } +// WaitForClose(strName); + } + } + } + } // strCommand != "REM" + + if (!strListName.IsEmpty()) + QueueDeleteOnClose(strListName); + + if (!strAnotherListName.IsEmpty()) + QueueDeleteOnClose(strAnotherListName); + + CurLine++; + } // while (1) + + CtrlObject->CmdLine->LockUpdatePanel(FALSE); + + if (!strOldCmdLine.IsEmpty()) // восстановим сохраненную командную строку + { + CtrlObject->CmdLine->SetString(strOldCmdLine, FrameManager->IsPanelsActive()); + CtrlObject->CmdLine->SetCurPos(OldCmdLineCurPos, OldCmdLineLeftPos); + CtrlObject->CmdLine->Select(OldCmdLineSelStart, OldCmdLineSelEnd); + } + + /* $ 01.05.2001 IS Отключим до лучших времен */ + /* + if (PanelsHidden) + { + CtrlObject->Cp()->LeftPanel->SetUpdateMode(TRUE); + CtrlObject->Cp()->RightPanel->SetUpdateMode(TRUE); + CtrlObject->Cp()->LeftPanel->Update(UPDATE_KEEP_SELECTION); + CtrlObject->Cp()->RightPanel->Update(UPDATE_KEEP_SELECTION); + if (RightVisible) + CtrlObject->Cp()->RightPanel->Show(); + if (LeftVisible) + CtrlObject->Cp()->LeftPanel->Show(); + } + */ + /* $ 14.07.2000 VVM ! Закрыть меню */ + /* $ 25.04.2001 DJ - сообщаем, что была выполнена команда (нужно перерисовать панели) */ + return(EC_COMMAND_SELECTED); + } +} + +enum EditMenuItems +{ + EM_DOUBLEBOX, + EM_HOTKEY_TEXT, + EM_HOTKEY_EDIT, + EM_LABEL_TEXT, + EM_LABEL_EDIT, + EM_SEPARATOR1, + EM_COMMANDS_TEXT, +#ifdef PROJECT_DI_MEMOEDIT + EM_MEMOEDIT, +#else + EM_EDITLINE_0, + EM_EDITLINE_1, + EM_EDITLINE_2, + EM_EDITLINE_3, + EM_EDITLINE_4, + EM_EDITLINE_5, + EM_EDITLINE_6, + EM_EDITLINE_7, + EM_EDITLINE_8, + EM_EDITLINE_9, +#endif + EM_SEPARATOR2, + EM_BUTTON_OK, + EM_BUTTON_CANCEL, +}; + +LONG_PTR WINAPI EditMenuDlgProc(HANDLE hDlg,int Msg,int Param1,LONG_PTR Param2) +{ +#if defined(PROJECT_DI_MEMOEDIT) + Dialog* Dlg=(Dialog*)hDlg; + + switch (Msg) + { + case DN_INITDIALOG: + { + break; + } + } + +#endif + + switch (Msg) + { + case DN_CLOSE: + + if (Param1==EM_BUTTON_OK) + { + BOOL Result=TRUE; + LPCWSTR HotKey=reinterpret_cast<LPCWSTR>(SendDlgMessage(hDlg,DM_GETCONSTTEXTPTR,EM_HOTKEY_EDIT,0)); + LPCWSTR Label=reinterpret_cast<LPCWSTR>(SendDlgMessage(hDlg,DM_GETCONSTTEXTPTR,EM_LABEL_EDIT,0)); + int FocusPos=-1; + + if(StrCmp(HotKey,L"--")) + { + if (!*Label) + { + FocusPos=EM_LABEL_EDIT; + } + else if (StrLength(HotKey)>1) + { + FocusPos=EM_HOTKEY_EDIT; + + if (Upper(*HotKey)==L'F') + { + int FuncNum=_wtoi(HotKey+1); + + if (FuncNum > 0 && FuncNum < 25) + FocusPos=-1; + } + } + } + + if (FocusPos!=-1) + { + Message(MSG_WARNING,1,MSG(MUserMenuTitle),MSG((*Label?MUserMenuInvalidInputHotKey:MUserMenuInvalidInputLabel)),MSG(MOk)); + SendDlgMessage(hDlg,DM_SETFOCUS,FocusPos,0); + Result=FALSE; + } + + return Result; + } + + break; + } + + return DefDlgProc(hDlg,Msg,Param1,Param2); +} + + +bool UserMenu::EditMenu(const wchar_t *MenuKey,int EditPos,int TotalRecords,bool Create) +{ + bool Result=false; + FormatString strItemKey; + strItemKey<<MenuKey<<L"/Item"<<EditPos; + MenuNeedRefresh=true; + bool SubMenu=false,Continue=true; + + s_cfg_reader->SelectSection(strItemKey); + + if (Create) + { + switch (Message(0,2,MSG(MUserMenuTitle),MSG(MAskInsertMenuOrCommand),MSG(MMenuInsertCommand),MSG(MMenuInsertMenu))) + { + case -1: + case -2: + Continue=false; + case 1: + SubMenu=true; + } + } + else + { + SubMenu = s_cfg_reader->GetInt("Submenu", 0) != 0; + } + + if (Continue) + { + const int DLG_X=76, DLG_Y=SubMenu?10:22; + DWORD State=SubMenu?DIF_HIDDEN|DIF_DISABLE:0; + DialogDataEx EditDlgData[]= + { + {DI_DOUBLEBOX,3,1,DLG_X-4,(short)(DLG_Y-2),{},0,MSG(SubMenu?MEditSubmenuTitle:MEditMenuTitle)}, + {DI_TEXT,5,2,0,2,{},0,MSG(MEditMenuHotKey)}, + {DI_FIXEDIT,5,3,7,3,{},DIF_FOCUS,L""}, + {DI_TEXT,5,4,0,4,{},0,MSG(MEditMenuLabel)}, + {DI_EDIT,5,5,DLG_X-6,5,{},0,L""}, + + {DI_TEXT,3,6,0,6,{},DIF_SEPARATOR|State,L""}, + {DI_TEXT,5,7,0,7,{},State,MSG(MEditMenuCommands)}, +#ifdef PROJECT_DI_MEMOEDIT + {DI_MEMOEDIT,5, 8,DLG_X-6,17,{},DIF_EDITPATH,L""}, +#else + {DI_EDIT,5, 8,DLG_X-6,8,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5, 9,DLG_X-6,9,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5,10,DLG_X-6,10,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5,11,DLG_X-6,11,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5,12,DLG_X-6,12,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5,13,DLG_X-6,13,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5,14,DLG_X-6,14,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5,15,DLG_X-6,15,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5,16,DLG_X-6,16,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, + {DI_EDIT,5,17,DLG_X-6,17,{},DIF_EDITPATH|DIF_EDITOR|State,L""}, +#endif + + {DI_TEXT,3,(short)(DLG_Y-4),0,(short)(DLG_Y-4),{},DIF_SEPARATOR,L""}, + {DI_BUTTON,0,(short)(DLG_Y-3),0,(short)(DLG_Y-3),{},DIF_DEFAULT|DIF_CENTERGROUP,MSG(MOk)}, + {DI_BUTTON,0,(short)(DLG_Y-3),0,(short)(DLG_Y-3),{},DIF_CENTERGROUP,MSG(MCancel)} + }; + MakeDialogItemsEx(EditDlgData,EditDlg); +#ifndef PROJECT_DI_MEMOEDIT + enum {DI_EDIT_COUNT=EM_SEPARATOR2-EM_COMMANDS_TEXT-1}; +#endif + + if (!Create) + { + EditDlg[EM_HOTKEY_EDIT].strData = s_cfg_reader->GetString("HotKey", L""); + EditDlg[EM_LABEL_EDIT].strData = s_cfg_reader->GetString("Label", L""); +#if defined(PROJECT_DI_MEMOEDIT) + /* + ... + здесь добавка строк из "Command%d" в EMR_MEMOEDIT + ... + */ + FARString strBuffer, strCommand; + int CommandNumber=0; + + while (s_cfg_reader->GetString(strCommand, StrPrintf("Command%d", CommandNumber), L"")) + { + strBuffer+=strCommand; + strBuffer+=L"\n"; //??? "\n\r" + CommandNumber++; + } + + EditDlg[EM_MEMOEDIT].strData = strBuffer; //??? +#else + int CommandNumber=0; + + while (CommandNumber < DI_EDIT_COUNT) + { + FARString strCommand; + if (!s_cfg_reader->GetString(strCommand, StrPrintf("Command%d", CommandNumber), L"")) + break; + + EditDlg[EM_EDITLINE_0+CommandNumber].strData = strCommand; + CommandNumber++; + } + +#endif + } + + Dialog Dlg(EditDlg,ARRAYSIZE(EditDlg),EditMenuDlgProc); + Dlg.SetHelp(L"UserMenu"); + Dlg.SetPosition(-1,-1,DLG_X,DLG_Y); + Dlg.Process(); + + if (Dlg.GetExitCode()==EM_BUTTON_OK) + { + MenuModified=true; + { + ConfigWriter cfg_writer; + cfg_writer.SelectSectionFmt("%ls/Item%u", MenuKey, (unsigned int)EditPos); + + if (Create) + { + cfg_writer.ReserveIndexedSection( + StrPrintf("%ls/Item", MenuKey).c_str(), (unsigned int)EditPos); + } + + cfg_writer.SetString("HotKey", EditDlg[EM_HOTKEY_EDIT].strData.CPtr()); + cfg_writer.SetString("Label", EditDlg[EM_LABEL_EDIT].strData.CPtr()); + cfg_writer.SetInt("Submenu", SubMenu ? 1 : 0); + + if (!SubMenu) + { +#if defined(PROJECT_DI_MEMOEDIT) + /* + ... + здесь преобразование содержимого итема EMR_MEMOEDIT в "Command%d" + ... + */ +#else + int CommandNumber=0; + + for (int i=0 ; i < DI_EDIT_COUNT ; i++) + if (!EditDlg[i+EM_EDITLINE_0].strData.IsEmpty()) + CommandNumber=i+1; + + for (int i=0 ; i < DI_EDIT_COUNT ; i++) + { + const std::string &strCommandName = StrPrintf("Command%d", i); + + if (i>=CommandNumber) + cfg_writer.RemoveKey(strCommandName); + else + cfg_writer.SetString(strCommandName, EditDlg[i+EM_EDITLINE_0].strData.CPtr()); + } +#endif + } + } + + ConfigReaderScope::Update(s_cfg_reader); + Result=true; + } + } + + return Result; +} + +int UserMenu::DeleteMenuRecord(const wchar_t *MenuKey,int DeletePos) +{ + FormatString strRegKey; + strRegKey<<MenuKey<<L"/Item"<<DeletePos; + s_cfg_reader->SelectSection(strRegKey); + FARString strRecText = s_cfg_reader->GetString("Label", L""); + int SubMenu = s_cfg_reader->GetInt("Submenu", 0); + FARString strItemName=strRecText; + InsertQuote(strItemName); + + if (Message(MSG_WARNING,2,MSG(MUserMenuTitle),MSG(!SubMenu?MAskDeleteMenuItem:MAskDeleteSubMenuItem),strItemName,MSG(MDelete),MSG(MCancel))) + return FALSE; + + MenuModified=MenuNeedRefresh=true; + strRegKey.Clear(); + strRegKey << MenuKey << L"/Item"; + FARString DefragPrefix(strRegKey); + strRegKey << DeletePos; + { + ConfigWriter cfg_writer(FARString(strRegKey).GetMB()); + cfg_writer.RemoveSection(); + cfg_writer.DefragIndexedSections(DefragPrefix.GetMB().c_str()); + } + ConfigReaderScope::Update(s_cfg_reader); + return TRUE; +} + +bool UserMenu::MoveMenuItem(const wchar_t *MenuKey,int Pos,int NewPos) +{ + if (Pos != NewPos) + { + ConfigWriter().MoveIndexedSection( + StrPrintf("%ls/Item", MenuKey).c_str(), Pos, NewPos); + MenuModified = MenuNeedRefresh=true; + } + ConfigReaderScope::Update(s_cfg_reader); + return true; +} |