ProjectTree.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. unit ProjectTree;
  2. interface
  3. uses
  4. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5. Dialogs, ComCtrls, CommCtrl, ExtCtrls, ImgList, Menus, JvComponent,
  6. JvDockControlForm, JvExComCtrls, JvComCtrls, JvDotNetControls, Main,
  7. VirtualTrees;
  8. type
  9. PProjectTreeData = ^TProjectTreeData;
  10. TProjectTreeData = record
  11. pLuaUnit: TLuaUnit;
  12. pLuaPrj: TLuaProject;
  13. ActiveProject: Boolean;
  14. ToKeep: Boolean;
  15. Deleting: Boolean;
  16. end;
  17. TfrmProjectTree = class(TForm)
  18. Panel1: TPanel;
  19. imlProjectTree: TImageList;
  20. ppmProjectTree: TPopupMenu;
  21. ActivateSelectedProject1: TMenuItem;
  22. N1: TMenuItem;
  23. UnloadFileProject1: TMenuItem;
  24. JvDockClient1: TJvDockClient;
  25. N2: TMenuItem;
  26. AddUnittoProject1: TMenuItem;
  27. RemoveUnitFromProject1: TMenuItem;
  28. Options1: TMenuItem;
  29. vstProjectTree: TVirtualStringTree;
  30. procedure UnloadFileProject1Click(Sender: TObject);
  31. procedure ppmProjectTreePopup(Sender: TObject);
  32. procedure vstProjectTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  33. procedure vstProjectTreeGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
  34. procedure vstProjectTreeGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
  35. procedure vstProjectTreePaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
  36. procedure vstProjectTreeDblClick(Sender: TObject);
  37. procedure vstProjectTreeMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  38. procedure vstProjectTreeAfterItemPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; ItemRect: TRect);
  39. function GetNodeInTree(sFileName, sProjectName: String): PVirtualNode;
  40. private
  41. { Private declarations }
  42. public
  43. { Public declarations }
  44. procedure BuildProjectTree(HandleNotifier: Boolean = True);
  45. end;
  46. var
  47. frmProjectTree: TfrmProjectTree;
  48. implementation
  49. {$R *.dfm}
  50. procedure TfrmProjectTree.vstProjectTreeDblClick(Sender: TObject);
  51. var
  52. pNode: PVirtualNode;
  53. pData: PProjectTreeData;
  54. pLuaUnit: TLuaUnit;
  55. begin
  56. pNode := vstProjectTree.GetFirstSelected;
  57. if Assigned(pNode) then
  58. begin
  59. pData := vstProjectTree.GetNodeData(pNode);
  60. if Assigned(pData.pLuaUnit) then
  61. begin
  62. pLuaUnit := pData.pLuaUnit;
  63. if pLuaUnit.IsLoaded then
  64. begin
  65. if LuaOpenedUnits.IndexOf(pLuaUnit) = -1 then
  66. begin
  67. // Insert new tab in the page control to view the requested unit
  68. frmMain.AddFileInTab(pLuaUnit);
  69. end
  70. else
  71. begin
  72. // Activate the tab associated to the requested unit
  73. frmMain.jvUnitBar.SelectedTab := frmMain.GetAssociatedTab(pLuaUnit);
  74. if pLuaUnit.HasChanged then
  75. frmMain.stbMain.Panels[2].Text := 'Modified'
  76. else
  77. frmMain.stbMain.Panels[2].Text := '';
  78. frmMain.synEditClick(pLuaUnit.synUnit);
  79. end;
  80. end;
  81. end;
  82. end;
  83. frmMain.CheckButtons;
  84. end;
  85. function TfrmProjectTree.GetNodeInTree(sFileName, sProjectName: String): PVirtualNode;
  86. var
  87. pNode: PVirtualNode;
  88. pData: PProjectTreeData;
  89. begin
  90. Result := nil;
  91. pNode := vstProjectTree.GetFirst;
  92. while Assigned(pNode) do
  93. begin
  94. pData := vstProjectTree.GetNodeData(pNode);
  95. if sProjectName <> '' then
  96. begin
  97. if (Assigned(pData.pLuaUnit) and (not pData.Deleting)) then
  98. begin
  99. if ((pData.pLuaUnit.pPrjOwner.sPrjName = sProjectName) or (sProjectName = '[@@SingleUnits@@]')) then
  100. begin
  101. if pData.pLuaUnit.sName = sFileName then
  102. begin
  103. Result := pNode;
  104. Break;
  105. end;
  106. end;
  107. end;
  108. end
  109. else
  110. begin
  111. if Assigned(pData.pLuaPrj) then
  112. begin
  113. if pData.pLuaPrj.sPrjName = sFileName then
  114. begin
  115. Result := pNode;
  116. Break;
  117. end;
  118. end;
  119. end;
  120. pNode := vstProjectTree.GetNext(pNode);
  121. end;
  122. end;
  123. procedure TfrmProjectTree.BuildProjectTree(HandleNotifier: Boolean);
  124. var
  125. pTempPrj: TLuaProject;
  126. pPrjNode, pUnitNode, pSingleUnitLastNode: PVirtualNode;
  127. pData: PProjectTreeData;
  128. x, y: Integer;
  129. // Go through all nodes of the tree and set their ToKeep flag to false
  130. procedure UnflagAllExpanded(pTree: TVirtualStringTree);
  131. var
  132. pNode: PVirtualNode;
  133. pData: PProjectTreeData;
  134. begin
  135. pNode := pTree.GetFirst;
  136. while Assigned(pNode) do
  137. begin
  138. pData := pTree.GetNodeData(pNode);
  139. pData.ToKeep := False;
  140. pNode := pTree.GetNext(pNode);
  141. end;
  142. end;
  143. // Deletes all nodes for wich their ToKeep flag is still on false
  144. procedure CleanTree(pTree: TVirtualStringTree);
  145. var
  146. pNode, pPrevious: PVirtualNode;
  147. pData: PProjectTreeData;
  148. begin
  149. pNode := pTree.GetFirst;
  150. while Assigned(pNode) do
  151. begin
  152. pData := pTree.GetNodeData(pNode);
  153. if not pData.ToKeep then
  154. begin
  155. pPrevious := pTree.GetPrevious(pNode);
  156. pTree.DeleteNode(pNode);
  157. pNode := pPrevious;
  158. end;
  159. pNode := pTree.GetNext(pNode);
  160. end;
  161. end;
  162. begin
  163. // Initialize stuff
  164. pPrjNode := nil;
  165. pUnitNode := nil;
  166. pSingleUnitLastNode := nil;
  167. // If the changes notifier is handled, we stop it while building the tree
  168. if HandleNotifier then
  169. begin
  170. frmMain.jvchnNotifier.Active := False;
  171. frmMain.jvchnNotifier.Notifications.Clear;
  172. end;
  173. vstProjectTree.BeginUpdate;
  174. UnflagAllExpanded(vstProjectTree);
  175. for x := 0 to LuaProjects.Count - 1 do
  176. begin
  177. pTempPrj := TLuaProject(LuaProjects.Items[x]);
  178. pPrjNode := GetNodeInTree(pTempPrj.sPrjName, '');
  179. if not Assigned(pPrjNode) then
  180. begin
  181. if pTempPrj.sPrjName <> '[@@SingleUnits@@]' then
  182. begin
  183. // Create the node
  184. pPrjNode := vstProjectTree.AddChild(vstProjectTree.RootNode);
  185. pData := vstProjectTree.GetNodeData(pPrjNode);
  186. pData.pLuaUnit := nil;
  187. pData.pLuaPrj := pTempPrj;
  188. pData.ActiveProject := (pTempPrj = ActiveProject);
  189. pData.ToKeep := True;
  190. pData.Deleting := False;
  191. // Adding project root to change notifier...
  192. if ((not pTempPrj.IsNew) and HandleNotifier) then
  193. frmMain.AddToNotifier(ExtractFileDir(pTempPrj.sPrjPath));
  194. end
  195. else
  196. pPrjNode := pSingleUnitLastNode;
  197. end
  198. else
  199. begin
  200. // Update the node's data
  201. pData := vstProjectTree.GetNodeData(pPrjNode);
  202. pData.pLuaUnit := nil;
  203. pData.pLuaPrj := pTempPrj;
  204. pData.ActiveProject := (pTempPrj = ActiveProject);
  205. pData.ToKeep := True;
  206. pData.Deleting := False;
  207. // Adding project root to change notifier...
  208. if ((not pTempPrj.IsNew) and HandleNotifier) then
  209. frmMain.AddToNotifier(ExtractFileDir(pTempPrj.sPrjPath));
  210. end;
  211. for y := 0 to pTempPrj.lstUnits.Count - 1 do
  212. begin
  213. pUnitNode := GetNodeInTree(TLuaUnit(pTempPrj.lstUnits.Items[y]).sName, pTempPrj.sPrjName);
  214. if not Assigned(pUnitNode) then
  215. begin
  216. // Adding single unit (projectless) to the tree
  217. if pTempPrj.sPrjName = '[@@SingleUnits@@]' then
  218. begin
  219. if not Assigned(pPrjNode) then
  220. pUnitNode := vstProjectTree.InsertNode(vstProjectTree.RootNode, amInsertBefore)
  221. else
  222. pUnitNode := vstProjectTree.InsertNode(pPrjNode, amInsertAfter);
  223. // Update last single unit node
  224. pSingleUnitLastNode := pUnitNode;
  225. end
  226. else
  227. pUnitNode := vstProjectTree.AddChild(pPrjNode);
  228. // Create the node
  229. pData := vstProjectTree.GetNodeData(pUnitNode);
  230. pData.pLuaUnit := TLuaUnit(pTempPrj.lstUnits.Items[y]);
  231. pData.pLuaPrj := nil;
  232. pData.ActiveProject := False;
  233. pData.ToKeep := True;
  234. pData.Deleting := False;
  235. // Adding unit root to change notifier...
  236. if ((not TLuaUnit(pTempPrj.lstUnits.Items[y]).IsNew) and HandleNotifier) then
  237. frmMain.AddToNotifier(ExtractFileDir(TLuaUnit(pTempPrj.lstUnits.Items[y]).sUnitPath));
  238. end
  239. else
  240. begin
  241. // Update the node's data
  242. pData := vstProjectTree.GetNodeData(pUnitNode);
  243. pData.pLuaUnit := TLuaUnit(pTempPrj.lstUnits.Items[y]);
  244. pData.pLuaPrj := nil;
  245. pData.ActiveProject := False;
  246. pData.ToKeep := True;
  247. pData.Deleting := False;
  248. // Adding unit root to change notifier...
  249. if ((not TLuaUnit(pTempPrj.lstUnits.Items[y]).IsNew) and HandleNotifier) then
  250. frmMain.AddToNotifier(ExtractFileDir(TLuaUnit(pTempPrj.lstUnits.Items[y]).sUnitPath));
  251. end;
  252. end;
  253. end;
  254. CleanTree(vstProjectTree);
  255. vstProjectTree.EndUpdate;
  256. // Set back on the changes notifier if required
  257. if ((frmMain.jvchnNotifier.Notifications.Count > 0) and HandleNotifier) then
  258. frmMain.jvchnNotifier.Active := True;
  259. end;
  260. procedure TfrmProjectTree.UnloadFileProject1Click(Sender: TObject);
  261. var
  262. pLuaPrj: TLuaProject;
  263. pLuaUnit: TLuaUnit;
  264. Answer, x: Integer;
  265. UnitsToDelete: TList;
  266. pNode: PVirtualNode;
  267. pData: PProjectTreeData;
  268. begin
  269. pNode := vstProjectTree.GetFirstSelected;
  270. if Assigned(pNode) then
  271. begin
  272. pData := vstProjectTree.GetNodeData(pNode);
  273. UnitsToDelete := TList.Create;
  274. // Case where the selected file was a project
  275. if Assigned(pData.pLuaPrj) then
  276. begin
  277. pLuaPrj := pData.pLuaPrj;
  278. // Ssaving any new or modified project's files
  279. for x := 0 to pLuaPrj.lstUnits.Count - 1 do
  280. begin
  281. pLuaUnit := TLuaUnit(pLuaPrj.lstUnits.Items[x]);
  282. if ((pLuaUnit.HasChanged) or (pLuaUnit.IsNew)) then
  283. begin
  284. Answer := Application.MessageBox(PChar('Save changes to unit "'+pLuaUnit.sName+'"?'), 'LuaEdit', MB_YESNOCANCEL+MB_ICONQUESTION);
  285. if Answer = IDYES then
  286. begin
  287. if SaveUnitsInc then
  288. pLuaUnit.SaveUnitInc(pLuaUnit.sUnitPath)
  289. else
  290. pLuaUnit.SaveUnit(pLuaUnit.sUnitPath);
  291. end
  292. else if Answer = IDCANCEL then
  293. begin
  294. UnitsToDelete.Free;
  295. Exit;
  296. end;
  297. end;
  298. UnitsToDelete.Add(pLuaUnit);
  299. end;
  300. // saving any new or modified project
  301. if ((pLuaPrj.HasChanged) or (pLuaPrj.IsNew)) then
  302. begin
  303. Answer := Application.MessageBox(PChar('Save changes to project "'+pLuaPrj.sPrjName+'"?'), 'LuaEdit', MB_YESNOCANCEL+MB_ICONQUESTION);
  304. if Answer = IDYES then
  305. begin
  306. if SaveProjectsInc then
  307. pLuaPrj.SaveProjectInc(pLuaPrj.sPrjPath)
  308. else
  309. pLuaPrj.SaveProject(pLuaPrj.sPrjPath);
  310. end
  311. else if Answer = IDCANCEL then
  312. begin
  313. UnitsToDelete.Free;
  314. Exit;
  315. end;
  316. end;
  317. end
  318. else if Assigned(pData.pLuaUnit) then
  319. begin
  320. pLuaUnit := pData.pLuaUnit;
  321. if pLuaUnit.pPrjOwner.sPrjName = '[@@SingleUnits@@]' then
  322. begin
  323. if ((pLuaUnit.HasChanged) or (pLuaUnit.IsNew)) then
  324. begin
  325. Answer := Application.MessageBox(PChar('Save changes to unit "'+pLuaUnit.sName+'"?'), 'LuaEdit', MB_YESNOCANCEL+MB_ICONQUESTION);
  326. if Answer = IDYES then
  327. begin
  328. if SaveUnitsInc then
  329. pLuaUnit.SaveUnitInc(pLuaUnit.sUnitPath)
  330. else
  331. pLuaUnit.SaveUnit(pLuaUnit.sUnitPath);
  332. end
  333. else if Answer = IDCANCEL then
  334. begin
  335. UnitsToDelete.Free;
  336. Exit;
  337. end;
  338. end;
  339. UnitsToDelete.Add(pLuaUnit);
  340. end;
  341. end;
  342. // Free and close units and project
  343. for x := UnitsToDelete.Count - 1 downto 0 do
  344. begin
  345. pLuaUnit := TLuaUnit(UnitsToDelete.Items[x]);
  346. if frmMain.GetAssociatedTab(pLuaUnit) <> nil then
  347. begin
  348. frmMain.jvUnitBar.Tabs.Delete(frmMain.GetAssociatedTab(pLuaUnit).Index);
  349. LuaOpenedUnits.Remove(pLuaUnit);
  350. end;
  351. pData.Deleting := True;
  352. pLuaUnit.pPrjOwner.lstUnits.Remove(pLuaUnit);
  353. pLuaUnit.Free;
  354. end;
  355. if Assigned(pLuaPrj) then
  356. LuaProjects.Remove(pLuaPrj);
  357. if pLuaPrj = ActiveProject then
  358. begin
  359. // Try to find another project to automatically set to the active one
  360. if LuaProjects.Count > 1 then
  361. ActiveProject := LuaProjects.Items[LuaProjects.Count - 1]
  362. else
  363. ActiveProject := nil;
  364. end;
  365. // Reset LuaEdit main form caption to its initial value
  366. if not Assigned(ActiveProject) then
  367. frmMain.Caption := 'LuaEdit';
  368. // Initialize stuff...
  369. UnitsToDelete.Free;
  370. BuildProjectTree;
  371. frmMain.CheckButtons;
  372. end;
  373. end;
  374. procedure TfrmProjectTree.ppmProjectTreePopup(Sender: TObject);
  375. begin
  376. frmMain.DoMainMenuProjectExecute;
  377. end;
  378. procedure TfrmProjectTree.vstProjectTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  379. var
  380. pData: PProjectTreeData;
  381. begin
  382. // Set text to display for all nodes
  383. if TextType = ttNormal then
  384. begin
  385. case Column of
  386. 0:
  387. begin
  388. pData := Sender.GetNodeData(Node);
  389. pData.ToKeep := True;
  390. if Assigned(pData.pLuaPrj) then
  391. CellText := pData.pLuaPrj.sPrjName
  392. else
  393. CellText := pData.pLuaUnit.sName;
  394. end;
  395. 1:
  396. begin
  397. pData := Sender.GetNodeData(Node);
  398. pData.ToKeep := True;
  399. if Assigned(pData.pLuaPrj) then
  400. CellText := pData.pLuaPrj.sPrjPath
  401. else
  402. CellText := pData.pLuaUnit.sUnitPath;
  403. end;
  404. end;
  405. end;
  406. end;
  407. procedure TfrmProjectTree.vstProjectTreePaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType);
  408. var
  409. pData: PProjectTreeData;
  410. begin
  411. pData := Sender.GetNodeData(Node);
  412. if Assigned(pData.pLuaPrj) then
  413. begin
  414. // Set bold style on the active project node
  415. if pData.ActiveProject then
  416. begin
  417. TargetCanvas.Font.Style := [fsBold];
  418. frmMain.Caption := 'LuaEdit - ' + TLuaProject(pData.pLuaPrj).sPrjName;
  419. end;
  420. end
  421. else
  422. begin
  423. // Set disabled color for non-loaded units
  424. if not pData.pLuaUnit.IsLoaded then
  425. begin
  426. TargetCanvas.Font.Color := clInactiveCaption;
  427. TargetCanvas.Pen.Color := clInactiveCaption;
  428. end;
  429. end;
  430. end;
  431. procedure TfrmProjectTree.vstProjectTreeGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
  432. var
  433. pData: PProjectTreeData;
  434. begin
  435. // Set image index for all nodes
  436. if Column = 0 then
  437. begin
  438. pData := Sender.GetNodeData(Node);
  439. if Assigned(pData.pLuaPrj) then
  440. begin
  441. ImageIndex := 0;
  442. end
  443. else
  444. begin
  445. if pData.pLuaUnit.IsLoaded then
  446. ImageIndex := 1
  447. else
  448. ImageIndex := 2;
  449. end;
  450. end;
  451. end;
  452. procedure TfrmProjectTree.vstProjectTreeGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
  453. begin
  454. NodeDataSize := SizeOf(TProjectTreeData);
  455. end;
  456. procedure TfrmProjectTree.vstProjectTreeMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  457. begin
  458. frmMain.CheckButtons;
  459. end;
  460. procedure TfrmProjectTree.vstProjectTreeAfterItemPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; ItemRect: TRect);
  461. var
  462. pRect: TRect;
  463. begin
  464. pRect := ItemRect;
  465. InflateRect(pRect, 0, 1);
  466. pRect.Right := pRect.Left + 22;
  467. TargetCanvas.Brush.Color := clWhite;
  468. TargetCanvas.FillRect(pRect);
  469. // Draw node button since the noda has some child
  470. if ((Node.Parent = Sender.RootNode) and (Node.ChildCount <> 0)) then
  471. begin
  472. // Draw the frame around the button
  473. TargetCanvas.Pen.Color := clBtnShadow;
  474. TargetCanvas.Rectangle(5, 4, 14, 13);
  475. TargetCanvas.MoveTo(14, 8);
  476. TargetCanvas.LineTo(20, 8);
  477. TargetCanvas.Pen.Color := clBlack;
  478. if not (vsExpanded in Node.States) then
  479. begin
  480. // Draw expandable node button (plus sign)
  481. TargetCanvas.MoveTo(7, 8);
  482. TargetCanvas.LineTo(12, 8);
  483. TargetCanvas.MoveTo(9, 6);
  484. TargetCanvas.LineTo(9, 11);
  485. end
  486. else
  487. begin
  488. // Draw non-expandable node button (minus sign)
  489. TargetCanvas.MoveTo(7, 8);
  490. TargetCanvas.LineTo(12, 8);
  491. end;
  492. end;
  493. end;
  494. end.