RecipeLayoutViewModel.cs 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. using Aitex.Core.RT.Log;
  2. using Caliburn.Micro;
  3. using Caliburn.Micro.Core;
  4. using MECF.Framework.Common.DataCenter;
  5. using MECF.Framework.UI.Client.CenterViews.Editors;
  6. using MECF.Framework.UI.Client.CenterViews.Editors.Recipe;
  7. using MECF.Framework.UI.Client.CenterViews.Editors.Sequence;
  8. using MECF.Framework.UI.Client.ClientBase;
  9. using OpenSEMI.ClientBase;
  10. using OpenSEMI.ClientBase.Command;
  11. using RecipeEditorLib.RecipeModel.Params;
  12. using System;
  13. using System.Collections.Generic;
  14. using System.Collections.ObjectModel;
  15. using System.Linq;
  16. using System.Windows;
  17. using System.Windows.Controls;
  18. using System.Windows.Input;
  19. using System.Windows.Media;
  20. using FurnaceUI.Models;
  21. using FurnaceUI.Views.Editors;
  22. using FurnaceUI.Common;
  23. namespace FurnaceUI.Views.Recipes
  24. {
  25. public class RecipeLayoutViewModel : FurnaceUIViewModelBase
  26. {
  27. public bool IsPermission { get => this.Permission == 3; }//&& RtStatus != "AutoRunning";
  28. private ICommand _RenameFolderCommand;
  29. public ICommand RenameFolderCommand
  30. {
  31. get
  32. {
  33. if (this._RenameFolderCommand == null)
  34. this._RenameFolderCommand = new BaseCommand(() => this.RenameFolder());
  35. return this._RenameFolderCommand;
  36. }
  37. }
  38. private ICommand _DeleteFolderCommand;
  39. public ICommand DeleteFolderCommand
  40. {
  41. get
  42. {
  43. if (this._DeleteFolderCommand == null)
  44. this._DeleteFolderCommand = new BaseCommand(() => this.DeleteFolder());
  45. return this._DeleteFolderCommand;
  46. }
  47. }
  48. private ICommand _NewFolderCommand;
  49. public ICommand NewFolderCommand
  50. {
  51. get
  52. {
  53. if (this._NewFolderCommand == null)
  54. this._NewFolderCommand = new BaseCommand(() => this.NewFolder());
  55. return this._NewFolderCommand;
  56. }
  57. }
  58. private ICommand _NewFolderRootCommand;
  59. public ICommand NewFolderRootCommand
  60. {
  61. get
  62. {
  63. if (this._NewFolderRootCommand == null)
  64. this._NewFolderRootCommand = new BaseCommand(() => this.NewFolderRoot());
  65. return this._NewFolderRootCommand;
  66. }
  67. }
  68. private ICommand _NewRecipeCommand;
  69. public ICommand NewRecipeCommand
  70. {
  71. get
  72. {
  73. if (this._NewRecipeCommand == null)
  74. this._NewRecipeCommand = new BaseCommand(() => this.NewRecipe());
  75. return this._NewRecipeCommand;
  76. }
  77. }
  78. private ICommand _NewRecipeRootCommand;
  79. public ICommand NewRecipeRootCommand
  80. {
  81. get
  82. {
  83. if (this._NewRecipeRootCommand == null)
  84. this._NewRecipeRootCommand = new BaseCommand(() => this.NewRecipeRoot());
  85. return this._NewRecipeRootCommand;
  86. }
  87. }
  88. private ICommand _RenameRecipeCommand;
  89. public ICommand RenameRecipeCommand
  90. {
  91. get
  92. {
  93. if (this._RenameRecipeCommand == null)
  94. this._RenameRecipeCommand = new BaseCommand(() => this.RenameRecipe());
  95. return this._RenameRecipeCommand;
  96. }
  97. }
  98. private ICommand _DeleteRecipeCommand;
  99. public ICommand DeleteRecipeCommand
  100. {
  101. get
  102. {
  103. if (this._DeleteRecipeCommand == null)
  104. this._DeleteRecipeCommand = new BaseCommand(() => this.DeleteRecipe());
  105. return this._DeleteRecipeCommand;
  106. }
  107. }
  108. private ICommand _SaveAsRecipeCommand;
  109. public ICommand SaveAsRecipeCommand
  110. {
  111. get
  112. {
  113. if (this._SaveAsRecipeCommand == null)
  114. this._SaveAsRecipeCommand = new BaseCommand(() => this.SaveAsRecipe());
  115. return this._SaveAsRecipeCommand;
  116. }
  117. }
  118. private ICommand _EditRecipeCommand;
  119. public ICommand EditRecipeCommand
  120. {
  121. get
  122. {
  123. if (this._EditRecipeCommand == null)
  124. this._EditRecipeCommand = new BaseCommand(() => this.EditRecipe());
  125. return this._EditRecipeCommand;
  126. }
  127. }
  128. private ICommand _SelectRecipeTypeCommand;
  129. public ICommand SelectRecipeTypeCommand
  130. {
  131. get
  132. {
  133. if (this._SelectRecipeTypeCommand == null)
  134. this._SelectRecipeTypeCommand = new BaseCommand(() => this.SelectRecipeType());
  135. return this._SelectRecipeTypeCommand;
  136. }
  137. }
  138. private ICommand _ViewRecipeCommand;
  139. public ICommand ViewRecipeCommand
  140. {
  141. get
  142. {
  143. if (this._ViewRecipeCommand == null)
  144. this._ViewRecipeCommand = new BaseCommand(() => this.ViewRecipe());
  145. return this._ViewRecipeCommand;
  146. }
  147. }
  148. private ICommand _ChangePermission;
  149. public ICommand ChangePermission
  150. {
  151. get
  152. {
  153. if (this._ChangePermission == null)
  154. this._ChangePermission = new BaseCommand(() => this.SavePermission());
  155. return this._ChangePermission;
  156. }
  157. }
  158. public ObservableCollection<ProcessTypeFileItem> ProcessTypeFileList { get; set; }
  159. public RecipeDataLayout CurrentRecipe { get; private set; }
  160. public FileNode CurrentFileNode { get; set; }
  161. public bool IsCurrentNodePath { get => CurrentFileNode != null && !CurrentFileNode.IsFile; }
  162. public bool IsCurrentNodeFile { get => CurrentFileNode != null && CurrentFileNode.IsFile; }
  163. private bool _isReadAndWritePermission;
  164. public bool IsReadAndWritePermission
  165. {
  166. get
  167. {
  168. _isReadAndWritePermission = IsSelectPermission && CurrentFileNode.Permission != "Read";
  169. return _isReadAndWritePermission;
  170. }
  171. set
  172. {
  173. _isReadAndWritePermission = value;
  174. }
  175. }
  176. private bool _isFreePermission;
  177. public bool IsFreePermission
  178. {
  179. get
  180. {
  181. _isFreePermission = IsSelectPermission;
  182. return _isFreePermission;
  183. }
  184. set
  185. {
  186. _isFreePermission = value;
  187. }
  188. }
  189. private bool _isSelectPermission;
  190. public bool IsSelectPermission
  191. {
  192. get
  193. {
  194. _isSelectPermission = LevelDisplay != "LEVEL1" && IsCurrentNodeFile;
  195. return _isSelectPermission;
  196. }
  197. set
  198. {
  199. _isSelectPermission = value;
  200. }
  201. }
  202. private bool IsChanged
  203. {
  204. get
  205. {
  206. return editMode == EditMode.Edit || CurrentRecipe.IsChanged;
  207. }
  208. }
  209. private RecipeFormatBuilder _columnBuilder = new RecipeFormatBuilder();
  210. private EditMode editMode;
  211. private RecipeProvider _recipeProvider = new RecipeProvider();
  212. public ObservableCollection<string> ChamberType { get; set; }
  213. public int ChamberTypeIndexSelection { get; set; }
  214. private int _ProcessTypeIndexSelection;
  215. public int ProcessTypeIndexSelection
  216. {
  217. get
  218. {
  219. return _ProcessTypeIndexSelection;
  220. }
  221. set
  222. {
  223. _ProcessTypeIndexSelection = value;
  224. NotifyOfPropertyChange(nameof(ProcessTypeIndexSelection));
  225. }
  226. }
  227. public string CurrentChamberType
  228. {
  229. get
  230. {
  231. return ChamberType[ChamberTypeIndexSelection];
  232. }
  233. }
  234. public string CurrentProcessType
  235. {
  236. get
  237. {
  238. return ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType;
  239. }
  240. }
  241. public Visibility MultiChamberVisibility
  242. {
  243. get;
  244. set;
  245. }
  246. public Visibility ToleranceVisibility
  247. {
  248. get;
  249. set;
  250. }
  251. public ObservableCollection<string> Chambers { get; set; }
  252. public string SelectedChamber { get; set; }
  253. public object View { get; set; }
  254. private string _SelectRecipeTypeName;
  255. public string SelectRecipeTypeName
  256. {
  257. get
  258. {
  259. return _SelectRecipeTypeName;
  260. }
  261. set
  262. {
  263. _SelectRecipeTypeName = value;
  264. NotifyOfPropertyChange(nameof(SelectRecipeTypeName));
  265. }
  266. }
  267. protected override void OnInitialize()
  268. {
  269. base.OnInitialize();
  270. var chamberType = QueryDataClient.Instance.Service.GetConfig("System.Recipe.SupportedChamberType");
  271. if (chamberType == null)
  272. {
  273. ChamberType = new ObservableCollection<string>() { "Default" };
  274. }
  275. else
  276. {
  277. ChamberType = new ObservableCollection<string>(((string)(chamberType)).Split(','));
  278. }
  279. ChamberTypeIndexSelection = 0;
  280. //Etch:Process,Clean,Chuck,Dechuck;CVD:Process,Clean;
  281. var processType = QueryDataClient.Instance.Service.GetConfig($"System.Recipe.{CurrentMenuID}");
  282. if (processType == null)
  283. {
  284. processType = CurrentMenuID;
  285. }
  286. ProcessTypeFileList = new ObservableCollection<ProcessTypeFileItem>();
  287. string[] recipeProcessType = ((string)processType).Split(',');
  288. for (int i = 0; i < recipeProcessType.Length; i++)
  289. {
  290. var type = new ProcessTypeFileItem();
  291. type.ProcessType = recipeProcessType[i];
  292. var prefix = $"{ChamberType[ChamberTypeIndexSelection]}\\{recipeProcessType[i]}";
  293. var recipes = _recipeProvider.GetXmlRecipeList(prefix);
  294. type.FileListByProcessType = RecipeSequenceTreeBuilder.BuildFileNode(prefix, "", false, recipes)[0].Files;
  295. ProcessTypeFileList.Add(type);
  296. }
  297. if (ProcessTypeFileList[0].FileListByProcessType.Count > 0)
  298. CurrentFileNode = ProcessTypeFileList[0].FileListByProcessType[ProcessTypeFileList[0].FileListByProcessType.Count - 1];
  299. SelectRecipeTypeName = processType + " Recipe";
  300. UpdateRecipeFormat();
  301. if (CurrentFileNode != null && CurrentFileNode.IsFile)
  302. {
  303. this.LoadData(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath);
  304. }
  305. }
  306. protected override void OnActivate()
  307. {
  308. base.OnActivate();
  309. }
  310. protected override void OnDeactivate(bool close)
  311. {
  312. base.OnDeactivate(close);
  313. if (this.IsChanged)
  314. {
  315. if (DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} content is changed, do you want to save it?") == DialogButton.Yes)
  316. {
  317. this.SaveRecipe();
  318. }
  319. }
  320. }
  321. public void TabSelectionChanged()
  322. {
  323. UpdateRecipeFormat();
  324. OnViewLoaded(View);
  325. }
  326. public void UpdateRecipeFormat()
  327. {
  328. this.CurrentRecipe = new RecipeDataLayout();
  329. CurrentRecipe.RecipeChamberType = _columnBuilder.RecipeChamberType;
  330. CurrentRecipe.RecipeVersion = _columnBuilder.RecipeVersion;
  331. this.editMode = EditMode.None;
  332. var chamber = QueryDataClient.Instance.Service.GetConfig("System.Recipe.ChamberModules");
  333. if (chamber == null)
  334. {
  335. chamber = "PM1";
  336. }
  337. Chambers = new ObservableCollection<string>(((string)chamber).Split(','));
  338. SelectedChamber = Chambers[0];
  339. MultiChamberVisibility = Chambers.Count > 1 ? Visibility.Visible : Visibility.Collapsed;
  340. ToleranceVisibility = CurrentRecipe.ToleranceEnable ? Visibility.Visible : Visibility.Collapsed;
  341. }
  342. public void TreeSelectChanged(FileNode node)
  343. {
  344. if (IsChanged)
  345. {
  346. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No,
  347. DialogType.CONFIRM,
  348. $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
  349. if (selection == DialogButton.Yes)
  350. {
  351. this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  352. this.CurrentRecipe.ReviseTime = DateTime.Now;
  353. this.Save(this.CurrentRecipe, false);
  354. }
  355. }
  356. CurrentFileNode = node;
  357. NotifyOfPropertyChange(nameof(IsCurrentNodeFile));
  358. NotifyOfPropertyChange(nameof(IsCurrentNodePath));
  359. NotifyOfPropertyChange(nameof(IsSelectPermission));
  360. NotifyOfPropertyChange(nameof(IsReadAndWritePermission));
  361. NotifyOfPropertyChange(nameof(IsFreePermission));
  362. if (node != null && node.IsFile)
  363. {
  364. this.LoadData(node.PrefixPath, node.FullPath);
  365. }
  366. else
  367. {
  368. this.ClearData();
  369. this.editMode = EditMode.None;
  370. }
  371. this.UpdateView();
  372. }
  373. #region folder
  374. public void SelectRecipeType()
  375. {
  376. var windowManager = IoC.Get<IWindowManager>();
  377. LayoutRecipeSelectTypeViewModel layoutRecipeSelectTypeViewModel = new LayoutRecipeSelectTypeViewModel();
  378. (windowManager as WindowManager)?.ShowDialogWithTitle(layoutRecipeSelectTypeViewModel, null, "Select Type");
  379. if (layoutRecipeSelectTypeViewModel.SelectRecipeType != null)
  380. {
  381. var selectRecipeType = layoutRecipeSelectTypeViewModel.SelectRecipeType;
  382. var processType = QueryDataClient.Instance.Service.GetConfig($"System.Recipe.{selectRecipeType}");
  383. if (processType == null)
  384. {
  385. processType = selectRecipeType;
  386. }
  387. ProcessTypeFileList = new ObservableCollection<ProcessTypeFileItem>();
  388. string[] recipeProcessType = ((string)processType).Split(',');
  389. for (int i = 0; i < recipeProcessType.Length; i++)
  390. {
  391. var type = new ProcessTypeFileItem();
  392. type.ProcessType = recipeProcessType[i];
  393. var prefix = $"{ChamberType[ChamberTypeIndexSelection]}\\{recipeProcessType[i]}";
  394. var recipes = _recipeProvider.GetXmlRecipeList(prefix);
  395. type.FileListByProcessType = RecipeSequenceTreeBuilder.BuildFileNode(prefix, "", false, recipes)[0].Files;
  396. ProcessTypeFileList.Add(type);
  397. }
  398. SelectRecipeTypeName = processType + " Recipe";
  399. this.NotifyOfPropertyChange("ProcessTypeFileList");
  400. ProcessTypeIndexSelection = 0;
  401. }
  402. }
  403. public void NewFolder()
  404. {
  405. if (IsChanged)
  406. {
  407. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
  408. if (selection == DialogButton.Cancel)
  409. return;
  410. if (selection == DialogButton.Yes)
  411. {
  412. this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  413. this.CurrentRecipe.ReviseTime = DateTime.Now;
  414. this.Save(this.CurrentRecipe, false);
  415. }
  416. }
  417. InputFileNameDialogViewModel dialog = new InputFileNameDialogViewModel("Input New Folder Name", ProcessTypeFileList[0].FileListByProcessType, "", Visibility.Visible, Visibility.Collapsed);
  418. dialog.FileName = "new folder";
  419. WindowManager wm = new WindowManager();
  420. bool? dialogReturn = wm.ShowDialog(dialog);
  421. if (!dialogReturn.HasValue || !dialogReturn.Value)
  422. return;
  423. string name = dialog.FileName.Trim();
  424. if (string.IsNullOrEmpty(name))
  425. {
  426. DialogBox.ShowWarning("Folder name should not be empty");
  427. return;
  428. }
  429. string prefix = ChamberType[ChamberTypeIndexSelection] + "\\" + ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType;
  430. string processType = string.Empty;
  431. string newFolder = string.Empty;
  432. if (CurrentFileNode != null)
  433. {
  434. prefix = CurrentFileNode.PrefixPath;
  435. string folder = CurrentFileNode.FullPath;
  436. if (CurrentFileNode.IsFile)
  437. {
  438. folder = folder.Substring(0, folder.LastIndexOf("\\") + 1);
  439. if (!string.IsNullOrEmpty(folder))
  440. newFolder = folder;
  441. }
  442. else
  443. {
  444. newFolder = folder + "\\";
  445. }
  446. }
  447. newFolder = newFolder + name;
  448. if (IsExist(newFolder, false))
  449. {
  450. DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder with the same name already exist.");
  451. return;
  452. }
  453. if (newFolder.Length > 200)
  454. {
  455. DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder name too long, should be less 200.");
  456. return;
  457. }
  458. _recipeProvider.CreateRecipeFolder(prefix, newFolder);
  459. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newFolder, true);
  460. }
  461. public void NewFolderRoot()
  462. {
  463. if (IsChanged)
  464. {
  465. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
  466. if (selection == DialogButton.Cancel)
  467. return;
  468. if (selection == DialogButton.Yes)
  469. {
  470. this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  471. this.CurrentRecipe.ReviseTime = DateTime.Now;
  472. this.Save(this.CurrentRecipe, false);
  473. }
  474. }
  475. InputFileNameDialogViewModel dialog = new InputFileNameDialogViewModel("Input New Folder Name", ProcessTypeFileList[0].FileListByProcessType);
  476. dialog.FileName = "new folder";
  477. WindowManager wm = new WindowManager();
  478. bool? dialogReturn = wm.ShowDialog(dialog);
  479. if (!dialogReturn.HasValue || !dialogReturn.Value)
  480. return;
  481. string name = dialog.FileName.Trim();
  482. if (string.IsNullOrEmpty(name))
  483. {
  484. DialogBox.ShowWarning("Folder name should not be empty");
  485. return;
  486. }
  487. if (IsExist(name, false))
  488. {
  489. DialogBox.ShowWarning($"Can not create folder {name}, Folder with the same name already exist.");
  490. return;
  491. }
  492. if (name.Length > 200)
  493. {
  494. DialogBox.ShowWarning($"Can not create folder {name}, Folder name too long, should be less 200.");
  495. return;
  496. }
  497. string prefix = ChamberType[ChamberTypeIndexSelection] + "\\" + ProcessTypeFileList[ProcessTypeIndexSelection].ProcessType;
  498. _recipeProvider.CreateRecipeFolder(prefix, name);
  499. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, name, true);
  500. }
  501. public void DeleteFolder()
  502. {
  503. if (CurrentFileNode == null || CurrentFileNode.IsFile)
  504. return;
  505. if (CurrentFileNode.Files.Count > 0)
  506. {
  507. DialogBox.ShowWarning($"Can not delete non-empty folder, Remove the files or folders under \r\n{CurrentFileNode.FullPath}.");
  508. return;
  509. }
  510. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM,
  511. $"Are you sure you want to delete \r\n {CurrentFileNode.FullPath}?");
  512. if (selection == DialogButton.No)
  513. return;
  514. string nextFocus = CurrentFileNode.Parent.FullPath;
  515. bool isFolder = true;
  516. if (CurrentFileNode.Parent.Files.Count > 1)
  517. {
  518. for (int i = 0; i < CurrentFileNode.Parent.Files.Count; i++)
  519. {
  520. if (CurrentFileNode.Parent.Files[i] == CurrentFileNode)
  521. {
  522. if (i == 0)
  523. {
  524. nextFocus = CurrentFileNode.Parent.Files[i + 1].FullPath;
  525. isFolder = !CurrentFileNode.Parent.Files[i + 1].IsFile;
  526. }
  527. else
  528. {
  529. nextFocus = CurrentFileNode.Parent.Files[i - 1].FullPath;
  530. isFolder = !CurrentFileNode.Parent.Files[i - 1].IsFile;
  531. }
  532. }
  533. }
  534. }
  535. _recipeProvider.DeleteRecipeFolder(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath);
  536. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, nextFocus, isFolder);
  537. }
  538. public void RenameFolder()
  539. {
  540. if (CurrentFileNode == null || CurrentFileNode.IsFile)
  541. return;
  542. InputFileNameDialogViewModel dialog = new InputFileNameDialogViewModel("Input New Folder Name", ProcessTypeFileList[0].FileListByProcessType, "", Visibility.Hidden,Visibility.Collapsed);
  543. dialog.FileName = CurrentFileNode.Name;
  544. WindowManager wm = new WindowManager();
  545. bool? dialogReturn = wm.ShowDialog(dialog);
  546. if (!dialogReturn.HasValue || !dialogReturn.Value)
  547. return;
  548. string name = dialog.FileName.Trim();
  549. if (string.IsNullOrEmpty(name))
  550. return;
  551. string newFolder = CurrentFileNode.FullPath.Substring(0, CurrentFileNode.FullPath.LastIndexOf("\\") + 1);
  552. if (!string.IsNullOrEmpty(newFolder))
  553. newFolder = newFolder + name;
  554. else
  555. newFolder = name;
  556. if (newFolder == CurrentFileNode.FullPath)
  557. return;
  558. if (IsExist(newFolder, false))
  559. {
  560. DialogBox.ShowWarning($"Can not rename to {newFolder}, Folder with the same name already exist.");
  561. return;
  562. }
  563. if (newFolder.Length > 200)
  564. {
  565. DialogBox.ShowWarning($"Can not create folder {newFolder}, Folder name too long, should be less 200.");
  566. return;
  567. }
  568. if (_recipeProvider.RenameFolder(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath, newFolder))
  569. {
  570. foreach (var file in CurrentFileNode.Files)
  571. {
  572. UIGlobalVariable.Instance.ProcessModifiedRecipe[file.FullPath] = $"Folder Rename from [{CurrentFileNode.FullPath}] to [{name}] {DateTime.Now}";
  573. }
  574. }
  575. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newFolder, true);
  576. }
  577. #endregion
  578. #region recipe
  579. public void NewRecipe()
  580. {
  581. if (IsChanged)
  582. {
  583. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
  584. if (selection == DialogButton.Cancel)
  585. return;
  586. if (selection == DialogButton.Yes)
  587. {
  588. this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  589. this.CurrentRecipe.ReviseTime = DateTime.Now;
  590. this.Save(this.CurrentRecipe, false);
  591. }
  592. }
  593. InputFileNameDialogViewModel dialog = new InputFileNameDialogViewModel("Input New Recipe Name", ProcessTypeFileList[0].FileListByProcessType, CurrentRecipe.Description);
  594. dialog.FileName = (string)QueryDataClient.Instance.Service.GetConfig($"System.Recipe.DefaultLayoutRecipeName");
  595. WindowManager wm = new WindowManager();
  596. bool? dialogReturn = wm.ShowDialog(dialog);
  597. if (!dialogReturn.HasValue || !dialogReturn.Value)
  598. return;
  599. string recipeName = dialog.FileName.Trim();
  600. string filepath = dialog.FilePath;
  601. string description = dialog.Comment;
  602. if (string.IsNullOrEmpty(dialog.FileName))
  603. {
  604. DialogBox.ShowWarning("Recipe file name should not be empty");
  605. return;
  606. }
  607. string prefix = CurrentChamberType + "\\" + CurrentProcessType + "\\" + filepath;
  608. if (CurrentFileNode != null)
  609. {
  610. if (CurrentFileNode.IsFile)
  611. {
  612. filepath = filepath.Substring(0, filepath.LastIndexOf("\\") + 1);
  613. //if (!string.IsNullOrEmpty(folder))
  614. // folder = folder;
  615. }
  616. else
  617. {
  618. filepath = filepath + "\\";
  619. }
  620. }
  621. if (IsExist(prefix + recipeName, true))
  622. {
  623. DialogBox.ShowWarning($"Can not create {recipeName}, Recipe with the same name already exist.");
  624. return;
  625. }
  626. if ((prefix + recipeName).Length > 200)
  627. {
  628. DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200.");
  629. return;
  630. }
  631. RecipeDataLayout recipe = new RecipeDataLayout();
  632. recipe.Name = recipeName;
  633. recipe.PrefixPath = prefix;
  634. recipe.Creator = BaseApp.Instance.UserContext.LoginName;
  635. recipe.CreateTime = DateTime.Now;
  636. recipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  637. recipe.ReviseTime = DateTime.Now;
  638. recipe.Description = description;
  639. recipe.RecipeLevel = LevelDisplay;
  640. recipe.RecipePermission = "Free";
  641. if (!Save(recipe, true))
  642. return;
  643. var types = prefix.Split('\\');
  644. ReloadRecipeFileList(types[0], types[1], recipeName, false);
  645. }
  646. public void NewRecipeRoot()
  647. {
  648. if (IsChanged)
  649. {
  650. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
  651. if (selection == DialogButton.Cancel)
  652. return;
  653. if (selection == DialogButton.Yes)
  654. {
  655. this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  656. this.CurrentRecipe.ReviseTime = DateTime.Now;
  657. this.Save(this.CurrentRecipe, false);
  658. }
  659. }
  660. InputFileNameDialogViewModel dialog = new InputFileNameDialogViewModel("Input New Recipe Name");
  661. dialog.FileName = "new recipe";
  662. WindowManager wm = new WindowManager();
  663. bool? dialogReturn = wm.ShowDialog(dialog);
  664. if (!dialogReturn.HasValue || !dialogReturn.Value)
  665. return;
  666. string recipeName = dialog.FileName.Trim();
  667. string description = dialog.Comment;
  668. if (string.IsNullOrEmpty(dialog.FileName))
  669. {
  670. DialogBox.ShowWarning("Recipe file name should not be empty");
  671. return;
  672. }
  673. if (IsExist(recipeName, true))
  674. {
  675. DialogBox.ShowWarning($"Can not create {recipeName}, Recipe with the same name already exist.");
  676. return;
  677. }
  678. if (recipeName.Length > 200)
  679. {
  680. DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200.");
  681. return;
  682. }
  683. RecipeDataLayout recipe = new RecipeDataLayout();
  684. recipe.Name = recipeName;
  685. recipe.PrefixPath = CurrentChamberType + "\\" + CurrentProcessType;
  686. recipe.Creator = BaseApp.Instance.UserContext.LoginName;
  687. recipe.CreateTime = DateTime.Now;
  688. recipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  689. recipe.ReviseTime = DateTime.Now;
  690. recipe.Description = description;
  691. recipe.RecipeLevel = LevelDisplay;
  692. recipe.RecipePermission = "Free";
  693. if (!Save(recipe, true))
  694. return;
  695. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, recipeName, false);
  696. }
  697. private void ReloadRecipeFileList(string chamberType, string processType, string selectedFile, bool selectionIsFolder)
  698. {
  699. ProcessTypeFileItem item = ProcessTypeFileList.FirstOrDefault(x => x.ProcessType == processType);
  700. if (item == null)
  701. {
  702. LOG.Write("error reload recipe file list, type = " + processType);
  703. }
  704. var prefix = $"{ChamberType[ChamberTypeIndexSelection]}\\{item.ProcessType}";
  705. var recipes = _recipeProvider.GetXmlRecipeList(prefix);
  706. item.FileListByProcessType = RecipeSequenceTreeBuilder.BuildFileNode(prefix, selectedFile, selectionIsFolder, recipes)[0].Files;
  707. item.InvokePropertyChanged();
  708. }
  709. private bool IsExist(string fullPath, bool isFile)
  710. {
  711. for (int i = 0; i < ProcessTypeFileList.Count; i++)
  712. {
  713. if (ProcessTypeFileList[i].ProcessType == CurrentProcessType)
  714. {
  715. if (ProcessTypeFileList[i].FileListByProcessType.Count == 0)
  716. return false;
  717. return FindFile(fullPath, ProcessTypeFileList[i].FileListByProcessType[0].Parent, isFile);
  718. }
  719. }
  720. return true;
  721. }
  722. private bool FindFile(string path, FileNode root, bool isFile)
  723. {
  724. if (root.FullPath.ToLower() == path && !isFile)
  725. {
  726. return true;
  727. }
  728. foreach (var node in root.Files)
  729. {
  730. if (isFile && node.IsFile && node.FullPath.ToLower() == path)
  731. return true;
  732. if (!node.IsFile && FindFile(path, node, isFile))
  733. return true;
  734. }
  735. return false;
  736. }
  737. public void SaveAsRecipe()
  738. {
  739. if (CurrentFileNode == null || !CurrentFileNode.IsFile)
  740. return;
  741. if (IsChanged)
  742. {
  743. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
  744. if (selection == DialogButton.Cancel)
  745. return;
  746. if (selection == DialogButton.Yes)
  747. {
  748. this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  749. this.CurrentRecipe.ReviseTime = DateTime.Now;
  750. this.Save(this.CurrentRecipe, false);
  751. }
  752. }
  753. InputFileNameDialogViewModel dialog = new InputFileNameDialogViewModel("Input New Recipe Name", ProcessTypeFileList[0].FileListByProcessType);
  754. dialog.FileName = CurrentFileNode.Name;
  755. WindowManager wm = new WindowManager();
  756. bool? dialogReturn = wm.ShowDialog(dialog);
  757. if (!dialogReturn.HasValue || !dialogReturn.Value)
  758. return;
  759. string recipeName = dialog.FileName.Trim();
  760. string filepath = dialog.FilePath;
  761. if (string.IsNullOrEmpty(dialog.FileName))
  762. {
  763. DialogBox.ShowWarning("Recipe file name should not be empty");
  764. return;
  765. }
  766. string prefix = CurrentChamberType + "\\" + CurrentProcessType + "\\" + filepath;
  767. string processType = string.Empty;
  768. string folder = CurrentFileNode.FullPath;
  769. if (CurrentFileNode.IsFile)
  770. {
  771. folder = folder.Substring(0, folder.LastIndexOf("\\") + 1);
  772. }
  773. if (!string.IsNullOrEmpty(folder))
  774. recipeName = folder + "\\" + recipeName;
  775. if (CurrentFileNode.FullPath == recipeName)
  776. return;
  777. if (IsExist(recipeName, true))
  778. {
  779. DialogBox.ShowWarning($"Can not copy to {recipeName}, Recipe with the same name already exist.");
  780. return;
  781. }
  782. if (recipeName.Length > 200)
  783. {
  784. DialogBox.ShowWarning($"Can not create folder {recipeName}, Folder name too long, should be less 200.");
  785. return;
  786. }
  787. CurrentRecipe.Creator = BaseApp.Instance.UserContext.LoginName;
  788. CurrentRecipe.CreateTime = DateTime.Now;
  789. CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  790. CurrentRecipe.ReviseTime = DateTime.Now;
  791. CurrentRecipe.Description = CurrentRecipe.Description + ". Renamed from " + CurrentFileNode.Name;
  792. _recipeProvider.SaveAsRecipe(prefix, recipeName, CurrentRecipe.GetXmlString());
  793. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, recipeName, false);
  794. }
  795. public void RenameRecipe()
  796. {
  797. if (CurrentFileNode == null || !CurrentFileNode.IsFile)
  798. return;
  799. if (IsChanged)
  800. {
  801. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No | DialogButton.Cancel, DialogType.CONFIRM, $"Recipe {CurrentRecipe.Name} is changed, do you want to save it?");
  802. if (selection == DialogButton.Cancel)
  803. return;
  804. if (selection == DialogButton.Yes)
  805. {
  806. this.CurrentRecipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  807. this.CurrentRecipe.ReviseTime = DateTime.Now;
  808. this.Save(this.CurrentRecipe, false);
  809. }
  810. }
  811. InputFileNameDialogViewModel dialog = new InputFileNameDialogViewModel("Input New Recipe Name", ProcessTypeFileList[0].FileListByProcessType, CurrentRecipe.Description, Visibility.Hidden);
  812. dialog.FileName = CurrentFileNode.Name;
  813. WindowManager wm = new WindowManager();
  814. bool? dialogReturn = wm.ShowDialog(dialog);
  815. if (!dialogReturn.HasValue || !dialogReturn.Value)
  816. return;
  817. string recipeName = dialog.FileName.Trim();
  818. if (string.IsNullOrEmpty(dialog.FileName))
  819. {
  820. DialogBox.ShowWarning("Recipe file name should not be empty");
  821. return;
  822. }
  823. string prefix = CurrentChamberType + "\\" + CurrentProcessType;
  824. string processType = string.Empty;
  825. string newName = CurrentFileNode.FullPath.Substring(0, CurrentFileNode.FullPath.LastIndexOf("\\") + 1);
  826. if (!string.IsNullOrEmpty(newName))
  827. newName = newName + recipeName;
  828. else
  829. newName = recipeName;
  830. if (newName == CurrentFileNode.FullPath)
  831. return;
  832. if (IsExist(newName.ToLower(), true))
  833. {
  834. DialogBox.ShowWarning($"Can not rename to {newName}, Recipe with the same name already exist.");
  835. return;
  836. }
  837. if (newName.Length > 200)
  838. {
  839. DialogBox.ShowWarning($"Can not create folder {newName}, Folder name too long, should be less 200.");
  840. return;
  841. }
  842. if(_recipeProvider.RenameRecipe(prefix, CurrentFileNode.FullPath, newName))
  843. {
  844. UIGlobalVariable.Instance.ProcessModifiedRecipe[CurrentFileNode.FullPath] = $"Rename Recipe from [{CurrentFileNode.FullPath}] to {newName} {DateTime.Now}";
  845. }
  846. CurrentRecipe.Name = dialog.FileName;
  847. CurrentRecipe.Description = dialog.Comment;
  848. this.Save(CurrentRecipe, false);
  849. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, newName, false);
  850. }
  851. public void DeleteRecipe()
  852. {
  853. if (CurrentFileNode == null || !CurrentFileNode.IsFile)
  854. return;
  855. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No, DialogType.CONFIRM,
  856. $"Are you sure you want to delete \r\n {CurrentFileNode.FullPath}?");
  857. if (selection == DialogButton.No)
  858. return;
  859. string nextFocus = CurrentFileNode.Parent.FullPath;
  860. bool isFolder = true;
  861. if (CurrentFileNode.Parent.Files.Count > 1)
  862. {
  863. for (int i = 0; i < CurrentFileNode.Parent.Files.Count; i++)
  864. {
  865. if (CurrentFileNode.Parent.Files[i] == CurrentFileNode)
  866. {
  867. if (i == 0)
  868. {
  869. nextFocus = CurrentFileNode.Parent.Files[i + 1].FullPath;
  870. isFolder = !CurrentFileNode.Parent.Files[i + 1].IsFile;
  871. }
  872. else
  873. {
  874. nextFocus = CurrentFileNode.Parent.Files[i - 1].FullPath;
  875. isFolder = !CurrentFileNode.Parent.Files[i - 1].IsFile;
  876. }
  877. }
  878. }
  879. }
  880. if(_recipeProvider.DeleteRecipe(CurrentFileNode.PrefixPath, CurrentFileNode.FullPath))
  881. {
  882. UIGlobalVariable.Instance.ProcessModifiedRecipe[CurrentFileNode.FullPath] = $"Delete Recipe from [{CurrentFileNode.FullPath}] {DateTime.Now}";
  883. }
  884. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, nextFocus, isFolder);
  885. }
  886. public void ReloadRecipe()
  887. {
  888. if (this.editMode == EditMode.Normal || this.editMode == EditMode.Edit)
  889. {
  890. this.LoadData(CurrentRecipe.PrefixPath, CurrentRecipe.Name);
  891. this.UpdateView();
  892. }
  893. }
  894. public void SaveToAll()
  895. {
  896. if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
  897. {
  898. DialogBox.ShowWarning($"Save failed, {CurrentRecipe.Name} is not a valid recipe file");
  899. return;
  900. }
  901. var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.No,
  902. DialogType.CONFIRM, $"Do you want to save to all? \r\n This will replace all the other chamber recipe content");
  903. if (selection == DialogButton.No)
  904. return;
  905. CurrentRecipe.SaveTo(Chambers.ToArray());
  906. Save(this.CurrentRecipe, false);
  907. }
  908. public void SaveTo()
  909. {
  910. if (!CurrentRecipe.IsCompatibleWithCurrentFormat)
  911. {
  912. DialogBox.ShowWarning($"Save failed, {CurrentRecipe.Name} is not a valid recipe file");
  913. return;
  914. }
  915. SaveToDialogViewModel dialog = new SaveToDialogViewModel("Select which chamber to copy to", SelectedChamber, Chambers.ToList());
  916. WindowManager wm = new WindowManager();
  917. bool? dialogReturn = wm.ShowDialog(dialog);
  918. if (!dialogReturn.HasValue || !dialogReturn.Value)
  919. return;
  920. List<string> chambers = new List<string>();
  921. foreach (var dialogChamber in dialog.Chambers)
  922. {
  923. if (dialogChamber.IsEnabled && dialogChamber.IsChecked)
  924. chambers.Add(dialogChamber.Name);
  925. }
  926. if (chambers.Count == 0)
  927. return;
  928. CurrentRecipe.SaveTo(chambers.ToArray());
  929. Save(this.CurrentRecipe, false);
  930. }
  931. public void ViewRecipe()
  932. {
  933. CGlobal.RecipeLayoutEditViewEnable = false;
  934. PopupPage();
  935. }
  936. public void EditRecipe()
  937. {
  938. CGlobal.RecipeLayoutEditViewEnable = true;
  939. if (PopupPage())
  940. {
  941. UIGlobalVariable.Instance.LayoutModifiedRecipe[CurrentFileNode.FullPath] = $"Edit {DateTime.Now}";
  942. }
  943. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, CurrentFileNode.Name, false);
  944. }
  945. #endregion
  946. #region Steps
  947. public void SaveRecipe()
  948. {
  949. if (this.IsChanged)
  950. {
  951. this.Save(this.CurrentRecipe, false);
  952. }
  953. }
  954. public bool Save(RecipeDataLayout recipe, bool createNew)
  955. {
  956. bool result = false;
  957. if (string.IsNullOrEmpty(recipe.Name))
  958. {
  959. MessageBox.Show("Recipe name can't be empty");
  960. return false;
  961. }
  962. recipe.Revisor = BaseApp.Instance.UserContext.LoginName;
  963. recipe.ReviseTime = DateTime.Now;
  964. result = this._recipeProvider.SaveRecipe(recipe.PrefixPath, recipe.Name, recipe.GetXmlString());
  965. if (result)
  966. {
  967. //recipe.DataSaved();
  968. this.editMode = EditMode.Normal;
  969. this.UpdateView();
  970. }
  971. else
  972. {
  973. MessageBox.Show("Save failed!");
  974. }
  975. return result;
  976. }
  977. private TreeViewItem GetParentObjectEx<TreeViewItem>(DependencyObject obj) where TreeViewItem : FrameworkElement
  978. {
  979. DependencyObject parent = VisualTreeHelper.GetParent(obj);
  980. while (parent != null)
  981. {
  982. if (parent is TreeViewItem)
  983. {
  984. return (TreeViewItem)parent;
  985. }
  986. parent = VisualTreeHelper.GetParent(parent);
  987. }
  988. return null;
  989. }
  990. public void TreeRightMouseDown(MouseButtonEventArgs e)
  991. {
  992. var item = GetParentObjectEx<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
  993. if (item != null)
  994. {
  995. item.Focus();
  996. }
  997. }
  998. #endregion
  999. private void ClearData()
  1000. {
  1001. this.editMode = EditMode.None;
  1002. this.CurrentRecipe.Clear();
  1003. this.CurrentRecipe.Name = string.Empty;
  1004. this.CurrentRecipe.Description = string.Empty;
  1005. }
  1006. private void LoadData(string prefixPath, string recipeName)
  1007. {
  1008. CurrentRecipe.Clear();
  1009. var recipeContent = _recipeProvider.LoadRecipe(prefixPath, recipeName);
  1010. if (string.IsNullOrEmpty(recipeContent))
  1011. {
  1012. MessageBox.Show($"{prefixPath}\\{recipeName} is empty, please confirm the file is valid.");
  1013. return;
  1014. }
  1015. CurrentRecipe.RecipeChamberType = "OriginChamber";
  1016. CurrentRecipe.RecipeVersion = _columnBuilder.RecipeVersion;
  1017. _columnBuilder.Build(CurrentProcessType);
  1018. CurrentRecipe.InitData(prefixPath, recipeName, recipeContent, _columnBuilder.Configs, SelectedChamber);
  1019. this.editMode = EditMode.Normal;
  1020. }
  1021. private void UpdateView()
  1022. {
  1023. bool isFileSelected = CurrentFileNode != null && CurrentFileNode.IsFile;
  1024. this.NotifyOfPropertyChange("CurrentRecipe");
  1025. }
  1026. public void SavePermission()
  1027. {
  1028. RecipePermissionSelectViewModel dialog = new RecipePermissionSelectViewModel("Save recipe and permission", CurrentFileNode.Permission, CurrentRecipe.Description);
  1029. WindowManager wm = new WindowManager();
  1030. bool? dialogReturn = wm.ShowDialog(dialog);
  1031. if (!dialogReturn.HasValue || !dialogReturn.Value)
  1032. return;
  1033. CurrentRecipe.RecipePermission = dialog.RecipePermission;
  1034. CurrentRecipe.Description = dialog.Comment;
  1035. this.Save(CurrentRecipe, false);
  1036. ReloadRecipeFileList(CurrentChamberType, CurrentProcessType, CurrentFileNode.Parent.FullPath, false);
  1037. }
  1038. private bool PopupPage()
  1039. {
  1040. if (CurrentFileNode == null || !CurrentFileNode.IsFile)
  1041. return false;
  1042. var windowManager = IoC.Get<IWindowManager>();
  1043. var currentProcessType = CurrentProcessType;
  1044. if (CurrentProcessType == "Expert Layout")
  1045. {
  1046. RecipeLayoutExpertEditViewModel recipeLayoutExpertEditViewModel = new RecipeLayoutExpertEditViewModel(CurrentRecipe.PrefixPath, CurrentRecipe.Name, CurrentFileNode.Permission);
  1047. return (windowManager as WindowManager)?.ShowDialogWithTitle(recipeLayoutExpertEditViewModel, null, "Layout Setting")==true;
  1048. }
  1049. else
  1050. {
  1051. RecipeLayoutEditViewModel sdSettingViewModel = new RecipeLayoutEditViewModel(CurrentRecipe.PrefixPath, CurrentRecipe.Name, _columnBuilder, CurrentFileNode.Permission);
  1052. return (windowManager as WindowManager)?.ShowDialogWithTitle(sdSettingViewModel, null, "Layout Setting")==true;
  1053. }
  1054. }
  1055. }
  1056. }