示例#1
0
/*
  Используется для запуска внешнего редактора и вьювера
*/
void ProcessExternal(const wchar_t *Command, const wchar_t *Name, const wchar_t *ShortName, bool AlwaysWaitFinish)
{
	string strListName, strAnotherListName;
	string strShortListName, strAnotherShortListName;
	string strFullName, strFullShortName;
	string strExecStr = Command;
	string strFullExecStr = Command;
	{
		int PreserveLFN=SubstFileName(strExecStr,Name,ShortName,&strListName,&strAnotherListName, &strShortListName, &strAnotherShortListName);
		bool ListFileUsed=!strListName.IsEmpty()||!strAnotherListName.IsEmpty()||!strShortListName.IsEmpty()||!strAnotherShortListName.IsEmpty();

		// Снова все "подставлено", теперь проверим условия "if exist"
		if (!ExtractIfExistCommand(strExecStr))
			return;

		PreserveLongName PreserveName(ShortName,PreserveLFN);
		ConvertNameToFull(Name,strFullName);
		ConvertNameToShort(strFullName,strFullShortName);
		//BUGBUGBUGBUGBUGBUG !!! Same ListNames!!!
		SubstFileName(strFullExecStr,strFullName,strFullShortName,&strListName,&strAnotherListName, &strShortListName, &strAnotherShortListName);

		// Снова все "подставлено", теперь проверим условия "if exist"
		if (!ExtractIfExistCommand(strFullExecStr))
			return;

		CtrlObject->ViewHistory->AddToHistory(strFullExecStr,(AlwaysWaitFinish&1)+2);

		if (strExecStr.At(0) != L'@')
			CtrlObject->CmdLine->ExecString(strExecStr,AlwaysWaitFinish, 0, 0, ListFileUsed, true);
		else
		{
			SaveScreen SaveScr;
			CtrlObject->Cp()->LeftPanel->CloseFile();
			CtrlObject->Cp()->RightPanel->CloseFile();
			Execute(strExecStr.CPtr()+1,AlwaysWaitFinish, 0, 0, 0, ListFileUsed);
		}
	}

	if (!strListName.IsEmpty())
		apiDeleteFile(strListName);

	if (!strAnotherListName.IsEmpty())
		apiDeleteFile(strAnotherListName);

	if (!strShortListName.IsEmpty())
		apiDeleteFile(strShortListName);

	if (!strAnotherShortListName.IsEmpty())
		apiDeleteFile(strAnotherShortListName);
}
示例#2
0
// обработка единичного меню
int UserMenu::ProcessSingleMenu(DList<UserMenuItem> *Menu, int MenuPos, DList<UserMenuItem> *MenuRoot, const string& MenuFileName, const wchar_t *Title)
{
	for (;;)
	{
		int NumLine=0, ExitCode, FuncPos[24];
		UserMenuItem *CurrentMenuItem = nullptr;

		// очистка F-хоткеев
		for (size_t I=0 ; I < ARRAYSIZE(FuncPos) ; I++)
			FuncPos[I]=-1;

		string strName,strShortName;
		CtrlObject->Cp()->ActivePanel->GetCurName(strName,strShortName);
		/* $ 24.07.2000 VVM + При показе главного меню в заголовок добавляет тип - FAR/Registry */
		string strMenuTitle;

		if (Title && *Title)
		{
			strMenuTitle = Title;
		}
		else
		{
			switch (MenuMode)
			{
				case MM_LOCAL:
					strMenuTitle = MSG(MLocalMenuTitle);
					break;

				case MM_GLOBAL:
					strMenuTitle = MSG(MMainMenuTitle);
					strMenuTitle += L" (";
					strMenuTitle += MSG(MMainMenuGlobal);
					strMenuTitle += L")";
					break;

				default:
					strMenuTitle = MSG(MMainMenuTitle);
					strMenuTitle += L" (";
					strMenuTitle += MSG(MMainMenuUser);
					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));
			MenuNeedRefresh=true;

			while (!UserMenu.Done())
			{
				if (MenuNeedRefresh)
				{
					UserMenu.Hide(); // спрячем
					// "изнасилуем" (перезаполним :-)
					NumLine=FillUserMenu(UserMenu,Menu,MenuPos,FuncPos,strName,strShortName);
					// заставим манагер менюхи корректно отрисовать ширину и
					// высоту, а заодно и скорректировать вертикальные позиции
					UserMenu.SetPosition(-1,-1,-1,-1);
					UserMenu.Show();
					MenuNeedRefresh=false;
				}

				int Key=UserMenu.ReadInput();
				MenuPos=UserMenu.GetSelectPos();

				void* userdata = UserMenu.GetUserData(nullptr, 0, MenuPos);
				CurrentMenuItem = userdata? *static_cast<UserMenuItem**>(userdata):nullptr;

				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:
						if (CurrentMenuItem && CurrentMenuItem->Submenu)
							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 (CurrentMenuItem)
							DeleteMenuRecord(Menu,CurrentMenuItem);
						break;

					case KEY_INS:
					case KEY_F4:
					case KEY_SHIFTF4:
					case KEY_NUMPAD0:
					{
						bool bNew = Key == KEY_INS || Key == KEY_NUMPAD0;
						if (!bNew && !CurrentMenuItem)
							break;

						EditMenu(Menu,CurrentMenuItem,bNew);
						break;
					}

					case KEY_CTRLUP:
					case KEY_RCTRLUP:
					case KEY_CTRLDOWN:
					case KEY_RCTRLDOWN:
					{
						int Pos=UserMenu.GetSelectPos();

						if (Pos!=UserMenu.GetItemCount()-1 && CurrentMenuItem)
						{
							if (!((Key==KEY_CTRLUP || Key==KEY_RCTRLUP) && !Pos) && !((Key==KEY_CTRLDOWN || Key==KEY_RCTRLDOWN) && Pos==UserMenu.GetItemCount()-2))
							{
								MenuModified = MenuNeedRefresh = true;
								if (Key==KEY_CTRLUP || Key==KEY_RCTRLUP)
								{
									Menu->MoveBefore(Menu->Prev(CurrentMenuItem),CurrentMenuItem);
									MenuPos--;
								}
								else
								{
									Menu->MoveAfter(Menu->Next(CurrentMenuItem),CurrentMenuItem);
									MenuPos++;
								}
							}
						}
					}
					break;

					case KEY_ALTF4:       // редактировать все меню
					case KEY_RALTF4:
					{
						File MenuFile;
						(*FrameManager)[0]->Unlock();
						{
							ConsoleTitle *OldTitle=new ConsoleTitle;
							SaveMenu(MenuFileName);
							FileEditor ShellEditor(MenuFileName,CP_UNICODE,FFILEEDIT_DISABLEHISTORY,-1,-1,nullptr);
							delete OldTitle;
							ShellEditor.SetDynamicallyBorn(false);
							FrameManager->EnterModalEV();
							FrameManager->ExecuteModal();
							FrameManager->ExitModalEV();
							if (!ShellEditor.IsFileChanged() || (!MenuFile.Open(MenuFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING)))
							{
								return 0;
							}
						}
						MenuRoot->Clear();
						GetFileString GetStr(MenuFile);
						MenuFileToList(MenuRoot, MenuFile, GetStr);
						MenuFile.Close();
						MenuModified=true;
						UserMenu.Hide();

						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_LOCAL)
							return EC_PARENT_MENU;

					default:
						UserMenu.ProcessInput();

						if (MenuPos!=UserMenu.GetSelectPos())
						{
							MenuPos=UserMenu.GetSelectPos();
							userdata = UserMenu.GetUserData(nullptr, 0, MenuPos);
							CurrentMenuItem = userdata? *static_cast<UserMenuItem**>(userdata):nullptr;
						}

						if (Key == KEY_F1)
							MenuNeedRefresh=true;

						break;
				} // switch(Key)
			} // while (!UserMenu.Done())

			ExitCode=UserMenu.Modal::GetExitCode();

			if (ExitCode<0 || ExitCode>=NumLine || !CurrentMenuItem)
				return EC_CLOSE_LEVEL; //  вверх на один уровень

			void* userdata = UserMenu.GetUserData(nullptr, 0, ExitCode);
			CurrentMenuItem = userdata? *static_cast<UserMenuItem**>(userdata):nullptr;

			if (!CurrentMenuItem)
				return EC_CLOSE_LEVEL; //  вверх на один уровень
		}

		if (CurrentMenuItem->Submenu)
		{
			/* $ 20.08.2001 VVM + При вложенных меню показывает заголовки предыдущих */
			string strSubMenuLabel = CurrentMenuItem->strLabel;
			SubstFileName(strSubMenuLabel,strName,strShortName,nullptr,nullptr,nullptr,nullptr,TRUE);
			apiExpandEnvironmentStrings(strSubMenuLabel, strSubMenuLabel);

			size_t pos;
			if (strSubMenuLabel.Pos(pos,L'&'))
				strSubMenuLabel.LShift(1,pos);

			string strSubMenuTitle;
			if (Title && *Title)
			{
				strSubMenuTitle = Title;
				strSubMenuTitle += L" -> ";
				strSubMenuTitle += strSubMenuLabel;
			}
			else
			{
				strSubMenuTitle = strSubMenuLabel;
			}

			/* $ 14.07.2000 VVM ! Если закрыли подменю, то остаться. Инече передать управление выше */
			MenuPos = ProcessSingleMenu(CurrentMenuItem->Menu, 0, MenuRoot, MenuFileName, strSubMenuTitle);

			if (MenuPos!=EC_CLOSE_LEVEL)
				return MenuPos;

			MenuPos = ExitCode;
			continue;
		}

		/* $ 01.05.2001 IS Отключим до лучших времен */
		//int LeftVisible,RightVisible,PanelsHidden=0;
		string strCmdLineDir;
		CtrlObject->CmdLine->GetCurDir(strCmdLineDir);
		string strOldCmdLine;
		CtrlObject->CmdLine->GetString(strOldCmdLine);
		int OldCmdLineCurPos = CtrlObject->CmdLine->GetCurPos();
		int OldCmdLineLeftPos = CtrlObject->CmdLine->GetLeftPos();
		intptr_t OldCmdLineSelStart, OldCmdLineSelEnd;
		CtrlObject->CmdLine->GetSelection(OldCmdLineSelStart,OldCmdLineSelEnd);
		CtrlObject->CmdLine->LockUpdatePanel(TRUE);

		// Цикл исполнения команд меню (CommandX)
		for (string *str=CurrentMenuItem->Commands.First(); str; str=CurrentMenuItem->Commands.Next(str))
		{
			string strCommand = *str;

			string strListName, strAnotherListName;
			string strShortListName, strAnotherShortListName;

			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", а уж потом делать выводы...
				*/
				//if(ExtractIfExistCommand(Command))
				{
					/* $ 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, strShortName, &strListName, &strAnotherListName, &strShortListName, &strAnotherShortListName, FALSE, strCmdLineDir);
					bool ListFileUsed=!strListName.IsEmpty()||!strAnotherListName.IsEmpty()||!strShortListName.IsEmpty()||!strAnotherShortListName.IsEmpty();

					if (ExtractIfExistCommand(strCommand))
					{
						PreserveLongName PreserveName(strShortName,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,TRUE, 0, 0, 0, ListFileUsed, true);
							}
						}
					}
				}
			} // strCommand != "REM"

			if (!strListName.IsEmpty())
				apiDeleteFile(strListName);

			if (!strAnotherListName.IsEmpty())
				apiDeleteFile(strAnotherListName);

			if (!strShortListName.IsEmpty())
				apiDeleteFile(strShortListName);

			if (!strAnotherShortListName.IsEmpty())
				apiDeleteFile(strAnotherShortListName);

		} // 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;
	}
}
示例#3
0
/* $ 06.07.2001
   + Используем CFileMask вместо GetCommaWord, этим самым добиваемся того, что
     можно использовать маски исключения
   - Убрал непонятный мне запрет на использование маски файлов типа "*.*"
     (был когда-то, вроде, такой баг-репорт)
*/
bool ProcessLocalFileTypes(const wchar_t *Name, const wchar_t *ShortName, int Mode, bool AlwaysWaitFinish)
{
	RenumKeyRecord(FTS.Associations,FTS.TypeFmt,FTS.Type0);
	MenuItemEx TypesMenuItem;
	VMenu TypesMenu(MSG(MSelectAssocTitle),nullptr,0,ScrY-4);
	TypesMenu.SetHelp(FTS.Help);
	TypesMenu.SetFlags(VMENU_WRAPMODE);
	TypesMenu.SetPosition(-1,-1,0,0);
	int DizWidth=GetDescriptionWidth(Name, ShortName);
	int ActualCmdCount=0; // отображаемых ассоциаций в меню
	CFileMask FMask; // для работы с масками файлов
	string strCommand, strDescription;
	int CommandCount=0;

	for (int I=0;; I++)
	{
		strCommand.Clear();
		string strRegKey, strMask;
		strRegKey.Format(FTS.TypeFmt,I);

		if (!GetRegKey(strRegKey,FTS.Mask,strMask,L""))
			break;

		if (FMask.Set(strMask,FMF_SILENT))
		{
			if (FMask.Compare(Name))
			{
				LPCWSTR Type=nullptr;

				switch (Mode)
				{
					case FILETYPE_EXEC:
						Type=FTS.Execute;
						break;
					case FILETYPE_VIEW:
						Type=FTS.View;
						break;
					case FILETYPE_EDIT:
						Type=FTS.Edit;
						break;
					case FILETYPE_ALTEXEC:
						Type=FTS.AltExec;
						break;
					case FILETYPE_ALTVIEW:
						Type=FTS.AltView;
						break;
					case FILETYPE_ALTEDIT:
						Type=FTS.AltEdit;
						break;
				}

				DWORD State=GetRegKey(strRegKey,FTS.State,0xffffffff);

				if (State&(1<<Mode))
				{
					string strNewCommand;
					GetRegKey(strRegKey,Type,strNewCommand,L"");

					if (!strNewCommand.IsEmpty())
					{
						strCommand = strNewCommand;
						GetRegKey(strRegKey,FTS.Desc,strDescription,L"");
						CommandCount++;
					}
				}
			}

			if (strCommand.IsEmpty())
				continue;
		}

		TypesMenuItem.Clear();
		string strCommandText = strCommand;
		SubstFileName(strCommandText,Name,ShortName,nullptr,nullptr,nullptr,nullptr,TRUE);

		// все "подставлено", теперь проверим условия "if exist"
		if (!ExtractIfExistCommand(strCommandText))
			continue;

		ActualCmdCount++;
		string strMenuText;

		if (DizWidth)
		{
			string strTitle;

			if (!strDescription.IsEmpty())
			{
				strTitle = strDescription;
				SubstFileName(strTitle, Name,ShortName,nullptr,nullptr,nullptr,nullptr,TRUE);
			}

			size_t Pos=0;
			bool Ampersand=strTitle.Pos(Pos,L'&');

			if (DizWidth+Ampersand>ScrX/2 && Ampersand && static_cast<int>(Pos)>DizWidth)
				Ampersand=false;

			strMenuText.Format(L"%-*.*s %c ",DizWidth+Ampersand,DizWidth+Ampersand,strTitle.CPtr(),BoxSymbols[BS_V1]);
		}

		TruncStr(strCommandText,ScrX-DizWidth-14);
		strMenuText += strCommandText;
		TypesMenuItem.strName = strMenuText;
		TypesMenuItem.SetSelect(!I);
		TypesMenu.SetUserData(strCommand.CPtr(),0,TypesMenu.AddItem(&TypesMenuItem));
	}

	if (!CommandCount)
		return false;

	if (!ActualCmdCount)
		return true;

	int ExitCode=0;

	if (ActualCmdCount>1)
	{
		TypesMenu.Process();
		ExitCode=TypesMenu.Modal::GetExitCode();

		if (ExitCode<0)
			return true;
	}

	int Size=TypesMenu.GetUserDataSize(ExitCode);
	LPWSTR Command=strCommand.GetBuffer(Size/sizeof(wchar_t));
	TypesMenu.GetUserData(Command,Size,ExitCode);
	strCommand.ReleaseBuffer(Size);
	string strListName, strAnotherListName;
	string strShortListName, strAnotherShortListName;
	int PreserveLFN=SubstFileName(strCommand,Name,ShortName,&strListName,&strAnotherListName, &strShortListName, &strAnotherShortListName);
	bool ListFileUsed=!strListName.IsEmpty()||!strAnotherListName.IsEmpty()||!strShortListName.IsEmpty()||!strAnotherShortListName.IsEmpty();

	// Снова все "подставлено", теперь проверим условия "if exist"
	if (ExtractIfExistCommand(strCommand))
	{
		PreserveLongName PreserveName(ShortName,PreserveLFN);
		RemoveExternalSpaces(strCommand);

		if (!strCommand.IsEmpty())
		{
			bool isSilent=(strCommand.At(0)==L'@');

			if (isSilent)
			{
				strCommand.LShift(1);
			}

			ProcessOSAliases(strCommand);

			if (!isSilent)
			{
				CtrlObject->CmdLine->ExecString(strCommand,AlwaysWaitFinish, false, false, ListFileUsed);

				if (!(Opt.ExcludeCmdHistory&EXCLUDECMDHISTORY_NOTFARASS) && !AlwaysWaitFinish) //AN
					CtrlObject->CmdHistory->AddToHistory(strCommand);
			}
			else
			{
#if 1
				SaveScreen SaveScr;
				CtrlObject->Cp()->LeftPanel->CloseFile();
				CtrlObject->Cp()->RightPanel->CloseFile();
				Execute(strCommand,AlwaysWaitFinish, 0, 0, 0, ListFileUsed, true);
#else
				// здесь была бага с прорисовкой (и... вывод данных
				// на команду "@type !@!" пропадал с экрана)
				// сделаем по аналогии с CommandLine::CmdExecute()
				{
					RedrawDesktop RdrwDesktop(TRUE);
					Execute(strCommand,AlwaysWaitFinish, 0, 0, 0, ListFileUsed);
					ScrollScreen(1); // обязательно, иначе деструктор RedrawDesktop
					// проредравив экран забьет последнюю строку вывода.
				}
				CtrlObject->Cp()->LeftPanel->UpdateIfChanged(UIC_UPDATE_FORCE);
				CtrlObject->Cp()->RightPanel->UpdateIfChanged(UIC_UPDATE_FORCE);
				CtrlObject->Cp()->Redraw();
#endif
			}
			if (FrameManager->GetCurrentFrame()->GetType()==MODALTYPE_VIEWER)
			{
				TypesMenu.ResetCursor();
			}
		}
	}

	if (!strListName.IsEmpty())
		apiDeleteFile(strListName);

	if (!strAnotherListName.IsEmpty())
		apiDeleteFile(strAnotherListName);

	if (!strShortListName.IsEmpty())
		apiDeleteFile(strShortListName);

	if (!strAnotherShortListName.IsEmpty())
		apiDeleteFile(strAnotherShortListName);

	return true;
}
示例#4
0
/* $ 06.07.2001
   + Используем filemasks вместо GetCommaWord, этим самым добиваемся того, что
     можно использовать маски исключения
   - Убрал непонятный мне запрет на использование маски файлов типа "*.*"
     (был когда-то, вроде, такой баг-репорт)
*/
bool ProcessLocalFileTypes(const string& Name, const string& ShortName, FILETYPE_MODE Mode, bool AlwaysWaitFinish)
{
	string strCommand, strDescription, strMask;
	{
		const auto TypesMenu = VMenu2::create(MSG(MSelectAssocTitle), nullptr, 0, ScrY - 4);
		TypesMenu->SetHelp(L"FileAssoc");
		TypesMenu->SetMenuFlags(VMENU_WRAPMODE);
		TypesMenu->SetId(SelectAssocMenuId);

		int ActualCmdCount=0; // отображаемых ассоциаций в меню
		filemasks FMask; // для работы с масками файлов

		int CommandCount=0;
		DWORD Index=0;
		unsigned __int64 id;
		string FileName = PointToName(Name);

		while (ConfigProvider().AssocConfig()->EnumMasksForType(Mode,Index++,&id,strMask))
		{
			strCommand.clear();

			if (FMask.Set(strMask,FMF_SILENT))
			{
				if (FMask.Compare(FileName))
				{
					ConfigProvider().AssocConfig()->GetCommand(id,Mode,strCommand);

					if (!strCommand.empty())
					{
						ConfigProvider().AssocConfig()->GetDescription(id,strDescription);
						CommandCount++;
					}
				}

				if (strCommand.empty())
					continue;
			}

			string strCommandText = strCommand;
			SubstFileName(nullptr,strCommandText,Name, ShortName,nullptr,nullptr,nullptr,nullptr,TRUE);

			// все "подставлено", теперь проверим условия "if exist"
			if (!ExtractIfExistCommand(strCommandText))
				continue;

			ActualCmdCount++;

			if (!strDescription.empty())
				SubstFileName(nullptr,strDescription, Name, ShortName, nullptr, nullptr, nullptr, nullptr, TRUE);
			else
				strDescription = strCommandText;

			MenuItemEx TypesMenuItem(strDescription);
			TypesMenuItem.SetSelect(Index==1);
			TypesMenuItem.UserData = strCommand;
			TypesMenu->AddItem(TypesMenuItem);
		}

		if (!CommandCount)
			return false;

		if (!ActualCmdCount)
			return true;

		int ExitCode=0;

		if (ActualCmdCount>1)
		{
			ExitCode=TypesMenu->Run();

			if (ExitCode<0)
				return true;
		}

		strCommand = *TypesMenu->GetUserDataPtr<string>(ExitCode);
	}

	string strListName, strAnotherListName, strShortListName, strAnotherShortListName;

	const string* ListNames[] =
	{
		&strListName,
		&strAnotherListName,
		&strShortListName,
		&strAnotherShortListName
	};

	int PreserveLFN=SubstFileName(nullptr,strCommand, Name, ShortName, &strListName, &strAnotherListName, &strShortListName, &strAnotherShortListName);
	const auto ListFileUsed = !std::all_of(ALL_CONST_RANGE(ListNames), std::mem_fn(&string::empty));

	// Снова все "подставлено", теперь проверим условия "if exist"
	if (ExtractIfExistCommand(strCommand))
	{
		SCOPED_ACTION(PreserveLongName)(ShortName, PreserveLFN);
		RemoveExternalSpaces(strCommand);

		if (!strCommand.empty())
		{
			Global->CtrlObject->CmdLine()->ExecString(strCommand,AlwaysWaitFinish, false, false, ListFileUsed, false,
				Mode == FILETYPE_VIEW || Mode == FILETYPE_ALTVIEW || Mode == FILETYPE_EDIT || Mode == FILETYPE_ALTEDIT);
			if (!(Global->Opt->ExcludeCmdHistory&EXCLUDECMDHISTORY_NOTFARASS) && !AlwaysWaitFinish) //AN
			{
				const auto curDir = Global->CtrlObject->CmdLine()->GetCurDir();
				Global->CtrlObject->CmdHistory->AddToHistory(strCommand, HR_DEFAULT, nullptr, nullptr, curDir.data());
			}
		}
	}

	std::for_each(CONST_RANGE(ListNames, i)
	{
		if (!i->empty())
			os::DeleteFile(*i);
	});