1 {
2 ***************************************************************************
3 * *
4 * This source is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This code is distributed in the hope that it will be useful, but *
10 * WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * General Public License for more details. *
13 * *
14 * A copy of the GNU General Public License is available on the World *
15 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
16 * obtain it by writing to the Free Software Foundation, *
17 * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
18 * *
19 ***************************************************************************
20
21 Abstract:
22 Functions to deal with different kinds of source files like
23 units, projects, packages and their related IDE features.
24 The code is copied and refactored from the huge main.pp. The goal was to not
25 call methods defined in TMainIDE but there are still some calls doing it.
26 }
27 unit SourceFileManager;
28
29 {$mode objfpc}{$H+}
30 {$MODESWITCH ADVANCEDRECORDS}
31
32 interface
33
34 uses
35 Classes, SysUtils, typinfo, math, fpjson, Laz_AVL_Tree,
36 // LCL
37 Controls, Forms, Dialogs, LCLIntf, LCLType, LclStrConsts,
38 LResources, LCLMemManager,
39 // LazUtils
40 LConvEncoding, LazFileCache, FileUtil, LazFileUtils, LazLoggerBase, LazUtilities,
41 LazUTF8, LazTracer, AvgLvlTree,
42 // Codetools
43 BasicCodeTools, CodeToolsStructs, CodeToolManager, FileProcs, DefineTemplates,
44 CodeCache, CodeTree, FindDeclarationTool, KeywordFuncLists,
45 // IdeIntf
46 IDEDialogs, PropEdits, IDEMsgIntf, LazIDEIntf, MenuIntf, NewItemIntf,
47 IDEWindowIntf, ProjectIntf, PackageIntf, PackageDependencyIntf, FormEditingIntf,
48 IDEExternToolIntf, ObjectInspector, UnitResources, ComponentReg,
49 SrcEditorIntf, EditorSyntaxHighlighterDef,
50 // IDE
51 IDEProcs, DialogProcs, IDEProtocol, LazarusIDEStrConsts, NewDialog, NewProjectDlg,
52 MainBase, MainBar, MainIntf, Project, ProjectDefs, ProjectInspector, CompilerOptions,
53 SourceSynEditor, SourceEditor, EditorOptions, EnvironmentOpts, CustomFormEditor,
54 ControlSelection, FormEditor, EmptyMethodsDlg, BaseDebugManager, TransferMacros,
55 BuildManager, EditorMacroListViewer, FindRenameIdentifier, BuildModesManager,
56 ViewUnit_Dlg, InputHistory, CheckLFMDlg, etMessagesWnd,
57 ConvCodeTool, BasePkgManager, PackageDefs, PackageSystem, Designer, DesignerProcs;
58
59 type
60
61 TBookmarkCommandsStamp = record
62 private
63 FBookmarksStamp: Int64;
64 public
Changednull65 function Changed(ABookmarksStamp: Int64): Boolean;
66 end;
67
68 TFileCommandsStamp = record
69 private
70 FSrcEdit: TSourceEditor;
71 public
Changednull72 function Changed(ASrcEdit: TSourceEditor): Boolean;
73 end;
74
75 TProjectCommandsStamp = record
76 private
77 FUnitInfo: TUnitInfo;
78 FProjectChangeStamp: Int64;
79 FProjectSessionChangeStamp: Int64;
80 FCompilerParseStamp: integer;
81 FBuildMacroChangeStamp: integer;
82 public
Changednull83 function Changed(AUnitInfo: TUnitInfo): Boolean;
84 end;
85
86 TPackageCommandsStamp = record
87 private
88 FUnitInfo: TUnitInfo;
89 FPackagesChangeStamp: Int64;
90 public
Changednull91 function Changed(AUnitInfo: TUnitInfo): Boolean;
92 end;
93
94 TSourceEditorTabCommandsStamp = record
95 private
96 FSrcEdit: TSourceEditor;
97 FSrcEditLocked: Boolean;
98 FSourceNotebook: TSourceNotebook;
99 FPageIndex, FPageCount: Integer;
100 public
Changednull101 function Changed(ASrcEdit: TSourceEditor): Boolean;
102 end;
103
104 TSourceEditorCommandsStamp = record
105 private
106 FSrcEdit: TSourceEditor;
107 FDisplayState: TDisplayState;
108 FEditorComponentStamp: int64;
109 FEditorCaretStamp: int64;
110
111 FDesigner: TDesigner;
112 FDesignerSelectionStamp: int64;
113 FDesignerStamp: int64;
114 public
Changednull115 function Changed(ASrcEdit: TSourceEditor; ADesigner: TDesigner;
116 ADisplayState: TDisplayState): Boolean;
117 end;
118
119 { TFileOpener }
120
121 TFileOpener = class
122 private
123 FFileName: string;
124 FUseWindowID: Boolean;
125 FPageIndex: integer;
126 FWindowIndex: integer;
127 // Used by OpenEditorFile
128 FUnitIndex: integer;
129 FEditorInfo: TUnitEditorInfo;
130 FNewEditorInfo: TUnitEditorInfo;
131 FFlags: TOpenFlags;
132 FUnknownFile: boolean;
133 FNewUnitInfo: TUnitInfo;
134 // Used by OpenFileAtCursor
135 FActiveSrcEdit: TSourceEditor;
136 FActiveUnitInfo: TUnitInfo;
137 FIsIncludeDirective: boolean;
OpenFileInSourceEditornull138 function OpenFileInSourceEditor(AnEditorInfo: TUnitEditorInfo): TModalResult;
139 // Used by GetAvailableUnitEditorInfo
AvailSrcWindowIndexnull140 function AvailSrcWindowIndex(AnUnitInfo: TUnitInfo): Integer;
141 // Used by OpenEditorFile
OpenResourcenull142 function OpenResource: TModalResult;
ChangeEditorPagenull143 function ChangeEditorPage: TModalResult;
144 procedure CheckInternalFile;
CheckRevertnull145 function CheckRevert: TModalResult;
OpenKnownnull146 function OpenKnown: TModalResult;
OpenUnknownnull147 function OpenUnknown: TModalResult;
OpenUnknownFilenull148 function OpenUnknownFile: TModalResult;
OpenNotExistingFilenull149 function OpenNotExistingFile: TModalResult;
PrepareFilenull150 function PrepareFile: TModalResult;
PrepareRevertnull151 function PrepareRevert(DiskFilename: String): TModalResult;
ResolvePossibleSymlinknull152 function ResolvePossibleSymlink: TModalResult;
153 // Used by OpenFileAtCursor
CheckIfIncludeDirectiveInFrontnull154 function CheckIfIncludeDirectiveInFront(const Line: string; X: integer): boolean;
FindFilenull155 function FindFile(SearchPath: String): Boolean;
GetFilenameAtRowColnull156 function GetFilenameAtRowCol(XY: TPoint): string;
157 public
158 // These methods have a global wrapper
GetAvailableUnitEditorInfonull159 function GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo;
160 ACaretPoint: TPoint; WantedTopLine: integer = -1): TUnitEditorInfo;
OpenEditorFilenull161 function OpenEditorFile(APageIndex, AWindowIndex: integer;
162 AEditorInfo: TUnitEditorInfo; AFlags: TOpenFlags): TModalResult;
OpenFileAtCursornull163 function OpenFileAtCursor: TModalResult;
OpenMainUnitnull164 function OpenMainUnit: TModalResult;
RevertMainUnitnull165 function RevertMainUnit: TModalResult;
166 end;
167
168
CreateSrcEditPageNamenull169 function CreateSrcEditPageName(const AnUnitName, AFilename: string;
170 IgnoreEditor: TSourceEditor): string;
171 procedure UpdateDefaultPasFileExt;
172
173 // Wrappers for TFileOpener methods.
174 // WindowIndex is WindowID
GetAvailableUnitEditorInfonull175 function GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo;
176 ACaretPoint: TPoint; WantedTopLine: integer = -1): TUnitEditorInfo;
OpenEditorFilenull177 function OpenEditorFile(AFileName: string; PageIndex, WindowIndex: integer;
178 AEditorInfo: TUnitEditorInfo; Flags: TOpenFlags; UseWindowID: Boolean = False): TModalResult;
OpenFileAtCursornull179 function OpenFileAtCursor(ActiveSrcEdit: TSourceEditor;
180 ActiveUnitInfo: TUnitInfo): TModalResult;
OpenMainUnitnull181 function OpenMainUnit(PageIndex, WindowIndex: integer;
182 Flags: TOpenFlags; UseWindowID: Boolean = False): TModalResult;
RevertMainUnitnull183 function RevertMainUnit: TModalResult;
184 // recent
185 procedure AddRecentProjectFile(const AFilename: string);
186 procedure RemoveRecentProjectFile(const AFilename: string);
187 procedure UpdateSourceNames;
CheckEditorNeedsSavenull188 function CheckEditorNeedsSave(AEditor: TSourceEditorInterface;
189 IgnoreSharedEdits: Boolean): Boolean;
190 procedure ArrangeSourceEditorAndMessageView(PutOnTop: boolean);
191 // files/units/projects
SomethingOfProjectIsModifiednull192 function SomethingOfProjectIsModified(Verbose: boolean = false): boolean;
NewFilenull193 function NewFile(NewFileDescriptor: TProjectFileDescriptor;
194 var NewFilename: string; NewSource: string;
195 NewFlags: TNewFlags; NewOwner: TObject): TModalResult;
NewOthernull196 function NewOther: TModalResult;
NewUnitOrFormnull197 function NewUnitOrForm(Template: TNewIDEItemTemplate;
198 DefaultDesc: TProjectFileDescriptor): TModalResult;
199 procedure CreateFileDialogFilterForSourceEditorFiles(Filter: string;
200 out AllEditorMask, AllMask: string);
SaveEditorFilenull201 function SaveEditorFile(AEditor: TSourceEditorInterface; Flags: TSaveFlags): TModalResult;
SaveEditorFilenull202 function SaveEditorFile(const Filename: string; Flags: TSaveFlags): TModalResult;
CloseEditorFilenull203 function CloseEditorFile(AEditor: TSourceEditorInterface; Flags: TCloseFlags):TModalResult;
CloseEditorFilenull204 function CloseEditorFile(const Filename: string; Flags: TCloseFlags): TModalResult;
205 // interactive unit selection
SelectProjectItemsnull206 function SelectProjectItems(ItemList: TViewUnitEntries; ItemType: TIDEProjectItem;
207 MultiSelect: boolean; var MultiSelectCheckedState: Boolean): TModalResult;
SelectUnitComponentsnull208 function SelectUnitComponents(DlgCaption: string; ItemType: TIDEProjectItem;
209 Files: TStringList; MultiSelect: boolean;
210 var MultiSelectCheckedState: Boolean): TModalResult;
211 // unit search
FindUnitFileImplnull212 function FindUnitFileImpl(const AFilename: string; TheOwner: TObject = nil;
213 Flags: TFindUnitFileFlags = []): string;
FindSourceFileImplnull214 function FindSourceFileImpl(const AFilename, BaseDirectory: string;
215 Flags: TFindSourceFlags): string;
FindUnitsOfOwnerImplnull216 function FindUnitsOfOwnerImpl(TheOwner: TObject; Flags: TFindUnitsOfOwnerFlags): TStrings;
217 // project
AddUnitToProjectnull218 function AddUnitToProject(const AEditor: TSourceEditorInterface): TModalResult;
AddActiveUnitToProjectnull219 function AddActiveUnitToProject: TModalResult;
RemoveFromProjectDialognull220 function RemoveFromProjectDialog: TModalResult;
InitNewProjectnull221 function InitNewProject(ProjectDesc: TProjectDescriptor): TModalResult;
InitOpenedProjectFilenull222 function InitOpenedProjectFile(AFileName: string; Flags: TOpenFlags): TModalResult;
223 procedure NewProjectFromFile;
CreateProjectForProgramnull224 function CreateProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult;
InitProjectForProgramnull225 function InitProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult;
SaveProjectnull226 function SaveProject(Flags: TSaveFlags): TModalResult;
SaveProjectIfChangednull227 function SaveProjectIfChanged: TModalResult;
CloseProjectnull228 function CloseProject: TModalResult;
229 procedure OpenProject(aMenuItem: TIDEMenuItem);
CompleteLoadingProjectInfonull230 function CompleteLoadingProjectInfo: TModalResult;
231 procedure CloseAll;
232 procedure InvertedFileClose(PageIndex: LongInt; SrcNoteBook: TSourceNotebook);
233 // designer
DesignerUnitIsVirtualnull234 function DesignerUnitIsVirtual(aLookupRoot: TComponent): Boolean;
CheckLFMInEditornull235 function CheckLFMInEditor(LFMUnitInfo: TUnitInfo; Quiet: boolean): TModalResult;
LoadLFMnull236 function LoadLFM(AnUnitInfo: TUnitInfo; OpenFlags: TOpenFlags;
237 CloseFlags: TCloseFlags): TModalResult;
LoadLFMnull238 function LoadLFM(AnUnitInfo: TUnitInfo; LFMBuf: TCodeBuffer;
239 OpenFlags: TOpenFlags;
240 CloseFlags: TCloseFlags): TModalResult;
OpenComponentnull241 function OpenComponent(const UnitFilename: string; OpenFlags: TOpenFlags;
242 CloseFlags: TCloseFlags; out Component: TComponent): TModalResult;
UpdateUnitInfoResourceBaseClassnull243 function UpdateUnitInfoResourceBaseClass(AnUnitInfo: TUnitInfo; Quiet: boolean): boolean;
CloseUnitComponentnull244 function CloseUnitComponent(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult;
CloseDependingUnitComponentsnull245 function CloseDependingUnitComponents(AnUnitInfo: TUnitInfo;
246 Flags: TCloseFlags): TModalResult;
UnitComponentIsUsednull247 function UnitComponentIsUsed(AnUnitInfo: TUnitInfo;
248 CheckHasDesigner: boolean): boolean;
RemoveFilesFromProjectnull249 function RemoveFilesFromProject(UnitInfos: TFPList): TModalResult;
AskSaveProjectnull250 function AskSaveProject(const ContinueText, ContinueBtn: string): TModalResult;
SaveEditorChangesToCodeCachenull251 function SaveEditorChangesToCodeCache(AEditor: TSourceEditorInterface): boolean;
252
253
254 // These are local functions. Forward reference is needed for most of them.
AskToSaveEditorsnull255 // function AskToSaveEditors(EditorList: TList): TModalResult;
256 // function CheckMainSrcLCLInterfaces(Silent: boolean): TModalResult;
257 // function FileExistsInIDE(const Filename: string;
258 // SearchFlags: TProjectFileSearchFlags): boolean;
259 //new unit
260 function CreateNewCodeBuffer(Descriptor: TProjectFileDescriptor;
261 NewOwner: TObject; NewFilename: string; var NewCodeBuffer: TCodeBuffer;
262 var NewUnitName: string): TModalResult;
CreateNewFormnull263 function CreateNewForm(NewUnitInfo: TUnitInfo;
264 AncestorType: TPersistentClass; ResourceCode: TCodeBuffer;
265 UseCreateFormStatements, DisableAutoSize: Boolean): TModalResult;
NewUniqueComponentNamenull266 function NewUniqueComponentName(Prefix: string): string;
267 //save unit
ShowSaveFileAsDialognull268 function ShowSaveFileAsDialog(var AFilename: string; AnUnitInfo: TUnitInfo;
269 var LFMCode, LRSCode: TCodeBuffer; CanAbort: boolean): TModalResult;
SaveUnitComponentnull270 function SaveUnitComponent(AnUnitInfo: TUnitInfo;
271 LRSCode, LFMCode: TCodeBuffer; Flags: TSaveFlags): TModalResult;
RemoveLooseEventsnull272 function RemoveLooseEvents(AnUnitInfo: TUnitInfo): TModalResult;
RenameUnitnull273 function RenameUnit(AnUnitInfo: TUnitInfo; NewFilename, NewUnitName: string;
274 var LFMCode, LRSCode: TCodeBuffer): TModalResult;
RenameUnitLowerCasenull275 function RenameUnitLowerCase(AnUnitInfo: TUnitInfo; AskUser: boolean): TModalresult;
ReplaceUnitUsenull276 function ReplaceUnitUse(OldFilename, OldUnitName, NewFilename, NewUnitName: string;
277 IgnoreErrors, Quiet, Confirm: boolean): TModalResult;
278 //designer
LoadResourceFilenull279 function LoadResourceFile(AnUnitInfo: TUnitInfo; var LFMCode, LRSCode: TCodeBuffer;
280 AutoCreateResourceCode, ShowAbort: boolean): TModalResult;
FindBaseComponentClassnull281 // function FindBaseComponentClass(AnUnitInfo: TUnitInfo; const AComponentClassName,
282 // DescendantClassName: string; out AComponentClass: TComponentClass): boolean;
283 function LoadAncestorDependencyHidden(AnUnitInfo: TUnitInfo;
284 const aComponentClassName: string; OpenFlags: TOpenFlags;
285 out AncestorClass: TComponentClass; out AncestorUnitInfo: TUnitInfo): TModalResult;
FindComponentClassnull286 // function FindComponentClass(AnUnitInfo: TUnitInfo; const AComponentClassName: string;
287 // Quiet: boolean; out ComponentUnitInfo: TUnitInfo; out AComponentClass: TComponentClass;
288 // out LFMFilename: string; out AncestorClass: TComponentClass): TModalResult;
289 function LoadComponentDependencyHidden(AnUnitInfo: TUnitInfo;
290 const AComponentClassName: string; Flags: TOpenFlags; MustHaveLFM: boolean;
291 out AComponentClass: TComponentClass; out ComponentUnitInfo: TUnitInfo;
292 out AncestorClass: TComponentClass; const IgnoreBtnText: string = ''): TModalResult;
LoadIDECodeBuffernull293 function LoadIDECodeBuffer(var ACodeBuffer: TCodeBuffer;
294 const AFilename: string; Flags: TLoadBufferFlags; ShowAbort: boolean): TModalResult;
295 //save project
ShowSaveProjectAsDialognull296 function ShowSaveProjectAsDialog(UseMainSourceFile: boolean): TModalResult;
SaveProjectInfonull297 function SaveProjectInfo(var Flags: TSaveFlags): TModalResult;
298 procedure GetMainUnit(out MainUnitInfo: TUnitInfo;
299 out MainUnitSrcEdit: TSourceEditor; UpdateModified: boolean);
300 procedure SaveSrcEditorProjectSpecificSettings(AnEditorInfo: TUnitEditorInfo);
301 procedure SaveSourceEditorProjectSpecificSettings;
302 procedure UpdateProjectResourceInfo;
303
304
305 implementation
306
CreateSrcEditPageNamenull307 function CreateSrcEditPageName(const AnUnitName, AFilename: string;
308 IgnoreEditor: TSourceEditor): string;
309 begin
310 Result:=AnUnitName;
311 if Result='' then
312 Result:=AFilename;
313 if FilenameIsPascalUnit(Result) then
314 Result:=ExtractFileNameOnly(Result)
315 else
316 Result:=ExtractFileName(Result);
317 Result:=SourceEditorManager.FindUniquePageName(Result,IgnoreEditor);
318 end;
319
320 procedure UpdateDefaultPasFileExt;
321 var
322 DefPasExt: string;
323 begin
324 // change default pascal file extensions
325 DefPasExt:=PascalExtension[EnvironmentOptions.PascalFileExtension];
326 if LazProjectFileDescriptors<>nil then
327 LazProjectFileDescriptors.DefaultPascalFileExt:=DefPasExt;
328 end;
329
330 // Wrappers for TFileOpener methods.
331
GetAvailableUnitEditorInfonull332 function GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo;
333 ACaretPoint: TPoint; WantedTopLine: integer = -1): TUnitEditorInfo;
334 var
335 Opener: TFileOpener;
336 begin
337 Opener := TFileOpener.Create;
338 try
339 Result := Opener.GetAvailableUnitEditorInfo(AnUnitInfo,ACaretPoint,WantedTopLine);
340 finally
341 Opener.Free;
342 end;
343 end;
344
OpenEditorFilenull345 function OpenEditorFile(AFileName: string; PageIndex, WindowIndex: integer;
346 AEditorInfo: TUnitEditorInfo; Flags: TOpenFlags; UseWindowID: Boolean = False): TModalResult;
347 var
348 Opener: TFileOpener;
349 begin
350 Opener := TFileOpener.Create;
351 try
352 Opener.FFileName := AFileName;
353 Opener.FUseWindowID := UseWindowID;
354 Result := Opener.OpenEditorFile(PageIndex,WindowIndex,AEditorInfo,Flags);
355 finally
356 Opener.Free;
357 end;
358 end;
359
OpenFileAtCursornull360 function OpenFileAtCursor(ActiveSrcEdit: TSourceEditor; ActiveUnitInfo: TUnitInfo): TModalResult;
361 var
362 Opener: TFileOpener;
363 begin
364 Opener := TFileOpener.Create;
365 try
366 Opener.FActiveSrcEdit := ActiveSrcEdit;
367 Opener.FActiveUnitInfo := ActiveUnitInfo;
368 Result := Opener.OpenFileAtCursor;
369 finally
370 Opener.Free;
371 end;
372 end;
373
OpenMainUnitnull374 function OpenMainUnit(PageIndex, WindowIndex: integer;
375 Flags: TOpenFlags; UseWindowID: Boolean): TModalResult;
376 var
377 Opener: TFileOpener;
378 begin
379 Opener := TFileOpener.Create;
380 try
381 Opener.FPageIndex := PageIndex;
382 Opener.FWindowIndex := WindowIndex;
383 Opener.FFlags := Flags;
384 Opener.FUseWindowID := UseWindowID;
385 Result := Opener.OpenMainUnit;
386 finally
387 Opener.Free;
388 end;
389 end;
390
RevertMainUnitnull391 function RevertMainUnit: TModalResult;
392 var
393 Opener: TFileOpener;
394 begin
395 Opener := TFileOpener.Create;
396 try
397 Result := Opener.RevertMainUnit;
398 finally
399 Opener.Free;
400 end;
401 end;
402
403 { TBookmarkCommandsStamp }
404
TBookmarkCommandsStamp.Changednull405 function TBookmarkCommandsStamp.Changed(ABookmarksStamp: Int64): Boolean;
406 begin
407 Result := (FBookmarksStamp <> ABookmarksStamp);
408 if Result then
409 FBookmarksStamp := ABookmarksStamp;
410 end;
411
412 { TFileCommandsStamp }
413
TFileCommandsStamp.Changednull414 function TFileCommandsStamp.Changed(ASrcEdit: TSourceEditor): Boolean;
415 begin
416 Result := not(
417 (FSrcEdit = ASrcEdit)
418 );
419
420 if not Result then Exit;
421
422 FSrcEdit := ASrcEdit;
423 end;
424
425 { TProjectCommandsStamp }
426
Changednull427 function TProjectCommandsStamp.Changed(AUnitInfo: TUnitInfo): Boolean;
428 var
429 CurProjectChangeStamp, CurProjectSessionChangeStamp: Integer;
430 begin
431 if Project1=nil then
432 begin
433 CurProjectChangeStamp := LUInvalidChangeStamp;
434 CurProjectSessionChangeStamp := LUInvalidChangeStamp;
435 end else
436 begin
437 CurProjectChangeStamp := Project1.ChangeStamp;
438 CurProjectSessionChangeStamp := Project1.SessionChangeStamp;
439 end;
440 Result := not(
441 (FUnitInfo = AUnitInfo)
442 and (FProjectChangeStamp = CurProjectChangeStamp)
443 and (FProjectSessionChangeStamp = CurProjectSessionChangeStamp)
444 and (FCompilerParseStamp = CompilerParseStamp)
445 and (FBuildMacroChangeStamp = BuildMacroChangeStamp)
446 );
447
448 if not Result then Exit;
449
450 FUnitInfo := AUnitInfo;
451 FProjectChangeStamp := CurProjectChangeStamp;
452 FProjectSessionChangeStamp := CurProjectSessionChangeStamp;
453 FCompilerParseStamp := CompilerParseStamp;
454 FBuildMacroChangeStamp := BuildMacroChangeStamp;
455 end;
456
457 { TPackageCommandsStamp }
458
TPackageCommandsStamp.Changednull459 function TPackageCommandsStamp.Changed(AUnitInfo: TUnitInfo): Boolean;
460 begin
461 Result := not(
462 (FUnitInfo = AUnitInfo)
463 and (FPackagesChangeStamp = PackageGraph.ChangeStamp)
464 );
465
466 if not Result then Exit;
467
468 FUnitInfo := AUnitInfo;
469 FPackagesChangeStamp := PackageGraph.ChangeStamp;
470 end;
471
472 { TSourceEditorTabCommandsStamp }
473
Changednull474 function TSourceEditorTabCommandsStamp.Changed(ASrcEdit: TSourceEditor): Boolean;
475 begin
476 Result := not(
477 (FSrcEdit = ASrcEdit)
478 and ((ASrcEdit = nil) or (
479 (FSrcEditLocked = ASrcEdit.IsLocked)
480 and (FSourceNotebook = ASrcEdit.SourceNotebook)
481 and (FPageIndex = ASrcEdit.SourceNotebook.PageIndex)
482 and (FPageCount = ASrcEdit.SourceNotebook.PageCount)))
483 );
484
485 if not Result then Exit;
486
487 FSrcEdit := ASrcEdit;
488 if ASrcEdit<>nil then
489 begin
490 FSrcEditLocked := ASrcEdit.IsLocked;
491 FSourceNotebook := ASrcEdit.SourceNotebook;
492 FPageIndex := ASrcEdit.SourceNotebook.PageIndex;
493 FPageCount := ASrcEdit.SourceNotebook.PageCount;
494 end;
495 end;
496
497 { TSourceEditorCommandsStamp }
498
TSourceEditorCommandsStamp.Changednull499 function TSourceEditorCommandsStamp.Changed(ASrcEdit: TSourceEditor;
500 ADesigner: TDesigner; ADisplayState: TDisplayState): Boolean;
501 begin
502 Result := not(
503 (FSrcEdit = ASrcEdit)
504 and (FDesigner = ADesigner)
505 and (FDisplayState = ADisplayState)
506 and ((ASrcEdit = nil) or (
507 (FEditorComponentStamp = ASrcEdit.EditorComponent.ChangeStamp)
508 and (FEditorCaretStamp = ASrcEdit.EditorComponent.CaretStamp)))
509 and ((ADesigner = nil) or (
510 (FDesignerSelectionStamp = ADesigner.Selection.ChangeStamp)
511 and (FDesignerStamp = ADesigner.ChangeStamp)))
512 );
513
514 if not Result then Exit;
515
516 FSrcEdit := ASrcEdit;
517 FDesigner := ADesigner;
518 FDisplayState := ADisplayState;
519 if ASrcEdit<>nil then
520 begin
521 FEditorComponentStamp := ASrcEdit.EditorComponent.ChangeStamp;
522 FEditorCaretStamp := ASrcEdit.EditorComponent.CaretStamp;
523 end;
524 if ADesigner<>nil then
525 begin
526 FDesignerSelectionStamp := ADesigner.Selection.ChangeStamp;
527 FDesignerStamp := ADesigner.ChangeStamp;
528 end;
529 end;
530
531 //==============================================================================
532
533 { TFileOpener }
534
TFileOpener.OpenFileInSourceEditornull535 function TFileOpener.OpenFileInSourceEditor(AnEditorInfo: TUnitEditorInfo): TModalResult;
536 var
537 NewSrcEdit: TSourceEditor;
538 AFilename: string;
539 NewCaretXY: TPoint;
540 NewTopLine: LongInt;
541 NewLeftChar: LongInt;
542 NewErrorLine: LongInt;
543 NewExecutionLine: LongInt;
544 FoldState: String;
545 SrcNotebook: TSourceNotebook;
546 AnUnitInfo: TUnitInfo;
547 AShareEditor: TSourceEditor;
548 begin
549 //debugln(['TFileOpener.OpenFileInSourceEditor ',AnEditorInfo.UnitInfo.Filename,' Window=',WindowIndex,'/',SourceEditorManager.SourceWindowCount,' Page=',PageIndex]);
550 AnUnitInfo := AnEditorInfo.UnitInfo;
551 AFilename:=AnUnitInfo.Filename;
552 if (FWindowIndex < 0) then
553 SrcNotebook := SourceEditorManager.ActiveOrNewSourceWindow
554 else
555 if FUseWindowID then begin
556 SrcNotebook := SourceEditorManager.SourceWindowWithID(FWindowIndex);
557 FWindowIndex := SourceEditorManager.IndexOfSourceWindow(SrcNotebook);
558 end
559 else
560 if (FWindowIndex >= SourceEditorManager.SourceWindowCount) then begin
561 SrcNotebook := SourceEditorManager.NewSourceWindow;
562 end
563 else
564 SrcNotebook := SourceEditorManager.SourceWindows[FWindowIndex];
565
566 // get syntax highlighter type
567 if (uifInternalFile in AnUnitInfo.Flags) then
568 AnUnitInfo.UpdateDefaultHighlighter(lshFreePascal)
569 else
570 AnUnitInfo.UpdateDefaultHighlighter(FilenameToLazSyntaxHighlighter(AFilename));
571
572 SrcNotebook.IncUpdateLock;
573 try
574 //DebugLn(['TFileOpener.OpenFileInSourceEditor Revert=',ofRevert in Flags,' ',AnUnitInfo.Filename,' PageIndex=',PageIndex]);
575 if (not (ofRevert in FFlags)) or (FPageIndex<0) then begin
576 // create a new source editor
577
578 // update marks and cursor positions in Project1, so that merging the old
579 // settings during restoration will work
580 SaveSourceEditorProjectSpecificSettings;
581 AShareEditor := nil;
582 if AnUnitInfo.OpenEditorInfoCount > 0 then
583 AShareEditor := TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent);
584 NewSrcEdit:=SrcNotebook.NewFile(
585 CreateSrcEditPageName(AnUnitInfo.Unit_Name, AFilename, AShareEditor),
586 AnUnitInfo.Source, False, AShareEditor);
587 NewSrcEdit.EditorComponent.BeginUpdate;
588 MainIDEBar.itmFileClose.Enabled:=True;
589 MainIDEBar.itmFileCloseAll.Enabled:=True;
590 NewCaretXY := AnEditorInfo.CursorPos;
591 NewTopLine := AnEditorInfo.TopLine;
592 FoldState := AnEditorInfo.FoldState;
593 NewLeftChar:=1;
594 NewErrorLine:=-1;
595 NewExecutionLine:=-1;
596 end else begin
597 // revert code in existing source editor
598 NewSrcEdit:=SourceEditorManager.SourceEditorsByPage[FWindowIndex, FPageIndex];
599 NewCaretXY:=NewSrcEdit.EditorComponent.CaretXY;
600 NewTopLine:=NewSrcEdit.EditorComponent.TopLine;
601 FoldState := NewSrcEdit.EditorComponent.FoldState;
602 NewLeftChar:=NewSrcEdit.EditorComponent.LeftChar;
603 NewErrorLine:=NewSrcEdit.ErrorLine;
604 NewExecutionLine:=NewSrcEdit.ExecutionLine;
605 NewSrcEdit.EditorComponent.BeginUpdate;
606 if NewSrcEdit.CodeBuffer=AnUnitInfo.Source then begin
607 AnUnitInfo.Source.AssignTo(NewSrcEdit.EditorComponent.Lines,true);
608 end else
609 NewSrcEdit.CodeBuffer:=AnUnitInfo.Source;
610 AnUnitInfo.ClearModifieds;
611 //DebugLn(['TFileOpener.OpenFileInSourceEditor NewCaretXY=',dbgs(NewCaretXY),' NewTopLine=',NewTopLine]);
612 end;
613
614 NewSrcEdit.IsLocked := AnEditorInfo.IsLocked;
615 AnEditorInfo.EditorComponent := NewSrcEdit;
616 //debugln(['TFileOpener.OpenFileInSourceEditor ',AnUnitInfo.Filename,' ',AnUnitInfo.EditorIndex]);
617
618 // restore source editor settings
619 DebugBoss.DoRestoreDebuggerMarks(AnUnitInfo);
620 NewSrcEdit.SyntaxHighlighterType := AnEditorInfo.SyntaxHighlighter;
621 NewSrcEdit.EditorComponent.AfterLoadFromFile;
622 try
623 NewSrcEdit.EditorComponent.FoldState := FoldState;
624 except
625 IDEMessageDialog(lisError, lisFailedToLoadFoldStat, mtError, [mbOK]);
626 end;
627
628 NewSrcEdit.EditorComponent.CaretXY:=NewCaretXY;
629 NewSrcEdit.EditorComponent.TopLine:=NewTopLine;
630 NewSrcEdit.EditorComponent.LeftChar:=NewLeftChar;
631 NewSrcEdit.ErrorLine:=NewErrorLine;
632 NewSrcEdit.ExecutionLine:=NewExecutionLine;
633 NewSrcEdit.ReadOnly:=AnUnitInfo.ReadOnly;
634 NewSrcEdit.Modified:=false;
635
636 // mark unit as loaded
637 NewSrcEdit.EditorComponent.EndUpdate;
638 AnUnitInfo.Loaded:=true;
639 finally
640 SrcNotebook.DecUpdateLock;
641 end;
642
643 // update statusbar and focus editor
644 if (not (ofProjectLoading in FFlags)) then begin
645 SourceEditorManager.ActiveEditor := NewSrcEdit;
646 SourceEditorManager.ShowActiveWindowOnTop(True);
647 end;
648 SrcNoteBook.UpdateStatusBar;
649 SrcNotebook.BringToFront;
650
651 Result:=mrOk;
652 end;
653
TFileOpener.AvailSrcWindowIndexnull654 function TFileOpener.AvailSrcWindowIndex(AnUnitInfo: TUnitInfo): Integer;
655 var
656 i: Integer;
657 begin
658 Result := -1;
659 i := 0;
660 if AnUnitInfo.OpenEditorInfoCount > 0 then
661 while (i < SourceEditorManager.SourceWindowCount) and
662 (SourceEditorManager.SourceWindowByLastFocused[i].IndexOfEditorInShareWith
663 (TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent)) >= 0)
664 do
665 inc(i);
666 if i < SourceEditorManager.SourceWindowCount then
667 Result := SourceEditorManager.IndexOfSourceWindow(SourceEditorManager.SourceWindowByLastFocused[i]);
668 end;
669
GetAvailableUnitEditorInfonull670 function TFileOpener.GetAvailableUnitEditorInfo(AnUnitInfo: TUnitInfo;
671 ACaretPoint: TPoint; WantedTopLine: integer): TUnitEditorInfo;
672
EditorMatchesnull673 function EditorMatches(AEditInfo: TUnitEditorInfo;
674 AAccess: TEditorOptionsEditAccessOrderEntry; ALockRun: Integer = 0): Boolean;
675 var
676 AEdit: TSourceEditor;
677 begin
678 AEdit := TSourceEditor(AEditInfo.EditorComponent);
679 Result := False;
680 case AAccess.SearchLocked of
681 eoeaIgnoreLock: ;
682 eoeaLockedOnly: if not AEdit.IsLocked then exit;
683 eoeaUnlockedOnly: if AEdit.IsLocked then exit;
684 eoeaLockedFirst: if (not AEdit.IsLocked) and (ALockRun = 0) then exit;
685 eoeaLockedLast: if (AEdit.IsLocked) and (ALockRun = 0) then exit;
686 end;
687 case AAccess.SearchInView of
688 eoeaIgnoreInView: ;
689 eoeaInViewOnly: if not AEdit.IsCaretOnScreen(ACaretPoint, False) then exit;
690 eoeaInViewSoftCenterOnly: if not AEdit.IsCaretOnScreen(ACaretPoint, True) then exit;
691 end;
692 Result := True;
693 end;
694
695 var
696 i, j, w, LockRun: Integer;
697 Access: TEditorOptionsEditAccessOrderEntry;
698 begin
699 Result := nil;
700 // Check for already open Editor. If there is none, then it must be opened in OpenEditorFile
701 if AnUnitInfo.OpenEditorInfoCount = 0 then exit;
702 for i := 0 to EditorOpts.MultiWinEditAccessOrder.Count - 1 do begin
703 Access := EditorOpts.MultiWinEditAccessOrder[i];
704 if not Access.Enabled then continue;
705 LockRun := 1;
706 if Access.SearchLocked in [eoeaLockedFirst, eoeaLockedLast] then LockRun := 0;
707 repeat
708 case Access.RealSearchOrder of
709 eoeaOrderByEditFocus, eoeaOrderByListPref:
710 begin
711 for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
712 if EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
713 Result := AnUnitInfo.OpenEditorInfo[j];
714 break;
715 end;
716 end;
717 eoeaOrderByWindowFocus:
718 begin
719 for w := 0 to SourceEditorManager.SourceWindowCount - 1 do begin
720 for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
721 if (TSourceEditor(AnUnitInfo.OpenEditorInfo[j].EditorComponent).SourceNotebook
722 = SourceEditorManager.SourceWindowByLastFocused[w])
723 and EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
724 Result := AnUnitInfo.OpenEditorInfo[j];
725 break;
726 end;
727 if Result <> nil then break;
728 end;
729 end;
730 eoeaOrderByOldestEditFocus:
731 begin
732 for j := AnUnitInfo.OpenEditorInfoCount - 1 downto 0 do
733 if EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
734 Result := AnUnitInfo.OpenEditorInfo[j];
735 break;
736 end;
737 end;
738 eoeaOrderByOldestWindowFocus:
739 begin
740 for w := SourceEditorManager.SourceWindowCount - 1 downto 0 do begin
741 for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
742 if (TSourceEditor(AnUnitInfo.OpenEditorInfo[j].EditorComponent).SourceNotebook
743 = SourceEditorManager.SourceWindowByLastFocused[w])
744 and EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
745 Result := AnUnitInfo.OpenEditorInfo[j];
746 break;
747 end;
748 if Result <> nil then break;
749 end;
750 end;
751 eoeaOnlyCurrentEdit:
752 begin
753 LockRun := 1;
754 for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
755 if (AnUnitInfo.OpenEditorInfo[j].EditorComponent = SourceEditorManager.ActiveEditor)
756 and EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
757 Result := AnUnitInfo.OpenEditorInfo[j];
758 break;
759 end;
760 end;
761 eoeaOnlyCurrentWindow:
762 begin
763 LockRun := 1;
764 for j := 0 to AnUnitInfo.OpenEditorInfoCount - 1 do
765 if (TSourceEditor(AnUnitInfo.OpenEditorInfo[j].EditorComponent).SourceNotebook
766 = SourceEditorManager.ActiveSourceWindow)
767 and EditorMatches(AnUnitInfo.OpenEditorInfo[j], Access) then begin
768 Result := AnUnitInfo.OpenEditorInfo[j];
769 break;
770 end;
771 end;
772 end;
773 inc(LockRun);
774 until (LockRun > 1) or (Result <> nil);
775 FUseWindowID:=False;
776 FFlags:=[];
777 FPageIndex:=-1;
778 if (Result = nil) then
779 case Access.SearchOpenNew of
780 eoeaNoNewTab: ;
781 eoeaNewTabInExistingWindowOnly:
782 begin
783 FWindowIndex := AvailSrcWindowIndex(AnUnitInfo);
784 if FWindowIndex >= 0 then
785 if OpenFileInSourceEditor(AnUnitInfo.GetClosedOrNewEditorInfo) = mrOk then
786 Result := AnUnitInfo.OpenEditorInfo[0]; // newly opened will be last focused
787 end;
788 eoeaNewTabInNewWindowOnly:
789 begin
790 FWindowIndex := SourceEditorManager.SourceWindowCount;
791 if OpenFileInSourceEditor(AnUnitInfo.GetClosedOrNewEditorInfo) = mrOk then
792 Result := AnUnitInfo.OpenEditorInfo[0]; // newly opened will be last focused
793 end;
794 eoeaNewTabInExistingOrNewWindow:
795 begin
796 FWindowIndex := AvailSrcWindowIndex(AnUnitInfo);
797 if FWindowIndex < 0 then
798 FWindowIndex := SourceEditorManager.SourceWindowCount;
799 if OpenFileInSourceEditor(AnUnitInfo.GetClosedOrNewEditorInfo) = mrOk then
800 Result := AnUnitInfo.OpenEditorInfo[0]; // newly opened will be last focused
801 end;
802 end;
803 if Result <> nil then
804 break;
805 end;
806 if Result = nil then
807 // should never happen
808 Result := AnUnitInfo.OpenEditorInfo[0];
809 if Result<>nil then begin
810 // WantedTopLine
811 if (WantedTopLine>0)
812 and (Result.EditorComponent<>nil) then
813 Result.EditorComponent.TopLine:=WantedTopLine;
814 end;
815 end;
816
TFileOpener.OpenResourcenull817 function TFileOpener.OpenResource: TModalResult;
818 var
819 CloseFlags: TCloseFlags;
820 begin
821 // read form data
822 if FilenameIsPascalUnit(FFilename) then begin
823 // this could be a unit with a form
824 //debugln('TFileOpener.OpenResource ',FFilename,' ',OpenFlagsToString(Flags));
825 if ([ofDoNotLoadResource]*FFlags=[])
826 and ( (ofDoLoadResource in FFlags)
827 or ((ofProjectLoading in FFlags)
828 and FNewUnitInfo.LoadedDesigner
829 and (not Project1.AutoOpenDesignerFormsDisabled)
830 and EnvironmentOptions.AutoCreateFormsOnOpen))
831 then begin
832 // -> try to (re)load the lfm file
833 //debugln(['TFileOpener.OpenResource Loading LFM for ',FNewUnitInfo.Filename,' LoadedDesigner=',FNewUnitInfo.LoadedDesigner]);
834 CloseFlags:=[cfSaveDependencies];
835 if ofRevert in FFlags then
836 Include(CloseFlags,cfCloseDependencies);
837 Result:=LoadLFM(FNewUnitInfo,FFlags,CloseFlags);
838 if Result<>mrOk then begin
839 DebugLn(['TFileOpener.OpenResource LoadLFM failed']);
840 exit;
841 end;
842 end else begin
843 Result:=mrOk;
844 end;
845 end else if FNewUnitInfo.Component<>nil then begin
846 // this is no pascal source and there is a designer form
847 // This can be the case, when the file is renamed and/or reverted
848 // -> close form
849 Result:=CloseUnitComponent(FNewUnitInfo,[cfCloseDependencies,cfSaveDependencies]);
850 if Result<>mrOk then begin
851 DebugLn(['TFileOpener.OpenResource CloseUnitComponent failed']);
852 end;
853 end else begin
854 Result:=mrOk;
855 end;
856 if FNewUnitInfo.Component=nil then
857 FNewUnitInfo.LoadedDesigner:=false;
858 end;
859
860 procedure TFileOpener.CheckInternalFile;
861 var
862 NewBuf: TCodeBuffer;
863 begin
864 if (copy(FFileName, 1, length(EditorMacroVirtualDrive)) = EditorMacroVirtualDrive)
865 then begin
866 FUnitIndex:=Project1.IndexOfFilename(FFilename);
867 if (FUnitIndex < 0) then begin
868 NewBuf := CodeToolBoss.SourceCache.CreateFile(FFileName);
869 if MacroListViewer.MacroByFullName(FFileName) <> nil then
870 NewBuf.Source := MacroListViewer.MacroByFullName(FFileName).GetAsSource;
871 FNewUnitInfo:=TUnitInfo.Create(NewBuf);
872 FNewUnitInfo.DefaultSyntaxHighlighter := lshFreePascal;
873 Project1.AddFile(FNewUnitInfo,false);
874 end
875 else begin
876 FNewUnitInfo:=Project1.Units[FUnitIndex];
877 end;
878 FNewUnitInfo.Flags := FNewUnitInfo.Flags + [uifInternalFile];
879
880 if FNewUnitInfo.OpenEditorInfoCount > 0 then begin
881 FNewEditorInfo := FNewUnitInfo.OpenEditorInfo[0];
882 SourceEditorManager.SetWindowByIDAndPage(FNewEditorInfo.WindowID, FNewEditorInfo.PageIndex);
883 end
884 else begin
885 FNewEditorInfo := FNewUnitInfo.GetClosedOrNewEditorInfo;
886 OpenFileInSourceEditor(FNewEditorInfo);
887 end;
888 end;
889 end;
890
TFileOpener.CheckRevertnull891 function TFileOpener.CheckRevert: TModalResult;
892 // revert: use source editor filename
893 begin
894 if (FPageIndex>=0) then begin
895 if FUseWindowID then // Revert must have a valid ID
896 FWindowIndex := SourceEditorManager.IndexOfSourceWindowWithID(FWindowIndex);
897 FUseWindowID := False;
898 Assert((FWindowIndex >= 0) and (FWindowIndex < SourceEditorManager.SourceWindowCount), 'FWindowIndex for revert');
899 FFilename := SourceEditorManager.SourceEditorsByPage[FWindowIndex, FPageIndex].FileName;
900 end
901 else
902 FFlags := FFlags - [ofRevert]; // No editor exists yet, don't try to revert.
903 FUnitIndex:=Project1.IndexOfFilename(FFilename);
904 if (FUnitIndex > 0) then begin
905 FNewUnitInfo:=Project1.Units[FUnitIndex];
906 if (uifInternalFile in FNewUnitInfo.Flags) then
907 begin
908 if (FNewUnitInfo.OpenEditorInfoCount > 0) then begin
909 FNewEditorInfo := FNewUnitInfo.OpenEditorInfo[0];
910 if MacroListViewer.MacroByFullName(FFileName) <> nil then
911 FNewUnitInfo.Source.Source := MacroListViewer.MacroByFullName(FFileName).GetAsSource;
912 FUseWindowID:=True;
913 FPageIndex := FNewEditorInfo.PageIndex;
914 FWindowIndex := FNewEditorInfo.WindowID;
915 OpenFileInSourceEditor(FNewEditorInfo);
916 end;
917 // else unknown internal file
918 exit(mrIgnore);
919 end;
920 end;
921 exit(mrOk);
922 end;
923
PrepareRevertnull924 function TFileOpener.PrepareRevert(DiskFilename: String): TModalResult;
925 var
926 WInd: integer;
927 ed: TSourceEditor;
928 begin
929 FUnknownFile := False;
930 if FUseWindowID then
931 WInd:=SourceEditorManager.IndexOfSourceWindowWithID(FWindowIndex)
932 else
933 WInd:=FWindowIndex;
934 ed := SourceEditorManager.SourceEditorsByPage[WInd, FPageIndex];
935 FNewEditorInfo := Project1.EditorInfoWithEditorComponent(ed);
936 FNewUnitInfo := FNewEditorInfo.UnitInfo;
937 FUnitIndex:=Project1.IndexOf(FNewUnitInfo);
938 FFilename:=FNewUnitInfo.Filename;
939 if CompareFilenames(FFileName,DiskFilename)=0 then
940 FFileName:=DiskFilename;
941 if FNewUnitInfo.IsVirtual then begin
942 if (not (ofQuiet in FFlags)) then begin
943 IDEMessageDialog(lisRevertFailed, Format(lisFileIsVirtual, [FFilename]),
944 mtInformation,[mbCancel]);
945 end;
946 exit(mrCancel);
947 end;
948 exit(mrOK);
949 end;
950
PrepareFilenull951 function TFileOpener.PrepareFile: TModalResult;
952 begin
953 FUnitIndex:=Project1.IndexOfFilename(FFilename);
954 FUnknownFile := (FUnitIndex < 0);
955 FNewEditorInfo := nil;
956 if not FUnknownFile then begin
957 FNewUnitInfo := Project1.Units[FUnitIndex];
958 if FEditorInfo <> nil then
959 FNewEditorInfo := FEditorInfo
960 else if (ofProjectLoading in FFlags) then
961 FNewEditorInfo := FNewUnitInfo.GetClosedOrNewEditorInfo
962 else
963 FNewEditorInfo := FNewUnitInfo.EditorInfo[0];
964 end;
965 Result := mrOK;
966 end;
967
ChangeEditorPagenull968 function TFileOpener.ChangeEditorPage: TModalResult;
969 // file already open -> change source notebook page
970 begin
971 //DebugLn(['TFileOpener.ChangeEditorPage file already open ',FNewUnitInfo.Filename,' WindowIndex=',FNewEditorInfo.WindowID,' PageIndex=',FNewEditorInfo.PageIndex]);
972 SourceEditorManager.SetWindowByIDAndPage(FNewEditorInfo.WindowID, FNewEditorInfo.PageIndex);
973 if ofDoLoadResource in FFlags then
974 Result:=OpenResource
975 else
976 Result:=mrOk;
977 end;
978
OpenKnownnull979 function TFileOpener.OpenKnown: TModalResult;
980 // project knows this file => all the meta data is known -> just load the source
981 var
982 LoadBufferFlags: TLoadBufferFlags;
983 NewBuf: TCodeBuffer;
984 begin
985 FNewUnitInfo:=Project1.Units[FUnitIndex];
986 LoadBufferFlags:=[lbfCheckIfText];
987 if FilenameIsAbsolute(FFilename) then begin
988 if (not (ofUseCache in FFlags)) then
989 Include(LoadBufferFlags,lbfUpdateFromDisk);
990 if ofRevert in FFlags then
991 Include(LoadBufferFlags,lbfRevert);
992 end;
993 Result:=LoadCodeBuffer(NewBuf,FFileName,LoadBufferFlags,
994 [ofProjectLoading,ofMultiOpen]*FFlags<>[]);
995 if Result<>mrOk then begin
996 DebugLn(['TFileOpener.OpenKnownFile failed LoadCodeBuffer: ',FFilename]);
997 exit;
998 end;
999 FNewUnitInfo.Source:=NewBuf;
1000 if FilenameIsPascalUnit(FNewUnitInfo.Filename) then
1001 FNewUnitInfo.ReadUnitNameFromSource(false);
1002 FNewUnitInfo.Modified:=FNewUnitInfo.Source.FileOnDiskNeedsUpdate;
1003 end;
1004
OpenUnknownnull1005 function TFileOpener.OpenUnknown: TModalResult;
1006 // open unknown file, Never happens if ofRevert
1007 begin
1008 Result:=OpenUnknownFile;
1009 if Result<>mrOk then exit;
1010 // the file was previously unknown, use the default EditorInfo
1011 if FEditorInfo <> nil then
1012 FNewEditorInfo := FEditorInfo
1013 else
1014 if FNewUnitInfo <> nil then
1015 FNewEditorInfo := FNewUnitInfo.GetClosedOrNewEditorInfo
1016 else
1017 FNewEditorInfo := nil;
1018 end;
1019
OpenUnknownFilenull1020 function TFileOpener.OpenUnknownFile: TModalResult;
1021 var
1022 Ext, NewProgramName, LPIFilename, ACaption, AText: string;
1023 PreReadBuf: TCodeBuffer;
1024 LoadFlags: TLoadBufferFlags;
1025 SourceType: String;
1026 begin
1027 Ext:=lowercase(ExtractFileExt(FFilename));
1028
1029 if ([ofProjectLoading,ofRegularFile]*FFlags=[]) and (MainIDE.ToolStatus=itNone)
1030 and (Ext='.lpi') then begin
1031 // this is a project info file -> load whole project
1032 Result:=MainIDE.DoOpenProjectFile(FFilename,[ofAddToRecent]);
1033 if Result = mrOK then
1034 Result := mrIgnore;
1035 exit;
1036 end;
1037
1038 // load the source
1039 LoadFlags := [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert];
1040 if ofQuiet in FFlags then Include(LoadFlags, lbfQuiet);
1041 Result:=LoadCodeBuffer(PreReadBuf,FFileName,LoadFlags,true);
1042 if Result<>mrOk then exit;
1043 FNewUnitInfo:=nil;
1044
1045 // check if unit is a program
1046 if ([ofProjectLoading,ofRegularFile]*FFlags=[])
1047 and FilenameIsPascalSource(FFilename) then begin
1048 SourceType:=CodeToolBoss.GetSourceType(PreReadBuf,false);
1049 if (SysUtils.CompareText(SourceType,'PROGRAM')=0)
1050 or (SysUtils.CompareText(SourceType,'LIBRARY')=0)
1051 then begin
1052 NewProgramName:=CodeToolBoss.GetSourceName(PreReadBuf,false);
1053 if NewProgramName<>'' then begin
1054 // source is a program
1055 // either this is a lazarus project or it is not yet a lazarus project ;)
1056 LPIFilename:=ChangeFileExt(FFilename,'.lpi');
1057 if FileExistsCached(LPIFilename) then begin
1058 case IDEQuestionDialog(lisProjectInfoFileDetected,
1059 Format(lisTheFileSeemsToBeTheProgramFileOfAnExistingLazarusP,
1060 [FFilename]), mtConfirmation,
1061 [mrOk, lisOpenProject2, mrAbort, lisOpenTheFileAsNormalSource])
1062 of
1063 mrOk:
1064 begin
1065 Result:=MainIDE.DoOpenProjectFile(LPIFilename,[ofAddToRecent]);
1066 if Result = mrOK then
1067 Result := mrIgnore;
1068 exit;
1069 end;
1070 mrCancel: Exit(mrCancel);
1071 end;
1072 end else begin
1073 AText:=Format(lisTheFileSeemsToBeAProgramCloseCurrentProject,
1074 [FFilename, LineEnding, LineEnding]);
1075 ACaption:=lisProgramDetected;
1076 if IDEMessageDialog(ACaption, AText, mtConfirmation, [mbYes,mbNo])=mrYes then
1077 begin
1078 Result:=CreateProjectForProgram(PreReadBuf);
1079 if Result = mrOK then
1080 Result := mrIgnore;
1081 exit;
1082 end;
1083 end;
1084 end;
1085 end;
1086 end;
1087 FNewUnitInfo:=TUnitInfo.Create(PreReadBuf);
1088 if FilenameIsPascalSource(FNewUnitInfo.Filename) then
1089 FNewUnitInfo.ReadUnitNameFromSource(true);
1090 Project1.AddFile(FNewUnitInfo,false);
1091 if (ofAddToProject in FFlags) and (not FNewUnitInfo.IsPartOfProject) then
1092 begin
1093 FNewUnitInfo.IsPartOfProject:=true;
1094 Project1.Modified:=true;
1095 end;
1096 Result:=mrOk;
1097 end;
1098
OpenNotExistingFilenull1099 function TFileOpener.OpenNotExistingFile: TModalResult;
1100 var
1101 NewFlags: TNewFlags;
1102 begin
1103 if ofProjectLoading in FFlags then begin
1104 // this is a file that was loaded last time, but was removed from disk
1105 Result:=IDEQuestionDialog(lisFileNotFound,
1106 Format(lisTheFileWasNotFoundIgnoreWillGoOnLoadingTheProject,
1107 [FFilename, LineEnding, LineEnding]),
1108 mtError, [mrIgnore, lisSkipFileAndContinueLoading,
1109 mrAbort, lisAbortLoadingProject]);
1110 exit;
1111 end;
1112
1113 // Default to cancel
1114 Result:=mrCancel;
1115 if ofQuiet in FFlags then Exit;
1116
1117 if ofOnlyIfExists in FFlags then
1118 begin
1119 IDEMessageDialog(lisFileNotFound,
1120 Format(lisFileNotFound2, [FFilename])+LineEnding, mtInformation,[mbCancel]);
1121 // cancel loading file
1122 Exit;
1123 end;
1124
1125 if IDEMessageDialog(lisFileNotFound,
1126 Format(lisFileNotFoundDoYouWantToCreateIt,[FFilename,LineEnding]),
1127 mtInformation,[mbYes,mbNo])=mrYes then
1128 begin
1129 // create new file
1130 NewFlags:=[nfOpenInEditor,nfCreateDefaultSrc];
1131 if ofAddToProject in FFlags then
1132 Include(NewFlags,nfIsPartOfProject);
1133 if FilenameIsPascalSource(FFilename) then
1134 Result:=MainIDE.DoNewEditorFile(FileDescriptorUnit,FFilename,'',NewFlags)
1135 else
1136 Result:=MainIDE.DoNewEditorFile(FileDescriptorText,FFilename,'',NewFlags);
1137 end;
1138 end;
1139
ResolvePossibleSymlinknull1140 function TFileOpener.ResolvePossibleSymlink: TModalResult;
1141 // Check if symlink and ask user if the real file should be opened instead.
1142 // Compiler never resolves symlinks, files in compiler search path must not be resolved.
1143 // If there already is an editor with a "physical" target of a symlink, use it.
1144 var
1145 SPath, Target: String; // Search path and target file for the symlink.
1146 begin
1147 Result := mrOK;
1148 if ofProjectLoading in FFlags then Exit; // Use the given name when project loads.
1149 Target := GetPhysicalFilenameCached(FFileName,false);
1150 if Target = FFilename then Exit; // Not a symlink, continue with FFilename.
1151 // ToDo: Check if there is an editor with a symlink for this "physical" file.
1152
1153 SPath := CodeToolBoss.GetCompleteSrcPathForDirectory('');
1154 // Check if symlink is found in search path or in editor.
1155 if (SearchDirectoryInSearchPath(SPath, ExtractFilePath(FFileName)) > 0)
1156 or Assigned(SourceEditorManager.SourceEditorIntfWithFilename(FFileName))
1157 then
1158 Exit; // Symlink found -> use it.
1159 // Check if "physical" target for a symlink is found in search path or in editor.
1160 if (SearchDirectoryInSearchPath(SPath, ExtractFilePath(Target)) > 0)
1161 or Assigned(SourceEditorManager.SourceEditorIntfWithFilename(Target))
1162 then // Target found -> use Target name.
1163 FFileName := Target
1164 else // Not found anywhere, ask user.
1165 Result := ChooseSymlink(FFileName, Target);
1166 end;
1167
OpenEditorFilenull1168 function TFileOpener.OpenEditorFile(APageIndex, AWindowIndex: integer;
1169 AEditorInfo: TUnitEditorInfo; AFlags: TOpenFlags): TModalResult;
1170 var
1171 s, DiskFilename: String;
1172 Reverting: Boolean;
1173 begin
1174 {$IFDEF IDE_VERBOSE}
1175 DebugLn('');
1176 DebugLn(['*** TFileOpener.OpenEditorFile START "',AFilename,'" ',OpenFlagsToString(Flags),' Window=',WindowIndex,' Page=',PageIndex]);
1177 {$ENDIF}
1178 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TFileOpener.OpenEditorFile START');{$ENDIF}
1179 FPageIndex := APageIndex;
1180 FWindowIndex := AWindowIndex;
1181 FEditorInfo := AEditorInfo;
1182 FFlags := AFlags;
1183
1184 Result:=mrCancel;
1185
1186 // replace macros
1187 if ofConvertMacros in FFlags then begin
1188 if not GlobalMacroList.SubstituteStr(FFilename) then exit;
1189 FFilename:=ExpandFileNameUTF8(FFilename);
1190 end;
1191
1192 if (ofRevert in FFlags) then begin
1193 Result := CheckRevert;
1194 if Result = mrIgnore then exit(mrOK);
1195 Assert(Result = mrOK);
1196 end;
1197
1198 if (ofInternalFile in FFlags) then begin
1199 CheckInternalFile;
1200 // unknown internal file => ignore
1201 exit(mrOK);
1202 end;
1203
1204 // normalize filename
1205 FFilename:=TrimFilename(FFilename);
1206 DiskFilename:=CodeToolBoss.DirectoryCachePool.FindDiskFilename(FFilename);
1207 if DiskFilename<>FFilename then begin
1208 // the case is different
1209 DebugLn(['TFileOpener.OpenEditorFile Fixing file name: ',FFilename,' -> ',DiskFilename]);
1210 FFilename:=DiskFilename;
1211 end;
1212 if not (ofRegularFile in FFlags) then begin
1213 DiskFilename:=GetShellLinkTarget(FFileName);
1214 if DiskFilename<>FFilename then begin
1215 // not regular file
1216 DebugLn(['TFileOpener.OpenEditorFile Fixing file name: ',FFilename,' -> ',DiskFilename]);
1217 FFilename:=DiskFilename;
1218 end;
1219 end;
1220
1221 if FilenameIsAbsolute(FFileName) then begin
1222 Result := ResolvePossibleSymlink;
1223 if Result <> mrOK then exit;
1224 end;
1225
1226 // check to not open directories
1227 s:=ExtractFilename(FFilename);
1228 if (s='') or (s='.') or (s='..') then
1229 begin
1230 DebugLn(['TFileOpener.OpenEditorFile ignoring special file: ',FFilename]);
1231 exit;
1232 end;
1233 if DirectoryExistsUTF8(FFileName) then begin
1234 debugln(['TFileOpener.OpenEditorFile skipping directory ',FFileName]);
1235 exit(mrCancel);
1236 end;
1237
1238 if ([ofAddToRecent,ofRevert,ofVirtualFile]*FFlags=[ofAddToRecent])
1239 and (FFilename<>'') and FilenameIsAbsolute(FFilename) then
1240 EnvironmentOptions.AddToRecentOpenFiles(FFilename);
1241
1242 // check if this is a hidden unit:
1243 // if this is the main unit, it is already
1244 // loaded and needs only to be shown in the sourceeditor/formeditor
1245 if (not (ofRevert in FFlags)) and (CompareFilenames(Project1.MainFilename,FFilename)=0)
1246 then begin
1247 Result:=OpenMainUnit;
1248 exit;
1249 end;
1250
1251 // check for special files
1252 if ([ofRegularFile,ofRevert,ofProjectLoading]*FFlags=[])
1253 and FilenameIsAbsolute(FFilename) and FileExistsCached(FFilename) then begin
1254 // check if file is a lazarus project (.lpi)
1255 if (CompareFileExt(FFilename,'.lpi',false)=0) then
1256 begin
1257 case
1258 IDEQuestionDialog(lisOpenProject, Format(lisOpenTheProject, [FFilename]),
1259 mtConfirmation, [mrYes, lisOpenProject2,
1260 mrNoToAll, lisOpenAsXmlFile,
1261 mrCancel])
1262 of
1263 mrYes: begin
1264 Result:=MainIDE.DoOpenProjectFile(FFilename,[ofAddToRecent]);
1265 exit;
1266 end;
1267 mrNoToAll: include(FFlags, ofRegularFile);
1268 mrCancel: exit(mrCancel);
1269 end;
1270 end;
1271
1272 // check if file is a lazarus package (.lpk)
1273 if (CompareFileExt(FFilename,'.lpk',false)=0) then
1274 begin
1275 case
1276 IDEQuestionDialog(lisOpenPackage,
1277 Format(lisOpenThePackage, [FFilename]),
1278 mtConfirmation, [mrYes, lisCompPalOpenPackage,
1279 mrNoToAll, lisOpenAsXmlFile,
1280 mrCancel])
1281 of
1282 mrYes: begin
1283 Result:=PkgBoss.DoOpenPackageFile(FFilename,[pofAddToRecent],
1284 [ofProjectLoading,ofMultiOpen]*FFlags<>[]);
1285 exit;
1286 end;
1287 mrCancel: exit(mrCancel);
1288 end;
1289 end;
1290 end;
1291
1292 // check if the project knows this file
1293 if (ofRevert in FFlags) then begin
1294 Result := PrepareRevert(DiskFilename);
1295 if Result <> mrOK then exit;
1296 end else begin
1297 Result := PrepareFile;
1298 if Result <> mrOK then exit;
1299 end;
1300
1301 if (FNewEditorInfo <> nil) and (ofAddToProject in FFlags) and (not FNewUnitInfo.IsPartOfProject) then
1302 begin
1303 FNewUnitInfo.IsPartOfProject:=true;
1304 Project1.Modified:=true;
1305 end;
1306
1307 if (FNewEditorInfo <> nil) and (FFlags * [ofProjectLoading, ofRevert] = [])
1308 and (FNewEditorInfo.EditorComponent <> nil) then
1309 begin
1310 Result := ChangeEditorPage;
1311 exit;
1312 end;
1313
1314 Reverting:=ofRevert in FFlags;
1315 if Reverting then
1316 Project1.BeginRevertUnit(FNewUnitInfo);
1317 try
1318
1319 // check if file exists
1320 if FilenameIsAbsolute(FFilename) and (not FileExistsCached(FFilename)) then
1321 begin
1322 // file does not exist
1323 if (ofRevert in FFlags) then begin
1324 // PrepareRevert failed, due to missing file
1325 if not (ofQuiet in FFlags) then begin
1326 IDEMessageDialog(lisRevertFailed, Format(lisPkgMangFileNotFound, [FFilename]),
1327 mtError,[mbCancel]);
1328 end;
1329 Result:=mrCancel;
1330 exit;
1331 end else begin
1332 Result:=OpenNotExistingFile;
1333 exit;
1334 end;
1335 end;
1336
1337 // load the source
1338 if FUnknownFile then
1339 Result := OpenUnknown
1340 else
1341 Result := OpenKnown;
1342 if Result=mrIgnore then exit(mrOK);
1343 if Result<>mrOk then exit;
1344
1345 // check readonly
1346 FNewUnitInfo.FileReadOnly:=FileExistsCached(FNewUnitInfo.Filename)
1347 and (not FileIsWritable(FNewUnitInfo.Filename));
1348 //debugln('[TFileOpener.OpenEditorFile] B');
1349 // open file in source notebook
1350 Result:=OpenFileInSourceEditor(FNewEditorInfo);
1351 if Result<>mrOk then begin
1352 DebugLn(['TFileOpener.OpenEditorFile failed OpenFileInSourceEditor: ',FFilename]);
1353 exit;
1354 end;
1355 // open resource component (designer, form, datamodule, ...)
1356 if FNewUnitInfo.OpenEditorInfoCount = 1 then
1357 Result:=OpenResource;
1358 if Result<>mrOk then begin
1359 DebugLn(['TFileOpener.OpenEditorFile failed OpenResource: ',FFilename]);
1360 exit;
1361 end;
1362 finally
1363 if Reverting then
1364 Project1.EndRevertUnit(FNewUnitInfo);
1365 end;
1366
1367 Result:=mrOk;
1368 //debugln('TFileOpener.OpenEditorFile END "',FFilename,'"');
1369 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TFileOpener.OpenEditorFile END');{$ENDIF}
1370 end;
1371
FindFilenull1372 function TFileOpener.FindFile(SearchPath: String): Boolean;
1373 // Searches for FileName in SearchPath
1374 // If FileName is not found, we'll check extensions pp and pas too
1375 // Returns true if found. FFileName contains the full file+path in that case
1376 var TempFile,TempPath,CurPath: String;
1377 p,c: Integer;
1378 PasExt: TPascalExtType;
1379
SetFileIfExistsnull1380 function SetFileIfExists(const Ext: String): Boolean;
1381 var
1382 FinalFile: String;
1383 begin
1384 FinalFile:=ExpandFileNameUTF8(CurPath+TempFile+Ext);
1385 Result:=FileExistsCached(FinalFile);
1386 if Result then
1387 FFileName:=FinalFile;
1388 end;
1389
1390 begin
1391 if SearchPath='' then SearchPath:='.';
1392 Result:=true;
1393 TempPath:=SearchPath;
1394 while TempPath<>'' do begin
1395 p:=pos(';',TempPath);
1396 if p=0 then p:=length(TempPath)+1;
1397 CurPath:=copy(TempPath,1,p-1);
1398 Delete(TempPath,1,p);
1399 if CurPath='' then continue;
1400 CurPath:=AppendPathDelim(CurPath);
1401 if not FilenameIsAbsolute(CurPath) then begin
1402 if FActiveUnitInfo.IsVirtual then
1403 CurPath:=AppendPathDelim(Project1.Directory)+CurPath
1404 else
1405 CurPath:=AppendPathDelim(ExtractFilePath(FActiveUnitInfo.Filename))+CurPath;
1406 end;
1407 for c:=0 to 2 do begin
1408 TempFile:='';
1409 // FPC searches first lowercase, then keeping case, then uppercase
1410 case c of
1411 0: TempFile:=LowerCase(FFileName);
1412 1: TempFile:=FFileName;
1413 2: TempFile:=UpperCase(FFileName);
1414 end;
1415 if ExtractFileExt(TempFile)='' then begin
1416 for PasExt:=Low(TPascalExtType) to High(TPascalExtType) do
1417 if SetFileIfExists(PascalExtension[PasExt]) then exit;
1418 end
1419 else
1420 if SetFileIfExists('') then exit;
1421 end;
1422 end;
1423 Result:=false;
1424 end;
1425
CheckIfIncludeDirectiveInFrontnull1426 function TFileOpener.CheckIfIncludeDirectiveInFront(const Line: string;
1427 X: integer): boolean;
1428 var
1429 DirectiveEnd, DirectiveStart: integer;
1430 Directive: string;
1431 begin
1432 Result:=false;
1433 DirectiveEnd:=X;
1434 while (DirectiveEnd>1) and (Line[DirectiveEnd-1] in [' ',#9]) do
1435 dec(DirectiveEnd);
1436 DirectiveStart:=DirectiveEnd-1;
1437 while (DirectiveStart>0) and (Line[DirectiveStart]<>'$') do
1438 dec(DirectiveStart);
1439 Directive:=uppercase(copy(Line,DirectiveStart,DirectiveEnd-DirectiveStart));
1440 if (Directive='$INCLUDE') or (Directive='$I') then begin
1441 if ((DirectiveStart>1) and (Line[DirectiveStart-1]='{'))
1442 or ((DirectiveStart>2)
1443 and (Line[DirectiveStart-2]='(') and (Line[DirectiveStart-1]='*'))
1444 then begin
1445 Result:=true;
1446 end;
1447 end;
1448 end;
1449
TFileOpener.GetFilenameAtRowColnull1450 function TFileOpener.GetFilenameAtRowCol(XY: TPoint): string;
1451 var
1452 Line: string;
1453 Len, Stop: integer;
1454 StopChars: set of char;
1455 begin
1456 Result := '';
1457 FIsIncludeDirective:=false;
1458 if (XY.Y >= 1) and (XY.Y <= FActiveSrcEdit.EditorComponent.Lines.Count) then
1459 begin
1460 Line := FActiveSrcEdit.EditorComponent.Lines.Strings[XY.Y - 1];
1461 Len := Length(Line);
1462 if (XY.X >= 1) and (XY.X <= Len + 1) then begin
1463 StopChars := [',',';',':','[',']','{','}','(',')','''','"','`'
1464 ,'#','%','=','>'];
1465 Stop := XY.X;
1466 if Stop>Len then Stop:=Len;
1467 while (Stop >= 1) and (not (Line[Stop] in ['''','"','`'])) do
1468 dec(Stop);
1469 if Stop<1 then
1470 StopChars:=StopChars+[' ',#9]; // no quotes in front => use spaces as boundaries
1471 Stop := XY.X;
1472 while (Stop <= Len) and (not (Line[Stop] in StopChars)) do
1473 Inc(Stop);
1474 while (XY.X > 1) and (not (Line[XY.X - 1] in StopChars)) do
1475 Dec(XY.X);
1476 if Stop > XY.X then begin
1477 Result := Copy(Line, XY.X, Stop - XY.X);
1478 FIsIncludeDirective:=CheckIfIncludeDirectiveInFront(Line,XY.X);
1479 end;
1480 end;
1481 end;
1482 end;
1483
TFileOpener.OpenFileAtCursornull1484 function TFileOpener.OpenFileAtCursor: TModalResult;
1485
ShowNotFoundnull1486 function ShowNotFound(aFilename: string): TModalResult;
1487 begin
1488 Result:=mrCancel;
1489 if aFilename<>'' then
1490 IDEMessageDialog(lisOpenFileAtCursor, lisFileNotFound+':'#13+aFileName,
1491 mtError, [mbOk]);
1492 end;
1493
1494 var
1495 Found: Boolean;
1496 BaseDir: String;
1497 NewFilename,InFilename: string;
1498 AUnitName: String;
1499 SearchPath, Line: String;
1500 Edit: TIDESynEditor;
1501 FoundType: TFindFileAtCursorFlag;
1502 XY: TPoint;
1503 Len: Integer;
1504 begin
1505 Result:=mrCancel;
1506
1507 {$IFDEF VerboseFindFileAtCursor}
1508 debugln(['TFileOpener.OpenFileAtCursor ',FActiveUnitInfo<>nil]);
1509 {$ENDIF}
1510 if (FActiveSrcEdit=nil) or (FActiveUnitInfo=nil) then exit;
1511 BaseDir:=ExtractFilePath(FActiveUnitInfo.Filename);
1512 {$IFDEF VerboseFindFileAtCursor}
1513 debugln(['TFileOpener.OpenFileAtCursor File="',FActiveUnitInfo.Filename,'"']);
1514 {$ENDIF}
1515
1516 Found:=false;
1517
1518 // check if a filename is selected
1519 Edit:=FActiveSrcEdit.EditorComponent;
1520 if Edit.SelAvail and (Edit.BlockBegin.Y=Edit.BlockBegin.X) then begin
1521 {$IFDEF VerboseFindFileAtCursor}
1522 debugln(['TFileOpener.OpenFileAtCursor Edit.SelAvail Edit.SelText="',Edit.SelText,'"']);
1523 {$ENDIF}
1524 FFileName:=ResolveDots(Edit.SelText);
1525 if not FilenameIsAbsolute(FFileName) then
1526 FFileName:=ResolveDots(BaseDir+FFileName);
1527 if FilenameIsAbsolute(FFileName) then begin
1528 if FileExistsCached(FFileName) then
1529 Found:=true
1530 else
1531 exit(ShowNotFound(FFileName));
1532 end;
1533 end;
1534
1535
1536 XY:=Edit.LogicalCaretXY;
1537 if (XY.Y >= 1) and (XY.Y <= FActiveSrcEdit.EditorComponent.Lines.Count) then
1538 begin
1539 Line := FActiveSrcEdit.EditorComponent.Lines.Strings[XY.Y - 1];
1540 Len := Length(Line);
1541 if (XY.X>1) and (XY.X-1<=Len) and IsWordChar[Line[XY.X-1]]
1542 and ((XY.X>Len) or IsNonWordChar[Line[XY.X]]) then
1543 dec(XY.X);
1544 end;
1545
1546
1547 // in a Pascal file use codetools
1548 if FilenameIsPascalSource(FActiveUnitInfo.Filename) then begin
1549 {$IFDEF VerboseFindFileAtCursor}
1550 debugln(['TFileOpener.OpenFileAtCursor FilenameIsPascalSource -> using codetools']);
1551 {$ENDIF}
1552 if MainIDE.BeginCodeTool(FActiveSrcEdit,FActiveUnitInfo,[]) then begin
1553 if CodeToolBoss.FindFileAtCursor(FActiveSrcEdit.CodeBuffer,
1554 XY.X,XY.Y,FoundType,FFileName) then
1555 Found:=true
1556 else begin
1557 FFileName:=FActiveSrcEdit.EditorComponent.GetWordAtRowCol(
1558 FActiveSrcEdit.EditorComponent.LogicalCaretXY);
1559 exit(ShowNotFound(FFileName));
1560 end;
1561 end;
1562 end;
1563
1564 if not Found then begin
1565 // parse FFileName at cursor
1566 FFileName:=GetFilenameAtRowCol(FActiveSrcEdit.EditorComponent.LogicalCaretXY);
1567 if FFileName='' then exit;
1568 // check if absolute FFileName
1569 if FilenameIsAbsolute(FFileName) then begin
1570 if FileExistsCached(FFileName) then
1571 Found:=true
1572 else
1573 exit(ShowNotFound(FFileName));
1574 end;
1575
1576 if FIsIncludeDirective then
1577 begin
1578 if (not Found) then begin
1579 // search include file
1580 SearchPath:='.;'+CodeToolBoss.DefineTree.GetIncludePathForDirectory(BaseDir);
1581 if FindFile(SearchPath) then // sets FFileName if result=true
1582 Found:=true;
1583 end;
1584 end else
1585 begin
1586 if (not Found) then
1587 begin
1588 // search pascal unit without extension
1589 AUnitName:=FFileName;
1590 InFilename:='';
1591 NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
1592 BaseDir,AUnitName,InFilename,true);
1593 if NewFilename<>'' then begin
1594 Found:=true;
1595 FFileName:=NewFilename;
1596 end;
1597 end;
1598
1599 if (not Found) and (ExtractFileExt(FFileName)<>'') then
1600 begin
1601 // search pascal unit with extension
1602 AUnitName:=ExtractFileNameOnly(FFileName);
1603 InFilename:=FFileName;
1604 NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
1605 BaseDir,AUnitName,InFilename,true);
1606 if NewFilename<>'' then begin
1607 Found:=true;
1608 FFileName:=NewFilename;
1609 end;
1610 end;
1611 end;
1612 end;
1613
1614 if (not Found) then begin
1615 // simple search relative to current unit
1616 InFilename:=AppendPathDelim(BaseDir)+FFileName;
1617 if FileExistsCached(InFilename) then begin
1618 Found:=true;
1619 FFileName:=InFilename;
1620 end;
1621 end;
1622
1623 if (not Found) and (System.Pos('.',FFileName)>0) and (not FIsIncludeDirective) then
1624 begin
1625 // for example 'SysUtils.CompareText'
1626 FFileName:=FActiveSrcEdit.EditorComponent.GetWordAtRowCol(
1627 FActiveSrcEdit.EditorComponent.LogicalCaretXY);
1628 if IsValidIdent(FFileName) then begin
1629 // search pascal unit
1630 AUnitName:=FFileName;
1631 InFilename:='';
1632 NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
1633 BaseDir,AUnitName,InFilename,true);
1634 if NewFilename<>'' then begin
1635 Found:=true;
1636 FFileName:=NewFilename;
1637 end;
1638 end;
1639 end;
1640
1641 if Found then begin
1642 // open, FFileName is set earlier.
1643 InputHistories.SetFileDialogSettingsInitialDir(ExtractFilePath(FFileName));
1644 FUseWindowID:=False;
1645 Result:=OpenEditorFile(-1, -1, nil, [ofAddToRecent]);
1646 end else
1647 exit(ShowNotFound(FFileName));
1648 end;
1649
OpenMainUnitnull1650 function TFileOpener.OpenMainUnit: TModalResult;
1651 var
1652 MainUnitInfo: TUnitInfo;
1653 begin
1654 {$IFDEF IDE_VERBOSE}
1655 debugln(['[TFileOpener.OpenMainUnit] A ProjectLoading=',ofProjectLoading in Flags,' MainUnitID=',Project1.MainUnitID]);
1656 {$ENDIF}
1657 Result:=mrCancel;
1658 if (Project1=nil) or (Project1.MainUnitID<0) then exit;
1659 MainUnitInfo:=Project1.MainUnitInfo;
1660
1661 // check if main unit is already open in source editor
1662 if (MainUnitInfo.OpenEditorInfoCount > 0) and (not (ofProjectLoading in FFlags)) then
1663 begin
1664 // already loaded -> switch to source editor
1665 SourceEditorManager.ActiveEditor := TSourceEditor(MainUnitInfo.OpenEditorInfo[0].EditorComponent);
1666 SourceEditorManager.ShowActiveWindowOnTop(True);
1667 Result:=mrOk;
1668 exit;
1669 end;
1670
1671 // open file in source notebook
1672 Result:=OpenFileInSourceEditor(MainUnitInfo.GetClosedOrNewEditorInfo);
1673 if Result<>mrOk then exit;
1674
1675 Result:=mrOk;
1676 {$IFDEF IDE_VERBOSE}
1677 debugln('[TFileOpener.OpenMainUnit] END');
1678 {$ENDIF}
1679 end;
1680
RevertMainUnitnull1681 function TFileOpener.RevertMainUnit: TModalResult;
1682 begin
1683 Result:=mrOk;
1684 if Project1.MainUnitID<0 then exit;
1685 FFileName:='';
1686 FUseWindowID:=True;
1687 if Project1.MainUnitInfo.OpenEditorInfoCount > 0 then
1688 // main unit is loaded, so we can just revert
1689 Result:=OpenEditorFile(Project1.MainUnitInfo.EditorInfo[0].PageIndex,
1690 Project1.MainUnitInfo.EditorInfo[0].WindowID, nil, [ofRevert])
1691 else begin
1692 // main unit is only loaded in background
1693 // -> just reload the source and update the source name
1694 Result:=Project1.MainUnitInfo.ReadUnitSource(true,true);
1695 end;
1696 end;
1697
CheckMainSrcLCLInterfacesnull1698 function CheckMainSrcLCLInterfaces(Silent: boolean): TModalResult;
1699 var
1700 MainUnitInfo: TUnitInfo;
1701 MainUsesSection,ImplementationUsesSection: TStrings;
1702 MsgResult: TModalResult;
1703 begin
1704 Result:=mrOk;
1705 if (Project1=nil) then exit;
1706 if Project1.SkipCheckLCLInterfaces then exit;
1707 MainUnitInfo:=Project1.MainUnitInfo;
1708 if (MainUnitInfo=nil) or (MainUnitInfo.Source=nil) then exit;
1709 if PackageGraph.FindDependencyRecursively(Project1.FirstRequiredDependency,
1710 PackageGraph.LCLBasePackage)=nil
1711 then
1712 exit; // project does not use LCLBase
1713 // project uses LCLBase
1714 MainUsesSection:=nil;
1715 ImplementationUsesSection:=nil;
1716 try
1717 if not CodeToolBoss.FindUsedUnitNames(MainUnitInfo.Source,
1718 MainUsesSection,ImplementationUsesSection) then exit;
1719 if (SearchInStringListI(MainUsesSection,'forms')<0)
1720 and (SearchInStringListI(ImplementationUsesSection,'forms')<0) then
1721 exit;
1722 // project uses lcl unit Forms
1723 if (SearchInStringListI(MainUsesSection,'interfaces')>=0)
1724 or (SearchInStringListI(ImplementationUsesSection,'interfaces')>=0) then
1725 exit;
1726 // project uses lcl unit Forms, but not unit interfaces
1727 // this will result in strange linker error
1728 if not Silent then
1729 begin
1730 MsgResult:=IDEQuestionDialog(lisCCOWarningCaption,
1731 Format(lisTheProjectDoesNotUseTheLCLUnitInterfacesButItSeems, [LineEnding]),
1732 mtWarning, [mrYes, lisAddUnitInterfaces,
1733 mrNo, lisIgnore,
1734 mrNoToAll, lisAlwaysIgnore,
1735 mrCancel]);
1736 case MsgResult of
1737 mrNo: exit;
1738 mrNoToAll: begin Project1.SkipCheckLCLInterfaces:=true; exit; end;
1739 mrCancel: exit(mrCancel);
1740 end;
1741 end;
1742 CodeToolBoss.AddUnitToMainUsesSection(MainUnitInfo.Source,'Interfaces','');
1743 finally
1744 MainUsesSection.Free;
1745 ImplementationUsesSection.Free;
1746 end;
1747 end;
1748
1749 procedure AddRecentProjectFile(const AFilename: string);
1750 begin
1751 EnvironmentOptions.AddToRecentProjectFiles(AFilename);
1752 MainIDE.SetRecentProjectFilesMenu;
1753 MainIDE.SaveEnvironment;
1754 end;
1755
1756 procedure RemoveRecentProjectFile(const AFilename: string);
1757 begin
1758 EnvironmentOptions.RemoveFromRecentProjectFiles(AFilename);
1759 MainIDE.SetRecentProjectFilesMenu;
1760 MainIDE.SaveEnvironment;
1761 end;
1762
AddUnitToProjectnull1763 function AddUnitToProject(const AEditor: TSourceEditorInterface): TModalResult;
1764 var
1765 ActiveSourceEditor: TSourceEditor;
1766 ActiveUnitInfo: TUnitInfo;
1767 s, ShortUnitName, LFMFilename, LFMType, LFMComponentName,
1768 LFMClassName: string;
1769 OkToAdd: boolean;
1770 Owners: TFPList;
1771 i: Integer;
1772 APackage: TLazPackage;
1773 MsgResult: TModalResult;
1774 LFMCode: TCodeBuffer;
1775 begin
1776 Result:=mrCancel;
1777 if AEditor<>nil then
1778 begin
1779 ActiveSourceEditor := AEditor as TSourceEditor;
1780 if not MainIDE.BeginCodeTool(ActiveSourceEditor,ActiveUnitInfo,[ctfUseGivenSourceEditor]) then exit;
1781 end else
1782 begin
1783 ActiveSourceEditor:=nil;
1784 if not MainIDE.BeginCodeTool(ActiveSourceEditor,ActiveUnitInfo,[]) then exit;
1785 end;
1786 if (ActiveUnitInfo=nil) then exit;
1787 if ActiveUnitInfo.IsPartOfProject then begin
1788 if not ActiveUnitInfo.IsVirtual then
1789 s:=Format(lisTheFile, [ActiveUnitInfo.Filename])
1790 else
1791 s:=Format(lisTheFile, [ActiveSourceEditor.PageName]);
1792 s:=Format(lisisAlreadyPartOfTheProject, [s]);
1793 IDEMessageDialog(lisInformation, s, mtInformation, [mbOk]);
1794 exit;
1795 end;
1796 if not ActiveUnitInfo.IsVirtual then
1797 s:='"'+ActiveUnitInfo.Filename+'"'
1798 else
1799 s:='"'+ActiveSourceEditor.PageName+'"';
1800 if (ActiveUnitInfo.Unit_Name<>'')
1801 and (Project1.IndexOfUnitWithName(ActiveUnitInfo.Unit_Name,true,ActiveUnitInfo)>=0) then
1802 begin
1803 IDEMessageDialog(lisInformation, Format(
1804 lisUnableToAddToProjectBecauseThereIsAlreadyAUnitWith, [s]),
1805 mtInformation, [mbOk]);
1806 exit;
1807 end;
1808
1809 Owners:=PkgBoss.GetPossibleOwnersOfUnit(ActiveUnitInfo.Filename,[]);
1810 try
1811 if (Owners<>nil) then begin
1812 for i:=0 to Owners.Count-1 do begin
1813 if TObject(Owners[i]) is TLazPackage then begin
1814 APackage:=TLazPackage(Owners[i]);
1815 MsgResult:=IDEQuestionDialog(lisAddPackageRequirement,
1816 Format(lisTheUnitBelongsToPackage, [APackage.IDAsString]),
1817 mtConfirmation, [mrYes, lisAddPackageToProject2,
1818 mrIgnore, lisAddUnitNotRecommended,
1819 mrCancel],'');
1820 case MsgResult of
1821 mrYes:
1822 begin
1823 PkgBoss.AddProjectDependency(Project1,APackage);
1824 exit;
1825 end;
1826 mrIgnore: ;
1827 else
1828 exit;
1829 end;
1830 end;
1831 end;
1832 end;
1833 finally
1834 Owners.Free;
1835 end;
1836
1837 if FilenameIsPascalUnit(ActiveUnitInfo.Filename)
1838 and (EnvironmentOptions.CharcaseFileAction<>ccfaIgnore) then
1839 begin
1840 // ask user to apply naming conventions
1841 Result:=RenameUnitLowerCase(ActiveUnitInfo,true);
1842 if Result=mrIgnore then Result:=mrOk;
1843 if Result<>mrOk then begin
1844 DebugLn('AddActiveUnitToProject A RenameUnitLowerCase failed ',ActiveUnitInfo.Filename);
1845 exit;
1846 end;
1847 end;
1848
1849 if IDEMessageDialog(lisConfirmation, Format(lisAddToProject, [s]),
1850 mtConfirmation, [mbYes, mbCancel]) in [mrOk, mrYes]
1851 then begin
1852 OkToAdd:=True;
1853 if FilenameIsPascalUnit(ActiveUnitInfo.Filename) then
1854 OkToAdd:=CheckDirIsInSearchPath(ActiveUnitInfo,False)
1855 else if CompareFileExt(ActiveUnitInfo.Filename,'inc',false)=0 then
1856 OkToAdd:=CheckDirIsInSearchPath(ActiveUnitInfo,True);
1857 if OkToAdd then begin
1858 ActiveUnitInfo.IsPartOfProject:=true;
1859 Project1.Modified:=true;
1860 if (FilenameIsPascalUnit(ActiveUnitInfo.Filename))
1861 and (pfMainUnitHasUsesSectionForAllUnits in Project1.Flags)
1862 then begin
1863 ActiveUnitInfo.ReadUnitNameFromSource(false);
1864 ShortUnitName:=ActiveUnitInfo.CreateUnitName;
1865 if (ShortUnitName<>'') then begin
1866 if CodeToolBoss.AddUnitToMainUsesSection(Project1.MainUnitInfo.Source,ShortUnitName,'')
1867 then
1868 Project1.MainUnitInfo.Modified:=true;
1869 end;
1870 end;
1871 end;
1872 end;
1873
1874 if Project1.AutoCreateForms
1875 and (pfMainUnitHasCreateFormStatements in Project1.Flags)
1876 and FilenameIsPascalUnit(ActiveUnitInfo.Filename) then
1877 begin
1878 UpdateUnitInfoResourceBaseClass(ActiveUnitInfo,true);
1879 if ActiveUnitInfo.ResourceBaseClass in [pfcbcForm,pfcbcCustomForm,pfcbcDataModule] then
1880 begin
1881 LFMFilename:=ActiveUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(ActiveUnitInfo.Filename,true);
1882 if LoadCodeBuffer(LFMCode,LFMFilename,[lbfUpdateFromDisk],false)=mrOk then
1883 begin
1884 // read lfm header
1885 ReadLFMHeader(LFMCode.Source,LFMType,LFMComponentName,LFMClassName);
1886 if (LFMComponentName<>'')
1887 and (LFMClassName<>'') then begin
1888 if IDEMessageDialog(lisAddToStartupComponents,
1889 Format(lisShouldTheComponentBeAutoCreatedWhenTheApplicationS, [
1890 LFMComponentName]),
1891 mtInformation,[mbYes,mbNo])=mrYes then
1892 begin
1893 Project1.AddCreateFormToProjectFile(LFMClassName,LFMComponentName);
1894 end;
1895 end;
1896 end;
1897 end;
1898 end;
1899 end;
1900
1901 procedure UpdateSourceNames;
1902 var
1903 i: integer;
1904 AnUnitInfo: TUnitInfo;
1905 SourceName, PageName: string;
1906 AEditor: TSourceEditor;
1907 begin
1908 for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin
1909 AEditor := SourceEditorManager.SourceEditors[i];
1910 AnUnitInfo := Project1.UnitWithEditorComponent(AEditor);
1911 if AnUnitInfo=nil then continue;
1912 if FilenameIsPascalUnit(AnUnitInfo.Filename) then begin
1913 SourceName:=CodeToolBoss.GetCachedSourceName(AnUnitInfo.Source);
1914 if SourceName<>'' then
1915 AnUnitInfo.ReadUnitNameFromSource(true);
1916 end else
1917 SourceName:='';
1918 PageName:=CreateSrcEditPageName(SourceName, AnUnitInfo.Filename, AEditor);
1919 AEditor.PageName := PageName;
1920 end;
1921 end;
1922
CheckEditorNeedsSavenull1923 function CheckEditorNeedsSave(AEditor: TSourceEditorInterface;
1924 IgnoreSharedEdits: Boolean): Boolean;
1925 var
1926 AnEditorInfo: TUnitEditorInfo;
1927 AnUnitInfo: TUnitInfo;
1928 begin
1929 Result := False;
1930 if AEditor = nil then exit;
1931 AnEditorInfo := Project1.EditorInfoWithEditorComponent(AEditor);
1932 if AnEditorInfo = nil then exit;
1933
1934 AnUnitInfo := AnEditorInfo.UnitInfo;
1935 if (AnUnitInfo.OpenEditorInfoCount > 1) and IgnoreSharedEdits then
1936 exit;
1937
1938 // save some meta data of the source
1939 SaveSrcEditorProjectSpecificSettings(AnEditorInfo);
1940
1941 Result := (AEditor.Modified) or (AnUnitInfo.Modified);
1942 end;
1943
1944 procedure ArrangeSourceEditorAndMessageView(PutOnTop: boolean);
1945 begin
1946 if SourceEditorManager.SourceWindowCount > 0 then
1947 begin
1948 if PutOnTop then
1949 begin
1950 IDEWindowCreators.ShowForm(MessagesView,true);
1951 SourceEditorManager.ShowActiveWindowOnTop(False);
1952 exit;
1953 end;
1954 end;
1955 MainIDE.DoShowMessagesView(PutOnTop);
1956 end;
1957
SomethingOfProjectIsModifiednull1958 function SomethingOfProjectIsModified(Verbose: boolean): boolean;
1959 begin
1960 Result:=(Project1<>nil)
1961 and (Project1.SomethingModified(true,true,Verbose)
1962 or SourceEditorManager.SomethingModified(Verbose));
1963 end;
1964
FileExistsInIDEnull1965 function FileExistsInIDE(const Filename: string;
1966 SearchFlags: TProjectFileSearchFlags): boolean;
1967 begin
1968 Result:=FileExistsCached(Filename)
1969 or ((Project1<>nil) and (Project1.UnitInfoWithFilename(Filename,SearchFlags)<>nil));
1970 end;
1971
BeautifySrcnull1972 function BeautifySrc(const s: string): string;
1973 begin
1974 Result:=CodeToolBoss.SourceChangeCache.BeautifyCodeOptions.BeautifyStatement(s,0);
1975 end;
1976
NewFilenull1977 function NewFile(NewFileDescriptor: TProjectFileDescriptor;
1978 var NewFilename: string; NewSource: string;
1979 NewFlags: TNewFlags; NewOwner: TObject): TModalResult;
1980 var
1981 NewUnitInfo: TUnitInfo;
1982 NewSrcEdit: TSourceEditor;
1983 NewUnitName: string;
1984 NewBuffer: TCodeBuffer;
1985 OldUnitIndex: Integer;
1986 AncestorType: TPersistentClass;
1987 LFMFilename: String;
1988 SearchFlags: TProjectFileSearchFlags;
1989 LFMSourceText: String;
1990 LFMCode: TCodeBuffer;
1991 AProject: TProject;
1992 LRSFilename: String;
1993 ResType: TResourceType;
1994 SrcNoteBook: TSourceNotebook;
1995 AShareEditor: TSourceEditor;
1996 DisableAutoSize: Boolean;
1997 APackage: TLazPackage;
1998 IsPartOfProject: Boolean;
1999 RequiredPackages: String;
2000 Src: String;
2001 i: Integer;
2002 LFindDesignerBaseClassByName: Boolean = True;
2003 PreventAutoSize: Boolean;
2004 begin
2005 //debugln('NewFile A NewFilename=',NewFilename);
2006 // empty NewFilename is ok, it will be auto generated
2007 SaveEditorChangesToCodeCache(nil);
2008
2009 // convert macros in filename
2010 if nfConvertMacros in NewFlags then begin
2011 if not GlobalMacroList.SubstituteStr(NewFilename) then begin
2012 Result:=mrCancel;
2013 exit;
2014 end;
2015 end;
2016
2017 Result:=NewFileDescriptor.Init(NewFilename,NewOwner,NewSource,nfQuiet in NewFlags);
2018 if Result<>mrOk then exit;
2019
2020 if FilenameIsAbsolute(NewFilename) and DirectoryExistsUTF8(NewFilename) then
2021 begin
2022 IDEMessageDialog(lisFileIsDirectory,
2023 lisUnableToCreateNewFileBecauseThereIsAlreadyADirecto,
2024 mtError,[mbCancel]);
2025 exit(mrCancel);
2026 end;
2027
2028 if NewOwner is TProject then
2029 AProject:=TProject(NewOwner)
2030 else
2031 AProject:=Project1;
2032 if NewOwner is TLazPackage then
2033 APackage:=TLazPackage(NewOwner)
2034 else
2035 APackage:=nil;
2036
2037 OldUnitIndex:=AProject.IndexOfFilename(NewFilename);
2038 if OldUnitIndex>=0 then begin
2039 // the file is not really new
2040 // => close form
2041 Result:=CloseUnitComponent(AProject.Units[OldUnitIndex],
2042 [cfCloseDependencies,cfSaveDependencies]);
2043 if Result<>mrOk then
2044 begin
2045 debugln(['NewFile CloseUnitComponent failed']);
2046 exit;
2047 end;
2048 end;
2049
2050 IsPartOfProject:=(nfIsPartOfProject in NewFlags)
2051 or (NewOwner is TProject)
2052 or (AProject.FileIsInProjectDir(NewFilename)
2053 and (not (nfIsNotPartOfProject in NewFlags)));
2054
2055 // add required packages
2056 //debugln(['NewFile NewFileDescriptor.RequiredPackages="',NewFileDescriptor.RequiredPackages,'" ',DbgSName(NewFileDescriptor)]);
2057 RequiredPackages:=NewFileDescriptor.RequiredPackages;
2058 if (RequiredPackages='') and (NewFileDescriptor.ResourceClass<>nil) then
2059 begin
2060 if (NewFileDescriptor.ResourceClass.InheritsFrom(TForm))
2061 or (NewFileDescriptor.ResourceClass.InheritsFrom(TFrame)) then
2062 RequiredPackages:='LCL';
2063 end;
2064 if RequiredPackages<>'' then
2065 begin
2066 if IsPartOfProject then begin
2067 Result:=PkgBoss.AddProjectDependencies(Project1,RequiredPackages);
2068 if Result<>mrOk then
2069 begin
2070 debugln(['NewFile PkgBoss.AddProjectDependencies failed RequiredPackages="',RequiredPackages,'"']);
2071 exit;
2072 end;
2073 end;
2074 if APackage<>nil then
2075 begin
2076 Result:=PkgBoss.AddPackageDependency(APackage,RequiredPackages);
2077 if Result<>mrOk then
2078 begin
2079 debugln(['NewFile PkgBoss.AddPackageDependency failed RequiredPackages="',RequiredPackages,'"']);
2080 exit;
2081 end;
2082 end;
2083 end;
2084
2085 // check if the new file fits
2086 Result:=NewFileDescriptor.CheckOwner(nfQuiet in NewFlags);
2087 if Result<>mrOk then
2088 begin
2089 debugln(['NewFile NewFileDescriptor.CheckOwner failed NewFilename="',NewFilename,'"']);
2090 exit;
2091 end;
2092
2093 // create new codebuffer and apply naming conventions
2094 NewBuffer:=nil;
2095 NewUnitName:='';
2096 Result:=CreateNewCodeBuffer(NewFileDescriptor,NewOwner,NewFilename,NewBuffer,NewUnitName);
2097 if Result<>mrOk then
2098 begin
2099 debugln(['NewFile CreateNewCodeBuffer failed NewFilename="',NewFilename,'"']);
2100 exit;
2101 end;
2102 NewFilename:=NewBuffer.Filename;
2103
2104 if OldUnitIndex>=0 then begin
2105 // the file is not really new
2106 NewUnitInfo:=AProject.Units[OldUnitIndex];
2107 // assign source
2108 NewUnitInfo.Source:=NewBuffer;
2109 end else
2110 NewUnitInfo:=TUnitInfo.Create(NewBuffer);
2111 //debugln(['NewFile ',NewUnitInfo.Filename,' ',NewFilename]);
2112 if (CompareText(NewUnitInfo.Unit_Name,NewUnitName)=0) then
2113 NewUnitInfo.Unit_Name:=NewUnitName;
2114 NewUnitInfo.BuildFileIfActive:=NewFileDescriptor.BuildFileIfActive;
2115 NewUnitInfo.RunFileIfActive:=NewFileDescriptor.RunFileIfActive;
2116
2117 // create source code
2118 //debugln('NewFile A nfCreateDefaultSrc=',nfCreateDefaultSrc in NewFlags,' ResourceClass=',dbgs(NewFileDescriptor.ResourceClass));
2119 if nfCreateDefaultSrc in NewFlags then begin
2120 if (NewFileDescriptor.ResourceClass<>nil) then begin
2121 NewUnitInfo.ComponentName:=NewUniqueComponentName(NewFileDescriptor.DefaultResourceName);
2122 NewUnitInfo.ComponentResourceName:='';
2123 end;
2124 Src:=NewFileDescriptor.CreateSource(NewUnitInfo.Filename,NewUnitName,NewUnitInfo.ComponentName);
2125 Src:=SourceEditorManager.Beautify(Src);
2126 //debugln(['NewFile ',dbgtext(Src)]);
2127 Src:=CodeToolBoss.SourceChangeCache.BeautifyCodeOptions.BeautifyStatement(Src,0);
2128 NewUnitInfo.Source.Source:=Src;
2129 end else begin
2130 if nfBeautifySrc in NewFlags then
2131 NewBuffer.Source:=BeautifySrc(NewSource)
2132 else
2133 NewBuffer.Source:=NewSource;
2134 end;
2135 NewUnitInfo.Modified:=true;
2136
2137 // add to project
2138 NewUnitInfo.Loaded:=true;
2139 NewUnitInfo.IsPartOfProject:=IsPartOfProject;
2140 if OldUnitIndex<0 then begin
2141 AProject.AddFile(NewUnitInfo,
2142 NewFileDescriptor.AddToProject
2143 and NewFileDescriptor.IsPascalUnit
2144 and NewUnitInfo.IsPartOfProject
2145 and (pfMainUnitHasUsesSectionForAllUnits in AProject.Flags));
2146 end;
2147
2148 // syntax highlighter type
2149 NewUnitInfo.DefaultSyntaxHighlighter := FilenameToLazSyntaxHighlighter(NewFilename);
2150
2151 NewSrcEdit := Nil;
2152 if nfOpenInEditor in NewFlags then begin
2153 // open a new sourceeditor
2154 SrcNoteBook := SourceEditorManager.ActiveOrNewSourceWindow;
2155 AShareEditor := nil;
2156 if NewUnitInfo.OpenEditorInfoCount > 0 then
2157 AShareEditor := TSourceEditor(NewUnitInfo.OpenEditorInfo[0].EditorComponent);
2158 NewSrcEdit := SrcNoteBook.NewFile(
2159 CreateSrcEditPageName(NewUnitInfo.Unit_Name, NewUnitInfo.Filename, AShareEditor),
2160 NewUnitInfo.Source, True, AShareEditor);
2161 MainIDEBar.itmFileClose.Enabled:=True;
2162 MainIDEBar.itmFileCloseAll.Enabled:=True;
2163 NewSrcEdit.SyntaxHighlighterType:=NewUnitInfo.EditorInfo[0].SyntaxHighlighter;
2164 NewUnitInfo.GetClosedOrNewEditorInfo.EditorComponent := NewSrcEdit;
2165 NewSrcEdit.EditorComponent.CaretXY := Point(1,1);
2166
2167 // create component
2168 AncestorType:=NewFileDescriptor.ResourceClass;
2169 if AncestorType <> nil then
2170 begin
2171 // loop for Inherited Items
2172 for i:=0 to BaseFormEditor1.StandardDesignerBaseClassesCount - 1 do
2173 if AncestorType.InheritsFrom(BaseFormEditor1.StandardDesignerBaseClasses[i]) then
2174 begin
2175 LFindDesignerBaseClassByName := False;
2176 Break;
2177 end;
2178 if LFindDesignerBaseClassByName then
2179 AncestorType:=FormEditor1.FindDesignerBaseClassByName(AncestorType.ClassName, True);
2180 end;
2181 //DebugLn(['NewFile AncestorType=',dbgsName(AncestorType),' ComponentName',NewUnitInfo.ComponentName]);
2182 if AncestorType<>nil then begin
2183 ResType:=MainBuildBoss.GetResourceType(NewUnitInfo);
2184 LFMSourceText:=NewFileDescriptor.GetResourceSource(NewUnitInfo.ComponentName);
2185 //DebugLn(['NewFile LFMSourceText=',LFMSourceText]);
2186 if LFMSourceText<>'' then begin
2187 // the NewFileDescriptor provides a custom .lfm source
2188 // -> put it into a new .lfm buffer and load it
2189 LFMFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lfm');
2190 LFMCode:=CodeToolBoss.CreateFile(LFMFilename);
2191 LFMCode.Source:=LFMSourceText;
2192 //debugln('NewFile A ',LFMFilename);
2193 Result:=LoadLFM(NewUnitInfo,LFMCode,[],[]);
2194 //DebugLn(['NewFile ',dbgsName(NewUnitInfo.Component),' ',dbgsName(NewUnitInfo.Component.ClassParent)]);
2195 // make sure the .lrs file exists
2196 if (ResType=rtLRS) and NewUnitInfo.IsVirtual then begin
2197 LRSFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lrs');
2198 CodeToolBoss.CreateFile(LRSFilename);
2199 end;
2200 if (NewUnitInfo.Component is TCustomForm)
2201 and NewFileDescriptor.UseCreateFormStatements
2202 and NewUnitInfo.IsPartOfProject
2203 and AProject.AutoCreateForms
2204 and (pfMainUnitHasCreateFormStatements in AProject.Flags) then
2205 begin
2206 AProject.AddCreateFormToProjectFile(NewUnitInfo.Component.ClassName,
2207 NewUnitInfo.Component.Name);
2208 end;
2209 end else begin
2210 // create a designer form for a form/datamodule/frame
2211 //DebugLn(['NewFile Name=',NewFileDescriptor.Name,' Class=',NewFileDescriptor.ClassName]);
2212 DisableAutoSize:=true;
2213 Result := CreateNewForm(NewUnitInfo, AncestorType, nil,
2214 NewFileDescriptor.UseCreateFormStatements,
2215 DisableAutoSize);
2216 if DisableAutoSize and (NewUnitInfo.Component<>nil)
2217 and (NewUnitInfo.Component is TControl) then
2218 begin
2219 // disable autosizing for docked form editor forms, see issue #32207
2220 PreventAutoSize := (IDETabMaster <> nil)
2221 and (NewUnitInfo.Component is TCustomDesignControl)
2222 and IDETabMaster.AutoSizeInShowDesigner(TControl(NewUnitInfo.Component));
2223 if not PreventAutoSize then
2224 TControl(NewUnitInfo.Component).EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster Delayed'){$ENDIF};
2225 end;
2226 end;
2227 if Result<>mrOk then
2228 begin
2229 debugln(['NewFile create designer form failed ',NewUnitInfo.Filename]);
2230 exit;
2231 end;
2232 end;
2233
2234 // show form and select form
2235 if NewUnitInfo.Component<>nil then begin
2236 // show form
2237 MainIDE.DoShowDesignerFormOfCurrentSrc(False);
2238 end else begin
2239 MainIDE.DisplayState:= dsSource;
2240 end;
2241 end else begin
2242 // do not open in editor
2243 end;
2244
2245 // Update HasResources property (if the .lfm file was created separately)
2246 if (not NewUnitInfo.HasResources)
2247 and FilenameIsPascalUnit(NewUnitInfo.Filename) then begin
2248 //debugln('NewFile no HasResources ',NewUnitInfo.Filename);
2249 LFMFilename:=ChangeFileExt(NewUnitInfo.Filename,'.lfm');
2250 SearchFlags:=[];
2251 if NewUnitInfo.IsPartOfProject then
2252 Include(SearchFlags,pfsfOnlyProjectFiles);
2253 if NewUnitInfo.IsVirtual then
2254 Include(SearchFlags,pfsfOnlyVirtualFiles);
2255 if (AProject.UnitInfoWithFilename(LFMFilename,SearchFlags)<>nil) then begin
2256 //debugln('NewFile no HasResources ',NewUnitInfo.Filename,' ResourceFile exists');
2257 NewUnitInfo.HasResources:=true;
2258 end;
2259 end;
2260
2261 if (nfAskForFilename in NewFlags) then begin
2262 // save and ask for filename
2263 NewUnitInfo.Modified:=true;
2264 Result:=SaveEditorFile(NewSrcEdit,[sfCheckAmbiguousFiles,sfSaveAs]);
2265 if Result<>mrOk then
2266 begin
2267 debugln(['NewFile SaveEditorFile failed ',NewFilename]);
2268 exit;
2269 end;
2270 end else if nfSave in NewFlags then begin
2271 if (nfOpenInEditor in NewFlags) or NewBuffer.IsVirtual then begin
2272 // save and ask for filename if needed
2273 NewUnitInfo.Modified:=true;
2274 Result:=SaveEditorFile(NewSrcEdit,[sfCheckAmbiguousFiles]);
2275 if Result<>mrOk then
2276 begin
2277 debugln(['NewFile SaveEditorFile SaveAs failed ',NewFilename]);
2278 exit;
2279 end;
2280 end else begin
2281 // save quietly
2282 NewBuffer.Save;
2283 end;
2284 end;
2285
2286 Result:=mrOk;
2287 //DebugLn('NewFile END ',NewUnitInfo.Filename);
2288 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('NewUnit end');{$ENDIF}
2289 end;
2290
NewOthernull2291 function NewOther: TModalResult;
2292 var
2293 NewIDEItem: TNewIDEItemTemplate;
2294 NewProjFile: TNewItemProjectFile;
2295 begin
2296 Result:=ShowNewIDEItemDialog(NewIDEItem);
2297 if Result<>mrOk then exit;
2298 if NewIDEItem is TNewItemProjectFile then begin
2299 // file
2300 NewProjFile:=TNewItemProjectFile(NewIDEItem);
2301 if NewProjFile.Descriptor<>nil then
2302 NewProjFile.Descriptor.Owner:=Project1;
2303 try
2304 Result:=MainIDE.DoNewEditorFile(NewProjFile.Descriptor,
2305 '','',[nfOpenInEditor,nfCreateDefaultSrc]);
2306 finally
2307 if NewProjFile.Descriptor<>nil then
2308 NewProjFile.Descriptor.Owner:=nil;
2309 end;
2310 end else if NewIDEItem is TNewItemProject then // project
2311 Result:=MainIDE.DoNewProject(TNewItemProject(NewIDEItem).Descriptor)
2312 else if NewIDEItem is TNewItemPackage then // packages
2313 PkgBoss.DoNewPackage
2314 else
2315 IDEMessageDialog(ueNotImplCap, lisSorryThisTypeIsNotYetImplemented, mtInformation,[mbOk]);
2316 end;
2317
NewUnitOrFormnull2318 function NewUnitOrForm(Template: TNewIDEItemTemplate;
2319 DefaultDesc: TProjectFileDescriptor): TModalResult;
2320 var
2321 Desc: TProjectFileDescriptor;
2322 Flags: TNewFlags;
2323 begin
2324 if (Template is TNewItemProjectFile) and Template.VisibleInNewDialog then
2325 Desc:=TNewItemProjectFile(Template).Descriptor
2326 else
2327 Desc:=DefaultDesc;
2328 Flags:=[nfOpenInEditor,nfCreateDefaultSrc];
2329 if (not Project1.IsVirtual) and EnvironmentOptions.AskForFilenameOnNewFile then
2330 Flags:=Flags+[nfAskForFilename,nfSave];
2331 Desc.Owner:=Project1;
2332 try
2333 Result := MainIDE.DoNewEditorFile(Desc,'','',Flags);
2334 finally
2335 Desc.Owner:=nil;
2336 end;
2337 end;
2338
2339 procedure CreateFileDialogFilterForSourceEditorFiles(Filter: string;
2340 out AllEditorMask, AllMask: string);
2341 // Filter: a TFileDialog filter, e.g. Pascal|*.pas;*.pp|Text|*.txt
2342 // AllEditorExt: a mask for all open files in the source editor, that are not
2343 // in Filter, e.g. '*.txt;*.xml'
2344 // AllFilter: all masks of Filter and AllEditorExt, e.g. '*.pas;*.pp;*.inc'
2345 var
2346 i: Integer;
2347 SrcEdit: TSourceEditor;
2348 Ext: String;
2349 begin
2350 AllMask:='|'+TFileDialog.ExtractAllFilterMasks(Filter);
2351 AllEditorMask:='|';
2352 for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin
2353 SrcEdit:=SourceEditorManager.SourceEditors[i];
2354 Ext:=ExtractFileExt(SrcEdit.FileName);
2355 if Ext<>'' then begin
2356 Ext:='*'+Ext;
2357 if (TFileDialog.FindMaskInFilter(AllMask,Ext)>0)
2358 or (TFileDialog.FindMaskInFilter(AllEditorMask,Ext)>0) then continue;
2359 if AllEditorMask<>'|' then
2360 AllEditorMask:=AllEditorMask+';';
2361 AllEditorMask:=AllEditorMask+Ext;
2362 end;
2363 end;
2364 System.Delete(AllMask,1,1);
2365 System.Delete(AllEditorMask,1,1);
2366 if AllEditorMask<>'' then begin
2367 if AllMask<>'' then
2368 AllMask:=AllMask+';';
2369 AllMask:=AllMask+AllEditorMask;
2370 end;
2371 end;
2372
SaveEditorFilenull2373 function SaveEditorFile(AEditor: TSourceEditorInterface; Flags: TSaveFlags): TModalResult;
2374 var
2375 AnUnitInfo, MainUnitInfo: TUnitInfo;
2376 TestFilename, DestFilename: string;
2377 LRSCode, LFMCode: TCodeBuffer;
2378 OldUnitName, OldFilename: String;
2379 NewUnitName, NewFilename: String;
2380 WasVirtual, WasPascalSource, CanAbort, Confirm: Boolean;
2381 SaveProjectFlags: TSaveFlags;
2382 EMacro: TEditorMacro;
2383 begin
2384 Result:=mrCancel;
2385 CanAbort:=[sfCanAbort,sfProjectSaving]*Flags<>[];
2386 //debugln('SaveEditorFile A PageIndex=',PageIndex,' Flags=',SaveFlagsToString(Flags));
2387 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('SaveEditorFile A');{$ENDIF}
2388 if not (MainIDE.ToolStatus in [itNone,itDebugger]) then
2389 exit(mrAbort);
2390 if AEditor=nil then exit(mrCancel);
2391 AnUnitInfo := Project1.UnitWithEditorComponent(AEditor);
2392 if AnUnitInfo=nil then exit(mrCancel);
2393
2394 // do not save a unit which is currently reverting
2395 if AnUnitInfo.IsReverting then
2396 exit(mrOk);
2397
2398 WasVirtual:=AnUnitInfo.IsVirtual;
2399 WasPascalSource:=FilenameIsPascalSource(AnUnitInfo.Filename);
2400
2401 // if this file is part of a virtual project then save the project first
2402 if (not (sfProjectSaving in Flags)) and Project1.IsVirtual and AnUnitInfo.IsPartOfProject then
2403 begin
2404 SaveProjectFlags:=Flags*[sfSaveToTestDir];
2405 if AnUnitInfo=Project1.MainUnitInfo then
2406 Include(SaveProjectFlags,sfSaveMainSourceAs);
2407 Result:=SaveProject(SaveProjectFlags);
2408 exit;
2409 end;
2410
2411 // update codetools cache and collect Modified flags
2412 if not (sfProjectSaving in Flags) then
2413 SaveEditorChangesToCodeCache(nil);
2414
2415 if (uifInternalFile in AnUnitInfo.Flags) then
2416 begin
2417 if (copy(AnUnitInfo.Filename, 1, length(EditorMacroVirtualDrive)) = EditorMacroVirtualDrive)
2418 then begin
2419 // save to macros
2420 EMacro := MacroListViewer.MacroByFullName(AnUnitInfo.Filename);
2421 if EMacro <> nil then begin
2422 EMacro.SetFromSource(AEditor.SourceText);
2423 if EMacro.IsInvalid and (EMacro.ErrorMsg <> '') then
2424 IDEMessagesWindow.AddCustomMessage(mluError,EMacro.ErrorMsg);
2425 end;
2426 MacroListViewer.UpdateDisplay;
2427 AnUnitInfo.ClearModifieds;
2428 AEditor.Modified:=false;
2429 end;
2430 // otherwise unknown internal file => skip
2431 exit(mrOk);
2432 end;
2433
2434 // if this is a new unit then a simple Save becomes a SaveAs
2435 if (not (sfSaveToTestDir in Flags)) and (AnUnitInfo.IsVirtual) then
2436 Include(Flags,sfSaveAs);
2437
2438 // if this is the main source and has the same name as the lpi
2439 // rename the project
2440 // Note:
2441 // Changing the main source file without the .lpi is possible only by
2442 // manually editing the lpi file, because this is only needed in
2443 // special cases (rare functions don't need front ends).
2444 MainUnitInfo:=AnUnitInfo.Project.MainUnitInfo;
2445 if (sfSaveAs in Flags) and (not (sfProjectSaving in Flags)) and (AnUnitInfo=MainUnitInfo)
2446 then begin
2447 Result:=SaveProject([sfSaveAs,sfSaveMainSourceAs]);
2448 exit;
2449 end;
2450
2451 // if nothing modified then a simple Save can be skipped
2452 //debugln(['SaveEditorFile A ',AnUnitInfo.Filename,' ',AnUnitInfo.NeedsSaveToDisk]);
2453 if ([sfSaveToTestDir,sfSaveAs]*Flags=[]) and (not AnUnitInfo.NeedsSaveToDisk) then
2454 begin
2455 if AEditor.Modified then
2456 begin
2457 AnUnitInfo.SessionModified:=true;
2458 AEditor.Modified:=false;
2459 end;
2460 exit(mrOk);
2461 end;
2462
2463 // check if file is writable on disk
2464 if (not AnUnitInfo.IsVirtual) and FileExistsUTF8(AnUnitInfo.Filename) then
2465 AnUnitInfo.FileReadOnly:=not FileIsWritable(AnUnitInfo.Filename)
2466 else
2467 AnUnitInfo.FileReadOnly:=false;
2468
2469 // if file is readonly then a simple Save is skipped
2470 if (AnUnitInfo.ReadOnly) and ([sfSaveToTestDir,sfSaveAs]*Flags=[]) then
2471 exit(mrOk);
2472
2473 // load old resource file
2474 LFMCode:=nil;
2475 LRSCode:=nil;
2476 if WasPascalSource then
2477 begin
2478 Result:=LoadResourceFile(AnUnitInfo,LFMCode,LRSCode,true,CanAbort);
2479 if not (Result in [mrIgnore,mrOk]) then
2480 exit;
2481 end;
2482
2483 OldUnitName:='';
2484 if WasPascalSource then
2485 OldUnitName:=AnUnitInfo.ReadUnitNameFromSource(true);
2486 OldFilename:=AnUnitInfo.Filename;
2487
2488 if [sfSaveAs,sfSaveToTestDir]*Flags=[sfSaveAs] then begin
2489 // let user choose a filename
2490 NewFilename:=OldFilename;
2491 Result:=ShowSaveFileAsDialog(NewFilename,AnUnitInfo,LFMCode,LRSCode,CanAbort);
2492 if not (Result in [mrIgnore,mrOk]) then
2493 exit;
2494 end;
2495
2496 // save source
2497
2498 // a) do before save events
2499 if EditorOpts.AutoRemoveEmptyMethods and (AnUnitInfo.Component<>nil) then begin
2500 // Note: When removing published methods, the source, the lfm, the lrs
2501 // and the form must be changed. At the moment editing the lfm without
2502 // the component is not yet implemented.
2503 Result:=RemoveEmptyMethods(AnUnitInfo.Source,
2504 AnUnitInfo.Component.ClassName,0,0,false,[pcsPublished]);
2505 if Result=mrAbort then exit;
2506 end;
2507
2508 // b) do actual save
2509 DestFilename := '';
2510 if (sfSaveToTestDir in Flags) or AnUnitInfo.IsVirtual then
2511 begin
2512 // save source to test directory
2513 TestFilename := MainBuildBoss.GetTestUnitFilename(AnUnitInfo);
2514 if TestFilename <> '' then
2515 begin
2516 DestFilename := TestFilename;
2517 // notify packages
2518 Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,AnUnitInfo,
2519 sefsBeforeWrite,DestFilename);
2520 if Result<>mrOk then exit;
2521 // actual write
2522 //DebugLn(['SaveEditorFile TestFilename="',TestFilename,'" Size=',AnUnitInfo.Source.SourceLength]);
2523 Result := AnUnitInfo.WriteUnitSourceToFile(DestFilename);
2524 if Result <> mrOk then
2525 Exit;
2526 // notify packages
2527 Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,AnUnitInfo,
2528 sefsAfterWrite,DestFilename);
2529 if Result<>mrOk then exit;
2530 end
2531 else
2532 exit(mrCancel);
2533 end else
2534 begin
2535 if AnUnitInfo.Modified or (MainIDE.CheckFilesOnDiskEnabled and AnUnitInfo.NeedsSaveToDisk) then
2536 begin
2537 // save source to file
2538 DestFilename := AnUnitInfo.Filename;
2539 // notify packages
2540 Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,
2541 AnUnitInfo,sefsBeforeWrite,DestFilename);
2542 if Result<>mrOk then exit;
2543 // actual write
2544 Result := AnUnitInfo.WriteUnitSource;
2545 if Result <> mrOK then
2546 exit;
2547 // notify packages
2548 Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,
2549 AnUnitInfo,sefsAfterWrite,DestFilename);
2550 if Result<>mrOk then exit;
2551 end;
2552 end;
2553
2554 if sfCheckAmbiguousFiles in Flags then
2555 MainBuildBoss.CheckAmbiguousSources(DestFilename,false);
2556
2557 {$IFDEF IDE_DEBUG}
2558 debugln(['*** HasResources=',AnUnitInfo.HasResources]);
2559 {$ENDIF}
2560 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('SaveEditorFile B');{$ENDIF}
2561 // save resource file and lfm file
2562 if (LRSCode<>nil) or (AnUnitInfo.Component<>nil) then begin
2563 Result:=SaveUnitComponent(AnUnitInfo,LRSCode,LFMCode,Flags);
2564 if not (Result in [mrIgnore, mrOk]) then
2565 exit;
2566 end;
2567
2568 // unset all modified flags
2569 if not (sfSaveToTestDir in Flags) then begin
2570 AnUnitInfo.ClearModifieds;
2571 AEditor.Modified:=false;
2572 MainIDE.UpdateSaveMenuItemsAndButtons(not (sfProjectSaving in Flags));
2573 end;
2574 TSourceEditor(AEditor).SourceNotebook.UpdateStatusBar;
2575
2576 // fix all references
2577 NewUnitName:='';
2578 if FilenameIsPascalSource(AnUnitInfo.Filename) then
2579 NewUnitName:=AnUnitInfo.ReadUnitNameFromSource(true);
2580 NewFilename:=AnUnitInfo.Filename;
2581 if (NewUnitName<>'')
2582 and ((OldUnitName<>NewUnitName) or (CompareFilenames(OldFilename,NewFilename)<>0))
2583 then begin
2584 if EnvironmentOptions.UnitRenameReferencesAction<>urraNever then
2585 begin
2586 // silently update references of new units (references were auto created
2587 // and keeping old references makes no sense)
2588 Confirm:=(EnvironmentOptions.UnitRenameReferencesAction=urraAsk)
2589 and (not WasVirtual);
2590 Result:=ReplaceUnitUse(OldFilename,OldUnitName,NewFilename,NewUnitName,
2591 true,true,Confirm);
2592 if Result<>mrOk then exit;
2593 end;
2594 end;
2595
2596 {$IFDEF IDE_VERBOSE}
2597 debugln(['SaveEditorFile END ',NewFilename,' AnUnitInfo.Modified=',AnUnitInfo.Modified,' AEditor.Modified=',AEditor.Modified]);
2598 {$ENDIF}
2599 Result:=mrOk;
2600 end;
2601
2602 function SaveEditorFile(const Filename: string; Flags: TSaveFlags): TModalResult;
2603 var
2604 UnitIndex: Integer;
2605 AnUnitInfo: TUnitInfo;
2606 i: Integer;
2607 begin
2608 Result:=mrOk;
2609 if Filename='' then exit;
2610 UnitIndex:=Project1.IndexOfFilename(TrimFilename(Filename),[pfsfOnlyEditorFiles]);
2611 if UnitIndex<0 then exit;
2612 AnUnitInfo:=Project1.Units[UnitIndex];
2613 for i := 0 to AnUnitInfo.OpenEditorInfoCount-1 do begin
2614 Result:=SaveEditorFile(AnUnitInfo.OpenEditorInfo[i].EditorComponent, Flags);
2615 if Result <> mrOK then Break;
2616 end;
2617 end;
2618
2619 function CloseEditorFile(AEditor: TSourceEditorInterface; Flags: TCloseFlags): TModalResult;
2620 var
2621 AnUnitInfo: TUnitInfo;
2622 ACaption, AText: string;
2623 i: integer;
2624 AnEditorInfo: TUnitEditorInfo;
2625 SrcEditWasFocused: Boolean;
2626 SrcEdit: TSourceEditor;
2627 begin
2628 {$IFDEF IDE_DEBUG}
2629 //debugln('CloseEditorFile A PageIndex=',IntToStr(AnUnitInfo.PageIndex));
2630 {$ENDIF}
2631 Result:=mrCancel;
2632 if AEditor = nil then exit;
2633 AnEditorInfo := Project1.EditorInfoWithEditorComponent(AEditor);
2634 //AnUnitInfo := Project1.UnitWithEditorComponent(AEditor);
2635 if AnEditorInfo = nil then begin
2636 // we need to close the page anyway or else we might enter a loop
2637 DebugLn('CloseEditorFile INCONSISTENCY: NO AnUnitInfo');
2638 SourceEditorManager.CloseFile(AEditor);
2639 Result:=mrOk;
2640 exit;
2641 end;
2642 AnUnitInfo := AnEditorInfo.UnitInfo;
2643 AnUnitInfo.SessionModified:=true;
2644 SrcEditWasFocused:=(AnEditorInfo.EditorComponent<>nil)
2645 and (AnEditorInfo.EditorComponent.EditorControl<>nil)
2646 and AnEditorInfo.EditorComponent.EditorControl.Focused;
2647 //debugln(['CloseEditorFile File=',AnUnitInfo.Filename,' WasFocused=',SrcEditWasFocused]);
2648 try
2649 //debugln(['CloseEditorFile File=',AnUnitInfo.Filename,' UnitSession=',AnUnitInfo.SessionModified,' ProjSession=',project1.SessionModified]);
2650 if AnUnitInfo.OpenEditorInfoCount > 1 then begin
2651 // opened multiple times => close one instance
2652 SourceEditorManager.CloseFile(AEditor);
2653 Result:=mrOk;
2654 exit;
2655 end;
2656
2657 if (AnUnitInfo.Component<>nil) and (MainIDE.LastFormActivated<>nil)
2658 and (MainIDE.LastFormActivated.Designer.LookupRoot=AnUnitInfo.Component) then
2659 MainIDE.LastFormActivated:=nil;
2660
2661 // save some meta data of the source
2662 SaveSrcEditorProjectSpecificSettings(AnEditorInfo);
2663
2664 // if SaveFirst then save the source
2665 if (cfSaveFirst in Flags) and (not AnUnitInfo.ReadOnly)
2666 and ((AEditor.Modified) or (AnUnitInfo.Modified)) then begin
2667 if not (cfQuiet in Flags) then begin
2668 // ask user
2669 if AnUnitInfo.Filename<>'' then
2670 AText:=Format(lisFileHasChangedSave, [AnUnitInfo.Filename])
2671 else if AnUnitInfo.Unit_Name<>'' then
2672 AText:=Format(lisUnitHasChangedSave, [AnUnitInfo.Unit_Name])
2673 else
2674 AText:=Format(lisSourceOfPageHasChangedSave, [TSourceEditor(AEditor).PageName]);
2675 ACaption:=lisSourceModified;
2676 Result:=IDEQuestionDialog(ACaption, AText,
2677 mtConfirmation, [mrYes, lisMenuSave,
2678 mrNo, lisDiscardChanges,
2679 mrAbort]);
2680 end else
2681 Result:=mrYes;
2682 if Result=mrYes then begin
2683 Result:=SaveEditorFile(AnEditorInfo.EditorComponent,[sfCheckAmbiguousFiles]);
2684 end;
2685 if Result in [mrAbort,mrCancel] then exit;
2686 Result:=mrOk;
2687 end;
2688 // mark file as unmodified
2689 if (AnUnitInfo.Source<>nil) and AnUnitInfo.Source.Modified then
2690 AnUnitInfo.Source.Clear;
2691
2692 // add to recent file list
2693 if (not AnUnitInfo.IsVirtual) and (not (cfProjectClosing in Flags)) then
2694 begin
2695 EnvironmentOptions.AddToRecentOpenFiles(AnUnitInfo.Filename);
2696 MainIDE.SetRecentFilesMenu;
2697 end;
2698
2699 // close form soft (keep it if used by another component)
2700 CloseUnitComponent(AnUnitInfo,[]);
2701
2702 // close source editor
2703 SourceEditorManager.CloseFile(AnEditorInfo.EditorComponent);
2704 MainIDEBar.itmFileClose.Enabled:=SourceEditorManager.SourceEditorCount > 0;
2705 MainIDEBar.itmFileCloseAll.Enabled:=MainIDEBar.itmFileClose.Enabled;
2706
2707 // free sources, forget changes
2708 if (AnUnitInfo.Source<>nil) then begin
2709 if (Project1.MainUnitInfo=AnUnitInfo)
2710 and (not (cfProjectClosing in Flags)) then begin
2711 AnUnitInfo.Source.Revert;
2712 end else begin
2713 AnUnitInfo.Source.IsDeleted:=true;
2714 end;
2715 end;
2716
2717 // close file in project
2718 AnUnitInfo.Loaded:=false;
2719 if AnUnitInfo<>Project1.MainUnitInfo then
2720 AnUnitInfo.Source:=nil;
2721 if not (cfProjectClosing in Flags) then begin
2722 i:=Project1.IndexOf(AnUnitInfo);
2723 if (i<>Project1.MainUnitID) and AnUnitInfo.IsVirtual then begin
2724 Project1.RemoveUnit(i);
2725 end;
2726 end;
2727
2728 finally
2729 if SrcEditWasFocused then begin
2730 // before closing the syendit was focused. Focus the current synedit.
2731 SrcEdit := SourceEditorManager.ActiveEditor;
2732 if (SrcEdit<>nil)
2733 and (SrcEdit.EditorControl<>nil)
2734 and (SrcEdit.EditorControl.CanFocus) then
2735 SrcEdit.EditorControl.SetFocus;
2736 //debugln(['CloseEditorFile Focus=',SrcEdit.FileName,' Editor=',DbgSName(SrcEdit.EditorControl),' Focused=',(SrcEdit.EditorControl<>nil) and (SrcEdit.EditorControl.Focused)]);
2737 end;
2738 end;
2739 {$IFDEF IDE_DEBUG}
2740 DebugLn('CloseEditorFile end');
2741 {$ENDIF}
2742 Result:=mrOk;
2743 end;
2744
2745 function CloseEditorFile(const Filename: string; Flags: TCloseFlags): TModalResult;
2746 var
2747 UnitIndex: Integer;
2748 AnUnitInfo: TUnitInfo;
2749 begin
2750 Result:=mrOk;
2751 if Filename='' then exit;
2752 UnitIndex:=Project1.IndexOfFilename(TrimFilename(Filename),[pfsfOnlyEditorFiles]);
2753 if UnitIndex<0 then exit;
2754 AnUnitInfo:=Project1.Units[UnitIndex];
2755 while (AnUnitInfo.OpenEditorInfoCount > 0) and (Result = mrOK) do
2756 Result:=CloseEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent, Flags);
2757 end;
2758
2759 function FindUnitFileImpl(const AFilename: string; TheOwner: TObject;
2760 Flags: TFindUnitFileFlags): string;
2761
2762 function FindInBaseIDE: string;
2763 var
2764 AnUnitName: String;
2765 BaseDir: String;
2766 UnitInFilename: String;
2767 begin
2768 AnUnitName:=ExtractFileNameOnly(AFilename);
2769 BaseDir:=EnvironmentOptions.GetParsedLazarusDirectory+PathDelim+'ide';
2770 UnitInFilename:='';
2771 Result:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
2772 BaseDir,AnUnitName,UnitInFilename,true);
2773 end;
2774
2775 function FindInProject(AProject: TProject): string;
2776 var
2777 AnUnitInfo: TUnitInfo;
2778 AnUnitName: String;
2779 BaseDir: String;
2780 UnitInFilename: String;
2781 begin
2782 // search in virtual (unsaved) files
2783 AnUnitInfo:=AProject.UnitInfoWithFilename(AFilename,
2784 [pfsfOnlyProjectFiles,pfsfOnlyVirtualFiles]);
2785 if AnUnitInfo<>nil then begin
2786 Result:=AnUnitInfo.Filename;
2787 exit;
2788 end;
2789
2790 // search in search path of project
2791 AnUnitName:=ExtractFileNameOnly(AFilename);
2792 BaseDir:=AProject.Directory;
2793 UnitInFilename:='';
2794 Result:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
2795 BaseDir,AnUnitName,UnitInFilename,true);
2796 end;
2797
2798 function FindInPackage(APackage: TLazPackage): string;
2799 var
2800 BaseDir: String;
2801 AnUnitName: String;
2802 UnitInFilename: String;
2803 begin
2804 Result:='';
2805 BaseDir:=APackage.Directory;
2806 if not FilenameIsAbsolute(BaseDir) then exit;
2807 // search in search path of package
2808 AnUnitName:=ExtractFileNameOnly(AFilename);
2809 UnitInFilename:='';
2810 Result:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
2811 BaseDir,AnUnitName,UnitInFilename,true);
2812 end;
2813
2814 var
2815 AProject: TProject;
2816 i: Integer;
2817 begin
2818 if FilenameIsAbsolute(AFilename) then begin
2819 Result:=AFilename;
2820 exit;
2821 end;
2822 Result:='';
2823
2824 // project
2825 AProject:=nil;
2826 if TheOwner=nil then begin
2827 AProject:=Project1;
2828 end else if (TheOwner is TProject) then
2829 AProject:=TProject(TheOwner);
2830
2831 if AProject<>nil then
2832 begin
2833 Result:=FindInProject(AProject);
2834 if Result<>'' then exit;
2835 end;
2836
2837 // package
2838 if TheOwner is TLazPackage then begin
2839 Result:=FindInPackage(TLazPackage(TheOwner));
2840 if Result<>'' then exit;
2841 end;
2842
2843 if TheOwner=LazarusIDE then begin
2844 // search in base IDE
2845 Result:=FindInBaseIDE;
2846 if Result<>'' then exit;
2847
2848 // search in installed packages
2849 for i:=0 to PackageGraph.Count-1 do
2850 if (PackageGraph[i].Installed<>pitNope)
2851 and ((not (fuffIgnoreUninstallPackages in Flags))
2852 or (PackageGraph[i].AutoInstall<>pitNope))
2853 then begin
2854 Result:=FindInPackage(PackageGraph[i]);
2855 if Result<>'' then exit;
2856 end;
2857 // search in auto install packages
2858 for i:=0 to PackageGraph.Count-1 do
2859 if (PackageGraph[i].Installed=pitNope)
2860 and (PackageGraph[i].AutoInstall<>pitNope) then begin
2861 Result:=FindInPackage(PackageGraph[i]);
2862 if Result<>'' then exit;
2863 end;
2864 // then search in all other open packages
2865 for i:=0 to PackageGraph.Count-1 do
2866 if (PackageGraph[i].Installed=pitNope)
2867 and (PackageGraph[i].AutoInstall=pitNope) then begin
2868 Result:=FindInPackage(PackageGraph[i]);
2869 if Result<>'' then exit;
2870 end;
2871 end;
2872 Result:='';
2873 end;
2874
2875 function FindSourceFileImpl(const AFilename, BaseDirectory: string;
2876 Flags: TFindSourceFlags): string;
2877 // AFilename can be an absolute or relative filename, of a source file or a
2878 // compiled unit (.ppu).
2879 // Find the source filename (pascal source or include file) and returns
2880 // the absolute path.
2881 // With fsfMapTempToVirtualFiles files in the temp directory are stripped off
2882 // the temporary files resulting in the virtual file name of the CodeTools.
2883 //
2884 // First it searches in the current projects src path, then its unit path, then
2885 // its include path. Then all used package source directories are searched.
2886 // Finally the fpc sources are searched.
2887 var
2888 CompiledSrcExt: String;
2889 BaseDir: String;
2890 AlreadySearchedPaths: string;
2891 StartUnitPath: String;
2892
2893 procedure MarkPathAsSearched(const AddSearchPath: string);
2894 begin
2895 AlreadySearchedPaths:=MergeSearchPaths(AlreadySearchedPaths,AddSearchPath);
2896 end;
2897
2898 function SearchIndirectIncludeFile: string;
2899 var
2900 UnitPath: String;
2901 CurDir: String;
2902 AlreadySearchedUnitDirs: String;
2903 CompiledUnitPath: String;
2904 AllSrcPaths: String;
2905 CurSrcPath: String;
2906 CurIncPath: String;
2907 PathPos: Integer;
2908 AllIncPaths: String;
2909 SearchPath: String;
2910 SearchFile: String;
2911 begin
2912 if CompiledSrcExt='' then exit('');
2913 // get unit path for compiled units
2914 UnitPath:=BaseDir+';'+StartUnitPath;
2915 UnitPath:=TrimSearchPath(UnitPath,BaseDir);
2916
2917 // Extract all directories with compiled units
2918 CompiledUnitPath:='';
2919 AlreadySearchedUnitDirs:='';
2920 PathPos:=1;
2921 while PathPos<=length(UnitPath) do begin
2922 CurDir:=GetNextDirectoryInSearchPath(UnitPath,PathPos);
2923 // check if directory is already tested
2924 if SearchDirectoryInSearchPath(AlreadySearchedUnitDirs,CurDir,1)>0 then
2925 continue;
2926 AlreadySearchedUnitDirs:=MergeSearchPaths(AlreadySearchedUnitDirs,CurDir);
2927 // check if directory contains a compiled unit
2928 if FindFirstFileWithExt(CurDir,CompiledSrcExt)<>'' then
2929 CompiledUnitPath:=CompiledUnitPath+';'+CurDir;
2930 end;
2931 {$IFDEF VerboseFindSourceFile}
2932 debugln(['SearchIndirectIncludeFile CompiledUnitPath="',CompiledUnitPath,'"']);
2933 {$ENDIF}
2934
2935 // collect all src paths for the compiled units
2936 AllSrcPaths:=CompiledUnitPath;
2937 PathPos:=1;
2938 while PathPos<=length(CompiledUnitPath) do begin
2939 CurDir:=GetNextDirectoryInSearchPath(CompiledUnitPath,PathPos);
2940 CurSrcPath:=CodeToolBoss.GetCompiledSrcPathForDirectory(CurDir);
2941 CurSrcPath:=TrimSearchPath(CurSrcPath,CurDir);
2942 AllSrcPaths:=MergeSearchPaths(AllSrcPaths,CurSrcPath);
2943 end;
2944 {$IFDEF VerboseFindSourceFile}
2945 debugln(['SearchIndirectIncludeFile AllSrcPaths="',AllSrcPaths,'"']);
2946 {$ENDIF}
2947
2948 // add fpc src directories
2949 // ToDo
2950
2951 // collect all include paths
2952 AllIncPaths:=AllSrcPaths;
2953 PathPos:=1;
2954 while PathPos<=length(AllSrcPaths) do begin
2955 CurDir:=GetNextDirectoryInSearchPath(AllSrcPaths,PathPos);
2956 CurIncPath:=CodeToolBoss.GetIncludePathForDirectory(CurDir);
2957 CurIncPath:=TrimSearchPath(CurIncPath,CurDir);
2958 AllIncPaths:=MergeSearchPaths(AllIncPaths,CurIncPath);
2959 end;
2960 {$IFDEF VerboseFindSourceFile}
2961 debugln(['SearchIndirectIncludeFile AllIncPaths="',AllIncPaths,'"']);
2962 {$ENDIF}
2963
2964 SearchFile:=AFilename;
2965 SearchPath:=AllIncPaths;
2966 Result:=FileUtil.SearchFileInPath(SearchFile,BaseDir,SearchPath,';',[]);
2967 {$IFDEF VerboseFindSourceFile}
2968 debugln(['SearchIndirectIncludeFile Result="',Result,'"']);
2969 {$ENDIF}
2970 MarkPathAsSearched(SearchPath);
2971 end;
2972
2973 function SearchInPath(const TheSearchPath, SearchFile: string;
2974 var Filename: string): boolean;
2975 var
2976 SearchPath: String;
2977 begin
2978 Filename:='';
2979 SearchPath:=RemoveSearchPaths(TheSearchPath,AlreadySearchedPaths);
2980 if SearchPath<>'' then begin
2981 Filename:=FileUtil.SearchFileInPath(SearchFile,BaseDir,SearchPath,';',[]);
2982 {$IFDEF VerboseFindSourceFile}
2983 debugln(['FindSourceFile trying "',SearchPath,'" Filename="',Filename,'"']);
2984 {$ENDIF}
2985 MarkPathAsSearched(SearchPath);
2986 end;
2987 Result:=Filename<>'';
2988 end;
2989
2990 var
2991 SearchPath: String;
2992 SearchFile: String;
2993 ProjFile: TLazProjectFile;
2994 begin
2995 {$IFDEF VerboseFindSourceFile}
2996 debugln(['FindSourceFile Filename="',AFilename,'" BaseDirectory="',BaseDirectory,'"']);
2997 {$ENDIF}
2998 if AFilename='' then exit('');
2999
3000 if fsfMapTempToVirtualFiles in Flags then
3001 begin
3002 BaseDir:=MainBuildBoss.GetTestBuildDirectory;
3003 if FilenameIsAbsolute(AFilename)
3004 and FileIsInPath(AFilename,BaseDir) then
3005 begin
3006 Result:=CreateRelativePath(AFilename,BaseDir);
3007 if (Project1<>nil) and (Project1.UnitInfoWithFilename(Result)<>nil) then
3008 exit;
3009 end;
3010 end;
3011
3012 if FilenameIsAbsolute(AFilename) then
3013 begin
3014 Result := AFilename;
3015 if not FileExistsCached(Result) then
3016 Result := '';
3017 Exit;
3018 end;
3019
3020 AlreadySearchedPaths:='';
3021 BaseDir:=BaseDirectory;
3022 GlobalMacroList.SubstituteStr(BaseDir);
3023 BaseDir:=AppendPathDelim(TrimFilename(BaseDir));
3024
3025 // search file in base directory
3026 if FilenameIsAbsolute(BaseDir) then begin
3027 Result:=TrimFilename(BaseDir+AFilename);
3028 {$IFDEF VerboseFindSourceFile}
3029 debugln(['FindSourceFile trying Base "',Result,'"']);
3030 {$ENDIF}
3031 if FileExistsCached(Result) then exit;
3032 MarkPathAsSearched(BaseDir);
3033 end else if Project1<>nil then begin
3034 // search in virtual files
3035 Result:=TrimFilename(BaseDir+AFilename);
3036 ProjFile:=Project1.FindFile(Result,[pfsfOnlyVirtualFiles]);
3037 if ProjFile<>nil then
3038 exit;
3039 end;
3040
3041 // search file in debug path
3042 if (fsfUseDebugPath in Flags) and (Project1<>nil) then begin
3043 SearchPath:=EnvironmentOptions.GetParsedDebuggerSearchPath;
3044 SearchPath:=MergeSearchPaths(Project1.CompilerOptions.GetDebugPath(false),
3045 SearchPath);
3046 SearchPath:=TrimSearchPath(SearchPath,BaseDir);
3047 if SearchInPath(SearchPath,AFilename,Result) then exit;
3048 end;
3049
3050 CompiledSrcExt:=CodeToolBoss.GetCompiledSrcExtForDirectory(BaseDir);
3051 StartUnitPath:=CodeToolBoss.GetCompleteSrcPathForDirectory(BaseDir);
3052 StartUnitPath:=TrimSearchPath(StartUnitPath,BaseDir);
3053
3054 // if file is a pascal unit, search via unit and src paths
3055 if FilenameIsPascalUnit(AFilename) then begin
3056 // first search file in unit path
3057 if SearchInPath(StartUnitPath,AFilename,Result) then exit;
3058
3059 // search unit in fpc source directory
3060 Result:=CodeToolBoss.FindUnitInUnitSet(BaseDir,
3061 ExtractFilenameOnly(AFilename));
3062 {$IFDEF VerboseFindSourceFile}
3063 debugln(['FindSourceFile tried unitset Result=',Result]);
3064 {$ENDIF}
3065 if Result<>'' then exit;
3066 end;
3067
3068 if fsfUseIncludePaths in Flags then begin
3069 // search in include path
3070 if (fsfSearchForProject in Flags) then
3071 SearchPath:=Project1.CompilerOptions.GetIncludePath(false)
3072 else
3073 SearchPath:=CodeToolBoss.GetIncludePathForDirectory(BaseDir);
3074 SearchPath:=TrimSearchPath(SearchPath,BaseDir);
3075 if SearchInPath(StartUnitPath,AFilename,Result) then exit;
3076
3077 if not(fsfSkipPackages in Flags) then begin
3078 // search include file in source directories of all required packages
3079 SearchFile:=AFilename;
3080 Result:=PkgBoss.FindIncludeFileInProjectDependencies(Project1,SearchFile);
3081 {$IFDEF VerboseFindSourceFile}
3082 debugln(['FindSourceFile trying packages "',SearchPath,'" Result=',Result]);
3083 {$ENDIF}
3084 end;
3085 if Result<>'' then exit;
3086
3087 Result:=SearchIndirectIncludeFile;
3088 if Result<>'' then exit;
3089 end;
3090
3091 Result:='';
3092 end;
3093
3094 function FindUnitsOfOwnerImpl(TheOwner: TObject; Flags: TFindUnitsOfOwnerFlags): TStrings;
3095 var
3096 Files: TFilenameToStringTree;
3097 UnitPath: string; // only if not AddPackages:
3098 // owner unitpath without unitpaths of required packages
3099
3100 function Add(const aFilename: string): boolean;
3101 begin
3102 if Files.Contains(aFilename) then exit(false);
3103 //debugln([' Add ',aFilename]);
3104 Files[aFilename]:='';
3105 FindUnitsOfOwnerImpl.Add(aFilename);
3106 Result := True;
3107 end;
3108
3109 procedure AddListedPackageUnits(aPackage: TLazPackage);
3110 // add listed units of aPackage
3111 var
3112 i: Integer;
3113 PkgFile: TPkgFile;
3114 begin
3115 //debugln([' AddListedPackageUnits ',aPackage.IDAsString]);
3116 for i:=0 to aPackage.FileCount-1 do
3117 begin
3118 PkgFile:=aPackage.Files[i];
3119 if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
3120 if not PkgFile.InUses then continue;
3121 Add(PkgFile.Filename);
3122 end;
3123 end;
3124
3125 procedure AddUsedUnit(const aFilename: string);
3126 // add recursively all units
3127
3128 procedure AddUses(UsesSection: TStrings);
3129 var
3130 i: Integer;
3131 Code: TCodeBuffer;
3132 begin
3133 if UsesSection=nil then exit;
3134 for i:=0 to UsesSection.Count-1 do begin
3135 //debugln(['AddUses ',UsesSection[i]]);
3136 Code:=TCodeBuffer(UsesSection.Objects[i]);
3137 if Code=nil then exit;
3138 AddUsedUnit(Code.Filename);
3139 end;
3140 end;
3141
3142 var
3143 Code: TCodeBuffer;
3144 MainUsesSection, ImplementationUsesSection: TStrings;
3145 begin
3146 //debugln([' AddUsedUnit START ',aFilename]);
3147 if not (fuooPackages in Flags) then
3148 begin
3149 if FilenameIsAbsolute(aFilename) then
3150 begin
3151 if SearchDirectoryInSearchPath(UnitPath,ExtractFilePath(aFilename))<0 then
3152 exit; // not in exclusive unitpath
3153 end else begin
3154 if (not (TheOwner is TProject)) or (not TProject(TheOwner).IsVirtual) then
3155 exit;
3156 end;
3157 end;
3158 //debugln([' AddUsedUnit OK ',aFilename]);
3159 if not Add(aFilename) then exit;
3160 Code:=CodeToolBoss.LoadFile(aFilename,true,false);
3161 if Code=nil then exit;
3162 MainUsesSection:=nil;
3163 ImplementationUsesSection:=nil;
3164 try
3165 CodeToolBoss.FindUsedUnitFiles(Code,MainUsesSection,ImplementationUsesSection);
3166 AddUses(MainUsesSection);
3167 AddUses(ImplementationUsesSection);
3168 finally
3169 MainUsesSection.Free;
3170 ImplementationUsesSection.Free;
3171 end;
3172 end;
3173
3174 var
3175 aProject: TProject;
3176 aPackage, ReqPackage: TLazPackage;
3177 MainFile, CurFilename: String;
3178 AnUnitInfo: TUnitInfo;
3179 i: Integer;
3180 Code: TCodeBuffer;
3181 FoundInUnits, MissingUnits, NormalUnits: TStrings;
3182 PkgList: TFPList;
3183 PkgListFlags: TPkgIntfRequiredFlags;
3184 begin
3185 Result:=TStringList.Create;
3186 MainFile:='';
3187 FoundInUnits:=nil;
3188 MissingUnits:=nil;
3189 NormalUnits:=nil;
3190 aProject:=nil;
3191 aPackage:=nil;
3192 PkgList:=nil;
3193 Files:=TFilenameToStringTree.Create(false);
3194 try
3195 //debugln(['FindUnitsOfOwner ',DbgSName(TheOwner)]);
3196 if TheOwner is TProject then
3197 begin
3198 aProject:=TProject(TheOwner);
3199 // add main project source (e.g. .lpr)
3200 if (aProject.MainFile<>nil) and (pfMainUnitIsPascalSource in aProject.Flags)
3201 then begin
3202 MainFile:=aProject.MainFile.Filename;
3203 Add(MainFile);
3204 end;
3205 if (fuooListed in Flags) then begin
3206 // add listed units (i.e. units in project inspector)
3207 AnUnitInfo:=aProject.FirstPartOfProject;
3208 while AnUnitInfo<>nil do
3209 begin
3210 if FilenameIsPascalUnit(AnUnitInfo.Filename) then
3211 Add(AnUnitInfo.Filename);
3212 AnUnitInfo:=AnUnitInfo.NextPartOfProject;
3213 end;
3214 end;
3215 if (fuooListed in Flags) and (fuooPackages in Flags) then
3216 begin
3217 // get required packages
3218 if pfUseDesignTimePackages in aProject.Flags then
3219 PkgListFlags:=[]
3220 else
3221 PkgListFlags:=[pirSkipDesignTimeOnly];
3222 PackageGraph.GetAllRequiredPackages(nil,aProject.FirstRequiredDependency,
3223 PkgList,PkgListFlags);
3224 end;
3225 end else if TheOwner is TLazPackage then begin
3226 aPackage:=TLazPackage(TheOwner);
3227 if (fuooListed in Flags) then
3228 begin
3229 // add listed units (i.e. units in package editor)
3230 AddListedPackageUnits(aPackage);
3231 end;
3232 if (fuooUsed in Flags) then
3233 MainFile:=aPackage.GetSrcFilename;
3234 if (fuooListed in Flags) and (fuooPackages in Flags) then
3235 begin
3236 // get required packages
3237 PackageGraph.GetAllRequiredPackages(aPackage,nil,PkgList,[]);
3238 end;
3239 end else begin
3240 FreeAndNil(Result);
3241 raise Exception.Create('FindUnitsOfOwner: invalid owner '+DbgSName(TheOwner));
3242 end;
3243
3244 if (fuooListed in Flags) and (fuooPackages in Flags) and (PkgList<>nil) then begin
3245 // add package units (listed in their package editors)
3246 for i:=0 to PkgList.Count-1 do begin
3247 ReqPackage:=TLazPackage(PkgList[i]);
3248 AddListedPackageUnits(ReqPackage);
3249 end;
3250 end;
3251
3252 if (fuooUsed in Flags) and (MainFile<>'') then
3253 begin
3254 // add all used units with 'in' files
3255 Code:=CodeToolBoss.LoadFile(MainFile,true,false);
3256 if Code<>nil then begin
3257 UnitPath:='';
3258 if aProject<>nil then begin
3259 CodeToolBoss.FindDelphiProjectUnits(Code,FoundInUnits,MissingUnits,NormalUnits);
3260 if not (fuooPackages in Flags) then
3261 begin
3262 // only project units wanted -> create unitpath excluding unitpaths from packages
3263 // Note: even if the project contains an unitpath to the source
3264 // folder of a package, the units are not project units.
3265 UnitPath:=aProject.CompilerOptions.GetUnitPath(false);
3266 RemoveSearchPaths(UnitPath,aProject.CompilerOptions.GetInheritedOption(icoUnitPath,false));
3267 end;
3268 end
3269 else if aPackage<>nil then begin
3270 CodeToolBoss.FindDelphiPackageUnits(Code,FoundInUnits,MissingUnits,NormalUnits);
3271 if not (fuooPackages in Flags) then
3272 begin
3273 // only units of this package wanted
3274 // -> create unitpath excluding unitpaths from used packages
3275 // Note: even if the package contains an unitpath to the source
3276 // folder of a sub package, the units belong to the sub package
3277 UnitPath:=aPackage.CompilerOptions.GetUnitPath(false);
3278 RemoveSearchPaths(UnitPath,aPackage.CompilerOptions.GetInheritedOption(icoUnitPath,false));
3279 end;
3280 end;
3281 //debugln(['FindUnitsOfOwner UnitPath="',UnitPath,'"']);
3282 if FoundInUnits<>nil then
3283 for i:=0 to FoundInUnits.Count-1 do
3284 begin
3285 CurFilename:=TCodeBuffer(FoundInUnits.Objects[i]).Filename;
3286 Add(CurFilename); // units with 'in' filename always belong to the
3287 // project, that's the Delphi way
3288 AddUsedUnit(CurFilename);
3289 end;
3290 if NormalUnits<>nil then
3291 for i:=0 to NormalUnits.Count-1 do
3292 AddUsedUnit(TCodeBuffer(NormalUnits.Objects[i]).Filename);
3293 end;
3294 end;
3295 if (fuooSourceEditor in Flags) then
3296 for i := 0 to pred(SourceEditorManager.SourceEditorCount) do
3297 begin
3298 CurFilename := SourceEditorManager.SourceEditors[i].FileName;
3299 if FilenameIsPascalUnit(CurFilename) then
3300 Add(CurFilename);
3301 end;
3302 finally
3303 FoundInUnits.Free;
3304 MissingUnits.Free;
3305 NormalUnits.Free;
3306 PkgList.Free;
3307 Files.Free;
3308 end;
3309 end;
3310
SelectProjectItemsnull3311 function SelectProjectItems(ItemList: TViewUnitEntries;
3312 ItemType: TIDEProjectItem; MultiSelect: boolean;
3313 var MultiSelectCheckedState: Boolean): TModalResult;
3314 var
3315 i: integer;
3316 AUnitName, DlgCaption: string;
3317 MainUnitInfo: TUnitInfo;
3318 ActiveSourceEditor: TSourceEditor;
3319 ActiveUnitInfo: TUnitInfo;
3320 CurUnitInfo: TUnitInfo;
3321 LFMFilename: String;
3322 LFMType: String;
3323 LFMComponentName: String;
3324 LFMClassName: String;
3325 anUnitName: String;
3326 LFMCode: TCodeBuffer;
3327 begin
3328 if Project1=nil then exit(mrCancel);
3329 MainIDE.GetCurrentUnit(ActiveSourceEditor, ActiveUnitInfo);
3330 for i := 0 to Project1.UnitCount - 1 do
3331 begin
3332 CurUnitInfo:=Project1.Units[i];
3333 if not CurUnitInfo.IsPartOfProject then
3334 Continue;
3335 if ItemType in [piComponent, piFrame] then
3336 begin
3337 // add all form names of project
3338 if CurUnitInfo.ComponentName <> '' then
3339 begin
3340 if (ItemType = piComponent) or
3341 ((ItemType = piFrame) and (CurUnitInfo.ResourceBaseClass = pfcbcFrame)) then
3342 ItemList.Add(CurUnitInfo.ComponentName,
3343 CurUnitInfo.Filename, i, CurUnitInfo = ActiveUnitInfo);
3344 end else if FilenameIsAbsolute(CurUnitInfo.Filename)
3345 and FilenameIsPascalSource(CurUnitInfo.Filename)
3346 and FileExistsCached(CurUnitInfo.Filename) then
3347 begin
3348 // this unit has a lfm, but the lpi does not know a ComponentName
3349 // => maybe this component was added without the IDE
3350 LFMFilename:=ChangeFileExt(CurUnitInfo.Filename,'.lfm');
3351 LFMCode:=CodeToolBoss.LoadFile(LFMFilename,true,false);
3352 if LFMCode<>nil then
3353 begin
3354 ReadLFMHeader(LFMCode.Source,LFMType,LFMComponentName,LFMClassName);
3355 if LFMComponentName<>'' then begin
3356 anUnitName:=CurUnitInfo.Unit_Name;
3357 if anUnitName='' then
3358 anUnitName:=ExtractFileNameOnly(LFMFilename);
3359 ItemList.Add(LFMComponentName, CurUnitInfo.Filename,
3360 i, CurUnitInfo = ActiveUnitInfo);
3361 end;
3362 end;
3363 end;
3364 end else
3365 begin
3366 // add all unit names of project
3367 if (CurUnitInfo.FileName <> '') then
3368 begin
3369 AUnitName := ExtractFileName(CurUnitInfo.Filename);
3370 if ItemList.Find(AUnitName) = nil then
3371 ItemList.Add(AUnitName, CurUnitInfo.Filename,
3372 i, CurUnitInfo = ActiveUnitInfo);
3373 end
3374 else
3375 if Project1.MainUnitID = i then
3376 begin
3377 MainUnitInfo := Project1.MainUnitInfo;
3378 if pfMainUnitIsPascalSource in Project1.Flags then
3379 begin
3380 AUnitName := ExtractFileName(MainUnitInfo.Filename);
3381 if (AUnitName <> '') and (ItemList.Find(AUnitName) = nil) then
3382 begin
3383 ItemList.Add(AUnitName, MainUnitInfo.Filename,
3384 i, MainUnitInfo = ActiveUnitInfo);
3385 end;
3386 end;
3387 end;
3388 end;
3389 end;
3390 case ItemType of
3391 piUnit: DlgCaption := dlgMainViewUnits;
3392 piComponent: DlgCaption := dlgMainViewForms;
3393 piFrame: DlgCaption := dlgMainViewFrames;
3394 else DlgCaption := '';
3395 end;
3396 Result := ShowViewUnitsDlg(ItemList, MultiSelect, MultiSelectCheckedState, DlgCaption, ItemType);
3397 end;
3398
SelectUnitComponentsnull3399 function SelectUnitComponents(DlgCaption: string;
3400 ItemType: TIDEProjectItem; Files: TStringList; MultiSelect: boolean;
3401 var MultiSelectCheckedState: Boolean): TModalResult;
3402 var
3403 ActiveSourceEditor: TSourceEditor;
3404 ActiveUnitInfo: TUnitInfo;
3405 UnitToFilename: TStringToStringTree;
3406 UnitPath: String;
3407
ResourceFitsnull3408 function ResourceFits(ResourceBaseClass: TPFComponentBaseClass): boolean;
3409 begin
3410 case ItemType of
3411 piUnit: Result:=true;
3412 piComponent: Result:=ResourceBaseClass<>pfcbcNone;
3413 piFrame: Result:=ResourceBaseClass=pfcbcFrame;
3414 else Result:=false;
3415 end;
3416 end;
3417
3418 procedure AddUnit(AnUnitName,AFilename: string);
3419 var
3420 LFMFilename: String;
3421 begin
3422 //debugln(['AddUnit ',AFilename]);
3423 if not FilenameIsPascalUnit(AFilename) then exit;
3424 if CompareFilenames(AFilename,ActiveUnitInfo.Filename)=0 then exit;
3425 if (AnUnitName='') then
3426 AnUnitName:=ExtractFileNameOnly(AFilename);
3427 if (not FilenameIsAbsolute(AFilename)) then begin
3428 if (not ActiveUnitInfo.IsVirtual) then
3429 exit; // virtual UnitToFilename can not be accessed from disk UnitToFilename
3430 end else begin
3431 //debugln(['AddUnit unitpath=',UnitPath]);
3432 if SearchDirectoryInSearchPath(UnitPath,ExtractFilePath(AFilename))<1 then
3433 exit; // not reachable
3434 end;
3435 if UnitToFilename.Contains(AnUnitName) then exit; // duplicate unit
3436 if not FileExistsCached(AFilename) then exit;
3437 LFMFilename:=ChangeFileExt(aFilename,'.lfm');
3438 if not FileExistsCached(LFMFilename) then exit;
3439 UnitToFilename[AnUnitName]:=AFilename;
3440 end;
3441
3442 procedure AddPackage(Pkg: TLazPackage);
3443 var
3444 i: Integer;
3445 PkgFile: TPkgFile;
3446 begin
3447 //debugln(['AddPackage ',pkg.Name]);
3448 for i:=0 to Pkg.FileCount-1 do begin
3449 PkgFile:=TPkgFile(Pkg.Files[i]);
3450 if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
3451 if not FilenameIsAbsolute(PkgFile.Filename) then continue;
3452 if not ResourceFits(PkgFile.ResourceBaseClass) then begin
3453 if PkgFile.ResourceBaseClass<>pfcbcNone then continue;
3454 // unknown resource class => check file
3455 PkgFile.ResourceBaseClass:=FindLFMBaseClass(PkgFile.Filename);
3456 if not ResourceFits(PkgFile.ResourceBaseClass) then continue;
3457 end;
3458 AddUnit(PkgFile.Unit_Name,PkgFile.Filename);
3459 end;
3460 end;
3461
3462 var
3463 Owners: TFPList;
3464 APackage: TLazPackage;
3465 AProject: TProject;
3466 AnUnitInfo: TUnitInfo;
3467 FirstDependency: TPkgDependency;
3468 PkgList: TFPList;
3469 i: Integer;
3470 S2SItem: PStringToStringItem;
3471 AnUnitName: String;
3472 AFilename: String;
3473 UnitList: TViewUnitEntries;
3474 Entry: TViewUnitsEntry;
3475 begin
3476 Result:=mrCancel;
3477 MainIDE.GetCurrentUnit(ActiveSourceEditor, ActiveUnitInfo);
3478 if ActiveUnitInfo=nil then exit;
3479 Owners:=PkgBoss.GetPossibleOwnersOfUnit(ActiveUnitInfo.Filename,[]);
3480 UnitPath:=CodeToolBoss.GetCompleteSrcPathForDirectory(ExtractFilePath(ActiveUnitInfo.Filename));
3481 PkgList:=nil;
3482 UnitToFilename:=TStringToStringTree.Create(false);
3483 UnitList:=TViewUnitEntries.Create;
3484 try
3485 // fetch owner of active unit
3486 AProject:=nil;
3487 APackage:=nil;
3488 if (Owners<>nil) then begin
3489 for i:=0 to Owners.Count-1 do begin
3490 if TObject(Owners[i]) is TProject then begin
3491 AProject:=TProject(Owners[i]);
3492 break;
3493 end else if TObject(Owners[i]) is TLazPackage then begin
3494 APackage:=TLazPackage(Owners[i]);
3495 end;
3496 end;
3497 end;
3498 if AProject<>nil then begin
3499 // add project units
3500 //debugln(['SelectUnitComponents Project=',AProject.ProjectInfoFile]);
3501 FirstDependency:=AProject.FirstRequiredDependency;
3502 for i:=0 to AProject.UnitCount-1 do begin
3503 AnUnitInfo:=AProject.Units[i];
3504 if (not AnUnitInfo.IsPartOfProject)
3505 or (AnUnitInfo.ComponentName='')
3506 then continue;
3507 if not ResourceFits(AnUnitInfo.ResourceBaseClass) then begin
3508 if AnUnitInfo.ResourceBaseClass<>pfcbcNone then continue;
3509 // unknown resource class => check file
3510 AnUnitInfo.ResourceBaseClass:=FindLFMBaseClass(AnUnitInfo.Filename);
3511 if not ResourceFits(AnUnitInfo.ResourceBaseClass) then continue;
3512 end;
3513 AddUnit(AnUnitInfo.Unit_Name,AnUnitInfo.Filename);
3514 end;
3515 end else if APackage<>nil then begin
3516 // add package units
3517 FirstDependency:=APackage.FirstRequiredDependency;
3518 AddPackage(APackage);
3519 end else
3520 FirstDependency:=nil;
3521 // add all units of all used packages
3522 PackageGraph.GetAllRequiredPackages(nil,FirstDependency,PkgList);
3523 if PkgList<>nil then
3524 for i:=0 to PkgList.Count-1 do
3525 AddPackage(TLazPackage(PkgList[i]));
3526
3527 // create Files
3528 i:=0;
3529 for S2SItem in UnitToFilename do begin
3530 AnUnitName:=S2SItem^.Name;
3531 AFilename:=S2SItem^.Value;
3532 UnitList.Add(AnUnitName,AFilename,i,false);
3533 inc(i);
3534 end;
3535 // show dialog
3536 Result := ShowViewUnitsDlg(UnitList, MultiSelect, MultiSelectCheckedState,
3537 DlgCaption, ItemType, ActiveUnitInfo.Filename);
3538 // create list of selected files
3539 for Entry in UnitList do
3540 if Entry.Selected then
3541 Files.Add(Entry.Filename);
3542
3543 finally
3544 UnitList.Free;
3545 PkgList.Free;
3546 Owners.Free;
3547 UnitToFilename.Free;
3548 end;
3549 end;
3550
AddActiveUnitToProjectnull3551 function AddActiveUnitToProject: TModalResult;
3552 begin
3553 Result := AddUnitToProject(nil);
3554 end;
3555
RemoveFromProjectDialognull3556 function RemoveFromProjectDialog: TModalResult;
3557 var
3558 ViewUnitEntries: TViewUnitEntries;
3559 i:integer;
3560 AName: string;
3561 AnUnitInfo: TUnitInfo;
3562 UnitInfos: TFPList;
3563 UEntry: TViewUnitsEntry;
3564 const
3565 MultiSelectCheckedState: Boolean = true;
3566 Begin
3567 if Project1=nil then exit(mrCancel);
3568 Project1.UpdateIsPartOfProjectFromMainUnit;
3569 ViewUnitEntries := TViewUnitEntries.Create;
3570 UnitInfos:=nil;
3571 try
3572 for i := 0 to Project1.UnitCount-1 do
3573 begin
3574 AnUnitInfo:=Project1.Units[i];
3575 if (AnUnitInfo.IsPartOfProject) and (i<>Project1.MainUnitID) then
3576 begin
3577 AName := Project1.RemoveProjectPathFromFilename(AnUnitInfo.FileName);
3578 ViewUnitEntries.Add(AName,AnUnitInfo.FileName,i,false);
3579 end;
3580 end;
3581 if ShowViewUnitsDlg(ViewUnitEntries, true, MultiSelectCheckedState,
3582 lisRemoveFromProject, piUnit) <> mrOk then
3583 exit(mrOk);
3584 { This is where we check what the user selected. }
3585 UnitInfos:=TFPList.Create;
3586 for UEntry in ViewUnitEntries do
3587 begin
3588 if UEntry.Selected then
3589 begin
3590 if UEntry.ID<0 then continue;
3591 AnUnitInfo:=Project1.Units[UEntry.ID];
3592 if AnUnitInfo.IsPartOfProject then
3593 UnitInfos.Add(AnUnitInfo);
3594 end;
3595 end;
3596 if UnitInfos.Count>0 then
3597 Result:=RemoveFilesFromProject(UnitInfos)
3598 else
3599 Result:=mrOk;
3600 finally
3601 UnitInfos.Free;
3602 ViewUnitEntries.Free;
3603 end;
3604 end;
3605
InitNewProjectnull3606 function InitNewProject(ProjectDesc: TProjectDescriptor): TModalResult;
3607 var
3608 i:integer;
3609 HandlerResult: TModalResult;
3610 begin
3611 try
3612 Project1.BeginUpdate(true);
3613 try
3614 if Project1.CompilerOptions.CompilerPath='' then
3615 Project1.CompilerOptions.CompilerPath:=DefaultCompilerPath;
3616 if pfUseDefaultCompilerOptions in Project1.Flags then begin
3617 MainIDE.DoMergeDefaultProjectOptions(Project1);
3618 Project1.Flags:=Project1.Flags-[pfUseDefaultCompilerOptions];
3619 end;
3620 Project1.AutoAddOutputDirToIncPath;
3621 MainIDE.UpdateCaption;
3622 if ProjInspector<>nil then ProjInspector.LazProject:=Project1;
3623 // add and load default required packages
3624 PkgBoss.OpenProjectDependencies(Project1,true);
3625 // rebuild codetools defines
3626 MainBuildBoss.SetBuildTargetProject1(false);
3627 // (i.e. remove old project specific things and create new)
3628 IncreaseCompilerParseStamp;
3629 Project1.DefineTemplates.AllChanged;
3630 Project1.DefineTemplates.Active:=true;
3631 DebugBoss.Reset;
3632 finally
3633 Project1.EndUpdate;
3634 end;
3635 Project1.BeginUpdate(true);
3636 try
3637 // create files
3638 if ProjectDesc.CreateStartFiles(Project1)<>mrOk then begin
3639 debugln('InitNewProject ProjectDesc.CreateStartFiles failed');
3640 end;
3641 if (Project1.MainUnitInfo<>nil)
3642 and ((Project1.FirstUnitWithEditorIndex=nil)
3643 or ([pfMainUnitHasCreateFormStatements,pfMainUnitHasTitleStatement,pfMainUnitHasScaledStatement]*Project1.Flags=[]))
3644 then begin
3645 // the project has not created any secondary files
3646 // or the project main source is not auto updated by the IDE
3647 OpenMainUnit(-1,-1,[]);
3648 end;
3649
3650 // init resource files
3651 if not Project1.ProjResources.Regenerate(Project1.MainFilename, True, False,'') then
3652 DebugLn('InitNewProject Project1.Resources.Regenerate failed');
3653 finally
3654 Project1.EndUpdate;
3655 end;
3656 Result:=mrOk;
3657 finally
3658 // set all modified to false
3659 Project1.UpdateAllVisibleUnits;
3660 for i:=0 to Project1.UnitCount-1 do
3661 Project1.Units[i].ClearModifieds;
3662 Project1.Modified:=false;
3663 // call handlers
3664 HandlerResult:=MainIDE.DoCallProjectChangedHandler(lihtProjectOpened, Project1);
3665 if not (HandlerResult in [mrOk,mrCancel,mrAbort]) then
3666 HandlerResult:=mrCancel;
3667 if (Result=mrOk) then
3668 Result:=HandlerResult;
3669 end;
3670 end;
3671
InitOpenedProjectFilenull3672 function InitOpenedProjectFile(AFileName: string; Flags: TOpenFlags): TModalResult;
3673 var
3674 EditorInfoIndex, i, j: Integer;
3675 NewBuf: TCodeBuffer;
3676 LastDesigner: TIDesigner;
3677 AnUnitInfo: TUnitInfo;
3678 HandlerResult: TModalResult;
3679 AnEditorInfo: TUnitEditorInfo;
3680 begin
3681 EditorInfoIndex := 0;
3682 SourceEditorManager.IncUpdateLock;
3683 try
3684 Project1.BeginUpdate(true);
3685 try
3686 if ProjInspector<>nil then ProjInspector.LazProject:=Project1;
3687
3688 // read project info file
3689 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile B3');{$ENDIF}
3690 Project1.ReadProject(AFilename, EnvironmentOptions.BuildMatrixOptions, True);
3691 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile B4');{$ENDIF}
3692 Result:=CompleteLoadingProjectInfo;
3693 finally
3694 Project1.EndUpdate;
3695 end;
3696 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile B5');{$ENDIF}
3697 if Result<>mrOk then exit;
3698
3699 if Project1.MainUnitID>=0 then begin
3700 // read MainUnit Source
3701 Result:=LoadCodeBuffer(NewBuf,Project1.MainFilename,
3702 [lbfUpdateFromDisk,lbfRevert],false);// do not check if source is text
3703 case Result of
3704 mrOk: Project1.MainUnitInfo.Source:=NewBuf;
3705 mrIgnore: Project1.MainUnitInfo.Source:=CodeToolBoss.CreateFile(Project1.MainFilename);
3706 else exit(mrCancel);
3707 end;
3708 end;
3709 //debugln('InitOpenedProjectFile C');
3710 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile C');{$ENDIF}
3711 IncreaseCompilerParseStamp;
3712
3713 // restore files
3714 while EditorInfoIndex < Project1.AllEditorsInfoCount do begin
3715 // TProject.ReadProject sorts all UnitEditorInfos
3716 AnEditorInfo := Project1.AllEditorsInfo[EditorInfoIndex];
3717 AnUnitInfo := AnEditorInfo.UnitInfo;
3718 if (not AnUnitInfo.Loaded) or (AnEditorInfo.PageIndex < 0) then begin
3719 inc(EditorInfoIndex);
3720 Continue;
3721 end;
3722
3723 // reopen file
3724 if (not AnUnitInfo.IsPartOfProject)
3725 and (not FileExistsCached(AnUnitInfo.Filename)) then begin
3726 // this file does not exist, but is not important => silently ignore
3727 end
3728 else begin
3729 // reopen file
3730 // This will adjust Page/WindowIndex if they are not continous
3731 Result:=OpenEditorFile(AnUnitInfo.Filename, -1, AnEditorInfo.WindowID,
3732 AnEditorInfo, [ofProjectLoading,ofMultiOpen,ofOnlyIfExists], True);
3733 if Result=mrAbort then
3734 exit;
3735 end;
3736 if not ((AnUnitInfo.Filename<>'') and (AnEditorInfo.EditorComponent <> nil))
3737 then begin
3738 // failed to open
3739 AnEditorInfo.PageIndex := -1;
3740 // if failed entirely -> mark as unloaded, so that next time it will not be tried again
3741 if AnUnitInfo.OpenEditorInfoCount = 0 then
3742 AnUnitInfo.Loaded := False;
3743 end;
3744 inc(EditorInfoIndex);
3745 end; // while EditorInfoIndex < Project1.AllEditorsInfoCount
3746 Result:=mrCancel;
3747 //debugln('InitOpenedProjectFile D');
3748
3749 // set active editor source editor
3750 for i := 0 to Project1.AllEditorsInfoCount - 1 do begin
3751 AnEditorInfo := Project1.AllEditorsInfo[i];
3752 if AnEditorInfo.IsVisibleTab then
3753 begin
3754 if (AnEditorInfo.WindowID < 0) then continue;
3755 j := SourceEditorManager.IndexOfSourceWindowWithID(AnEditorInfo.WindowID);
3756 if j < 0
3757 then begin
3758 // session info is invalid (buggy lps file?) => auto fix
3759 AnEditorInfo.IsVisibleTab:=false;
3760 AnEditorInfo.WindowID:=-1;
3761 Continue;
3762 end;
3763 if (SourceEditorManager.SourceWindows[j] <> nil) then
3764 SourceEditorManager.SourceWindows[j].PageIndex := AnEditorInfo.PageIndex;
3765 end;
3766 end;
3767 if (Project1.ActiveWindowIndexAtStart<0)
3768 or (Project1.ActiveWindowIndexAtStart >= SourceEditorManager.SourceWindowCount)
3769 then begin
3770 // session info is invalid (buggy lps file?) => auto fix
3771 Project1.ActiveWindowIndexAtStart := 0;
3772 end;
3773 if (Project1.ActiveWindowIndexAtStart >= 0) and
3774 (Project1.ActiveWindowIndexAtStart < SourceEditorManager.SourceWindowCount)
3775 then begin
3776 SourceEditorManager.ActiveSourceWindow :=
3777 SourceEditorManager.SourceWindows[Project1.ActiveWindowIndexAtStart];
3778 SourceEditorManager.ShowActiveWindowOnTop(True);
3779 end;
3780
3781 if ([ofDoNotLoadResource]*Flags=[])
3782 and ( (not Project1.AutoOpenDesignerFormsDisabled)
3783 and EnvironmentOptions.AutoCreateFormsOnOpen
3784 and (SourceEditorManager.ActiveEditor<>nil) )
3785 then begin
3786 // auto open form of active unit
3787 AnUnitInfo:=Project1.UnitWithEditorComponent(SourceEditorManager.ActiveEditor);
3788 if AnUnitInfo<>nil then
3789 Result:=LoadLFM(AnUnitInfo,[ofProjectLoading,ofMultiOpen,ofOnlyIfExists],
3790 [cfSaveDependencies]);
3791 end;
3792
3793 // select a form (object inspector, formeditor, control selection)
3794 if MainIDE.LastFormActivated<>nil then begin
3795 LastDesigner:=MainIDE.LastFormActivated.Designer;
3796 debugln(['InitOpenedProjectFile select form in designer: ',
3797 DbgSName(MainIDE.LastFormActivated),' ',DbgSName(MainIDE.LastFormActivated.Designer)]);
3798 LastDesigner.SelectOnlyThisComponent(LastDesigner.LookupRoot);
3799 end;
3800
3801 // set all modified to false
3802 Project1.UpdateAllVisibleUnits;
3803 Project1.ClearModifieds(true);
3804
3805 IncreaseCompilerParseStamp;
3806 IDEProtocolOpts.LastProjectLoadingCrashed := False;
3807 Result:=mrOk;
3808 finally
3809 SourceEditorManager.DecUpdateLock;
3810 if (Result<>mrOk) and (Project1<>nil) then begin
3811 // mark all files, that are left to open as unloaded:
3812 for i := EditorInfoIndex to Project1.AllEditorsInfoCount - 1 do begin
3813 AnEditorInfo := Project1.AllEditorsInfo[i];
3814 AnEditorInfo.PageIndex := -1;
3815 AnUnitInfo := AnEditorInfo.UnitInfo;
3816 if AnUnitInfo.Loaded and (AnUnitInfo.OpenEditorInfoCount = 0) then
3817 AnUnitInfo.Loaded := false;
3818 end;
3819 end;
3820 // call handlers
3821 HandlerResult:=MainIDE.DoCallProjectChangedHandler(lihtProjectOpened, Project1);
3822 if not (HandlerResult in [mrOk,mrCancel,mrAbort]) then
3823 HandlerResult:=mrCancel;
3824 if (Result=mrOk) then
3825 Result:=HandlerResult;
3826 end;
3827 if Result=mrAbort then exit;
3828 //debugln('InitOpenedProjectFile end CodeToolBoss.ConsistencyCheck=',IntToStr(CodeToolBoss.ConsistencyCheck));
3829 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('InitOpenedProjectFile end');{$ENDIF}
3830 end;
3831
3832 procedure NewProjectFromFile;
3833 var
3834 OpenDialog:TOpenDialog;
3835 AFilename: string;
3836 PreReadBuf: TCodeBuffer;
3837 Filter: String;
3838 Begin
3839 OpenDialog:=TOpenDialog.Create(nil);
3840 try
3841 InputHistories.ApplyFileDialogSettings(OpenDialog);
3842 OpenDialog.Title:=lisChooseProgramSourcePpPasLpr;
3843 OpenDialog.Options:=OpenDialog.Options+[ofPathMustExist,ofFileMustExist];
3844 Filter := dlgFilterLazarusUnit + ' (*.pas;*.pp;*.p)|*.pas;*.pp;*.p'
3845 + '|' + dlgFilterLazarusProjectSource + ' (*.lpr)|*.lpr';
3846 Filter:=Filter+ '|' + dlgFilterAll + ' (' + GetAllFilesMask + ')|' + GetAllFilesMask;
3847 OpenDialog.Filter := Filter;
3848 if OpenDialog.Execute then begin
3849 AFilename:=ExpandFileNameUTF8(OpenDialog.Filename);
3850 if not FilenameIsPascalSource(AFilename) then begin
3851 IDEMessageDialog(lisPkgMangInvalidFileExtension,
3852 lisProgramSourceMustHaveAPascalExtensionLikePasPpOrLp,
3853 mtError,[mbOk],'');
3854 exit;
3855 end;
3856 if mrOk<>LoadCodeBuffer(PreReadBuf,AFileName,
3857 [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert],false)
3858 then
3859 exit;
3860 if CreateProjectForProgram(PreReadBuf)=mrOk then
3861 exit;
3862 end;
3863 finally
3864 InputHistories.StoreFileDialogSettings(OpenDialog);
3865 OpenDialog.Free;
3866 end;
3867 end;
3868
CreateProjectForProgramnull3869 function CreateProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult;
3870 var
3871 NewProjectDesc: TProjectDescriptor;
3872 begin
3873 //debugln('[CreateProjectForProgram] A ',ProgramBuf.Filename);
3874 if (Project1 <> nil)
3875 and (not MainIDE.DoResetToolStatus([rfInteractive, rfSuccessOnTrigger])) then exit(mrAbort);
3876
3877 Result:=SaveProjectIfChanged;
3878 if Result=mrAbort then exit;
3879
3880 // let user choose the program type
3881 NewProjectDesc:=nil;
3882 if ChooseNewProject(NewProjectDesc)<>mrOk then exit;
3883
3884 // close old project
3885 If Project1<>nil then begin
3886 if CloseProject=mrAbort then begin
3887 Result:=mrAbort;
3888 exit;
3889 end;
3890 end;
3891
3892 // reload file (if the file was open in the IDE, closeproject unloaded it)
3893 ProgramBuf.Reload;
3894
3895 // switch codetools to new project directory
3896 CodeToolBoss.GlobalValues.Variables[ExternalMacroStart+'ProjPath']:=
3897 ExpandFileNameUTF8(ExtractFilePath(ProgramBuf.Filename));
3898
3899 // create a new project
3900 Project1:=MainIDE.CreateProjectObject(NewProjectDesc,ProjectDescriptorProgram);
3901 Result:=InitProjectForProgram(ProgramBuf);
3902 //debugln('[CreateProjectForProgram] END');
3903 end;
3904
InitProjectForProgramnull3905 function InitProjectForProgram(ProgramBuf: TCodeBuffer): TModalResult;
3906 var
3907 MainUnitInfo: TUnitInfo;
3908 begin
3909 Project1.BeginUpdate(true);
3910 try
3911 if ProjInspector<>nil then ProjInspector.LazProject:=Project1;
3912 MainUnitInfo:=Project1.MainUnitInfo;
3913 MainUnitInfo.Source:=ProgramBuf;
3914 Project1.ProjectInfoFile:=ChangeFileExt(ProgramBuf.Filename,'.lpi');
3915 Project1.CompilerOptions.TargetFilename:=ExtractFileNameOnly(ProgramBuf.Filename);
3916 MainIDE.DoMergeDefaultProjectOptions(Project1);
3917 MainIDE.UpdateCaption;
3918 IncreaseCompilerParseStamp;
3919 // add and load default required packages
3920 PkgBoss.OpenProjectDependencies(Project1,true);
3921 Result:=CompleteLoadingProjectInfo;
3922 if Result<>mrOk then exit;
3923 finally
3924 Project1.EndUpdate;
3925 end;
3926 // show program unit
3927 Result:=OpenEditorFile(ProgramBuf.Filename,-1,-1, nil, [ofAddToRecent,ofRegularFile]);
3928 if Result=mrAbort then exit;
3929 Result:=mrOk;
3930 end;
3931
SaveProjectnull3932 function SaveProject(Flags: TSaveFlags):TModalResult;
3933 var
3934 i, j: integer;
3935 AnUnitInfo: TUnitInfo;
3936 SaveFileFlags: TSaveFlags;
3937 SrcEdit: TSourceEditor;
3938 begin
3939 Result:=mrCancel;
3940 if not (MainIDE.ToolStatus in [itNone,itDebugger]) then begin
3941 Result:=mrAbort;
3942 exit;
3943 end;
3944 SaveEditorChangesToCodeCache(nil);
3945 //DebugLn('SaveProject A SaveAs=',dbgs(sfSaveAs in Flags),' SaveToTestDir=',dbgs(sfSaveToTestDir in Flags),' ProjectInfoFile=',Project1.ProjectInfoFile);
3946 Result:=MainIDE.DoCheckFilesOnDisk(true);
3947 if Result in [mrCancel,mrAbort] then begin
3948 debugln(['Info: (lazarus) [SaveProject] MainIDE.DoCheckFilesOnDisk failed']);
3949 exit;
3950 end;
3951
3952 if CheckMainSrcLCLInterfaces(sfQuietUnitCheck in Flags)<>mrOk then begin
3953 debugln(['Info: (lazarus) [SaveProject] CheckMainSrcLCLInterfaces failed']);
3954 exit(mrCancel);
3955 end;
3956
3957 // if this is a virtual project then save first the project info file
3958 // to get a project directory
3959 if Project1.IsVirtual and ([sfSaveToTestDir,sfDoNotSaveVirtualFiles]*Flags=[])
3960 then begin
3961 Result:=SaveProjectInfo(Flags);
3962 if Result in [mrCancel,mrAbort] then begin
3963 debugln(['Info: (lazarus) [SaveProject] SaveProjectInfo failed']);
3964 exit;
3965 end;
3966 end;
3967
3968 // save virtual files
3969 if (not (sfDoNotSaveVirtualFiles in Flags)) then
3970 begin
3971 // check that all new units are saved first to get valid filenames
3972 // Note: this can alter the mainunit: e.g. used unit names
3973 for i:=0 to Project1.UnitCount-1 do begin
3974 AnUnitInfo:=Project1.Units[i];
3975 if (AnUnitInfo.Loaded) and AnUnitInfo.IsVirtual
3976 and AnUnitInfo.IsPartOfProject
3977 and (Project1.MainUnitID<>i)
3978 and (AnUnitInfo.OpenEditorInfoCount > 0) then begin
3979 SaveFileFlags:=[sfSaveAs,sfProjectSaving]+[sfCheckAmbiguousFiles]*Flags;
3980 if sfSaveToTestDir in Flags then begin
3981 Assert(AnUnitInfo.IsPartOfProject or AnUnitInfo.IsVirtual, 'SaveProject: Not IsPartOfProject or IsVirtual');
3982 Include(SaveFileFlags,sfSaveToTestDir);
3983 end;
3984 Result:=SaveEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent, SaveFileFlags);
3985 if Result in [mrCancel,mrAbort] then begin
3986 debugln(['Info: (lazarus) [SaveProject] SaveEditorFile "',AnUnitInfo.Filename,'" failed']);
3987 exit;
3988 end;
3989 end;
3990 end;
3991 end;
3992
3993 Result:=SaveProjectInfo(Flags);
3994 if Result in [mrCancel,mrAbort] then begin
3995 debugln(['Info: (lazarus) [SaveProject] SaveProjectInfo failed']);
3996 exit;
3997 end;
3998
3999 // save all editor files
4000 for i:=0 to SourceEditorManager.SourceEditorCount-1 do begin
4001 SrcEdit:=SourceEditorManager.SourceEditors[i];
4002 AnUnitInfo:=Project1.UnitWithEditorComponent(SrcEdit);
4003 if (Project1.MainUnitID>=0) and (Project1.MainUnitInfo = AnUnitInfo) then
4004 continue;
4005 SaveFileFlags:=[sfProjectSaving]+Flags*[sfCheckAmbiguousFiles];
4006 if AnUnitInfo = nil
4007 then begin
4008 // inconsistency detected, write debug info
4009 DebugLn(['SaveProject - unit not found for page ',i,' File="',SrcEdit.FileName,'" SrcEdit=',dbgsname(SrcEdit),'=',dbgs(Pointer(SrcEdit))]);
4010 DumpStack;
4011 debugln(['SaveProject Project1 has the following information about the source editor:']);
4012 AnUnitInfo:=Project1.FirstUnitWithEditorIndex;
4013 while AnUnitInfo<>nil do begin
4014 for j:=0 to AnUnitInfo.EditorInfoCount-1 do begin
4015 dbgout([' ',AnUnitInfo.Filename,' ',j,'/',AnUnitInfo.EditorInfoCount,' Component=',dbgsname(AnUnitInfo.EditorInfo[j].EditorComponent),'=',dbgs(Pointer(AnUnitInfo.EditorInfo[j].EditorComponent))]);
4016 if AnUnitInfo.EditorInfo[j].EditorComponent<>nil then
4017 dbgout(AnUnitInfo.EditorInfo[j].EditorComponent.FileName);
4018 debugln;
4019 end;
4020 debugln([' ',AnUnitInfo.EditorInfoCount]);
4021 AnUnitInfo:=AnUnitInfo.NextUnitWithEditorIndex;
4022 end;
4023 end else begin
4024 if AnUnitInfo.IsVirtual then begin
4025 if (sfSaveToTestDir in Flags) then
4026 Include(SaveFileFlags,sfSaveToTestDir)
4027 else
4028 continue;
4029 end;
4030 end;
4031 Result:=SaveEditorFile(SrcEdit, SaveFileFlags);
4032 if Result=mrAbort then begin
4033 debugln(['Info: (lazarus) [SaveProject] SaveEditorFile "',SrcEdit.FileName,'" failed']);
4034 exit;
4035 end;
4036 // mrCancel: continue saving other files
4037 end;
4038
4039 // update all lrs files
4040 if sfSaveToTestDir in Flags then
4041 MainBuildBoss.UpdateProjectAutomaticFiles(EnvironmentOptions.GetParsedTestBuildDirectory)
4042 else
4043 MainBuildBoss.UpdateProjectAutomaticFiles('');
4044
4045 // everything went well => clear all modified flags
4046 Project1.ClearModifieds(true);
4047 // update menu and buttons state
4048 MainIDE.UpdateSaveMenuItemsAndButtons(true);
4049 //DebugLn('SaveProject End');
4050 Result:=mrOk;
4051 end;
4052
SaveProjectIfChangednull4053 function SaveProjectIfChanged: TModalResult;
4054 begin
4055 if SomethingOfProjectIsModified then begin
4056 if IDEMessageDialog(lisProjectChanged, Format(lisSaveChangesToProject,
4057 [Project1.GetTitleOrName]),
4058 mtconfirmation, [mbYes, mbNo, mbCancel])=mrYes then
4059 begin
4060 if SaveProject([])=mrAbort then begin
4061 Result:=mrAbort;
4062 exit;
4063 end;
4064 end;
4065 end;
4066 Result:=mrOk;
4067 end;
4068
CloseProjectnull4069 function CloseProject: TModalResult;
4070 var
4071 SrcEdit: TSourceEditor;
4072 begin
4073 if Project1=nil then exit(mrOk);
4074
4075 //debugln('CloseProject A');
4076 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('CloseProject A');{$ENDIF}
4077 Result:=DebugBoss.DoStopProject;
4078 if Result<>mrOk then begin
4079 debugln('CloseProject DebugBoss.DoStopProject failed');
4080 exit;
4081 end;
4082
4083 // call handlers
4084 Result:=MainIDE.DoCallProjectChangedHandler(lihtProjectClose, Project1);
4085 if Result=mrAbort then exit;
4086
4087 // close all loaded files
4088 SourceEditorManager.IncUpdateLock;
4089 try
4090 while SourceEditorManager.SourceEditorCount > 0 do begin
4091 SrcEdit:=SourceEditorManager.SourceEditors[SourceEditorManager.SourceEditorCount-1];
4092 Result:=CloseEditorFile(SrcEdit,[cfProjectClosing]);
4093 if Result=mrAbort then exit;
4094 end;
4095 finally
4096 SourceEditorManager.DecUpdateLock;
4097 end;
4098 // remove all source modifications
4099 CodeToolBoss.SourceCache.ClearAllModified;
4100
4101 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('CloseProject B');{$ENDIF}
4102 IncreaseCompilerParseStamp;
4103
4104 // close Project
4105 if ProjInspector<>nil then
4106 ProjInspector.LazProject:=nil;
4107 FreeThenNil(Project1);
4108 if IDEMessagesWindow<>nil then IDEMessagesWindow.Clear;
4109
4110 MainIDE.UpdateCaption;
4111 {$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('CloseProject C');{$ENDIF}
4112 Result:=mrOk;
4113 //debugln('CloseProject end ',CodeToolBoss.ConsistencyCheck);
4114 end;
4115
4116 procedure OpenProject(aMenuItem: TIDEMenuItem);
4117 var
4118 OpenDialog: TOpenDialog;
4119 AFileName: string;
4120 LoadFlags: TLoadBufferFlags;
4121 PreReadBuf: TCodeBuffer;
4122 SourceType: String;
4123 LPIFilename: String;
4124 begin
4125 if Assigned(aMenuItem) and (aMenuItem.Section=itmProjectRecentOpen) then
4126 begin
4127 // Hint holds the full filename, Caption may have a shortened form.
4128 AFileName:=aMenuItem.Hint;
4129 Assert(AFileName = ExpandFileNameUTF8(AFileName),'OpenProject: AFileName is not absolute.');
4130 if MainIDE.DoOpenProjectFile(AFilename,[ofAddToRecent])=mrOk then begin
4131 AddRecentProjectFile(AFilename);
4132 end else begin
4133 // open failed
4134 if not FileExistsCached(AFilename) then begin
4135 EnvironmentOptions.RemoveFromRecentProjectFiles(AFilename);
4136 end else
4137 AddRecentProjectFile(AFilename);
4138 end;
4139 end
4140 else begin
4141 OpenDialog:=TOpenDialog.Create(nil);
4142 try
4143 InputHistories.ApplyFileDialogSettings(OpenDialog);
4144 OpenDialog.Title:=lisOpenProjectFile+' (*.lpi)';
4145 OpenDialog.Filter := dlgFilterLazarusProject+' (*.lpi)|*.lpi|'
4146 +dlgFilterAll+'|'+GetAllFilesMask;
4147 if OpenDialog.Execute then begin
4148 AFilename:=GetPhysicalFilenameCached(ExpandFileNameUTF8(OpenDialog.Filename),false);
4149 if CompareFileExt(AFilename,'.lpi')<>0 then begin
4150 // not a lpi file
4151 // check if it is a program source
4152
4153 // load the source
4154 LoadFlags := [lbfCheckIfText,lbfUpdateFromDisk,lbfRevert];
4155 if LoadCodeBuffer(PreReadBuf,AFileName,LoadFlags,true)<>mrOk then exit;
4156
4157 // check if unit is a program
4158 SourceType:=CodeToolBoss.GetSourceType(PreReadBuf,false);
4159 if (SysUtils.CompareText(SourceType,'PROGRAM')=0)
4160 or (SysUtils.CompareText(SourceType,'LIBRARY')=0)
4161 then begin
4162 // source is a program
4163 // either this is a lazarus project
4164 // or it is not yet a lazarus project ;)
4165 LPIFilename:=ChangeFileExt(AFilename,'.lpi');
4166 if FileExistsCached(LPIFilename) then begin
4167 if IDEQuestionDialog(lisProjectInfoFileDetected,
4168 Format(lisTheFileSeemsToBeTheProgramFileOfAnExistingLazarusP, [AFilename]),
4169 mtConfirmation, [mrOk,lisOpenProject2,
4170 mrCancel]) <> mrOk
4171 then
4172 exit;
4173 AFilename:=LPIFilename;
4174 end else begin
4175 if IDEQuestionDialog(lisFileHasNoProject,
4176 Format(lisTheFileIsNotALazarusProjectCreateANewProjectForThi,
4177 [AFilename, LineEnding, lowercase(SourceType)]),
4178 mtConfirmation, [mrYes, lisCreateProject,
4179 mrCancel]) <> mrYes
4180 then
4181 exit;
4182 CreateProjectForProgram(PreReadBuf);
4183 exit;
4184 end;
4185 end;
4186 end;
4187 MainIDE.DoOpenProjectFile(AFilename,[ofAddToRecent]);
4188 end;
4189 InputHistories.StoreFileDialogSettings(OpenDialog);
4190 finally
4191 OpenDialog.Free;
4192 end;
4193 end;
4194 end;
4195
AskToSaveEditorsnull4196 function AskToSaveEditors(EditorList: TList): TModalResult;
4197 // Ask from user about saving the changed SourceEditors in EditorList.
4198 var
4199 Ed: TSourceEditor;
4200 r: TModalResult;
4201 i, Remain: Integer;
4202 begin
4203 Result := mrOK;
4204 if EditorList.Count = 1 then begin
4205 Ed := TSourceEditor(EditorList[0]);
4206 r := IDEQuestionDialog(lisSourceModified,
4207 Format(lisSourceOfPageHasChangedSave, [Ed.PageName]),
4208 mtConfirmation, [mrYes, lisMenuSave,
4209 mrNo, lisDiscardChanges,
4210 mrAbort]);
4211 case r of
4212 mrYes: SaveEditorFile(Ed, [sfCheckAmbiguousFiles]);
4213 mrNo: ; // don't save
4214 mrAbort, mrCancel: Result := mrAbort;
4215 end;
4216 end
4217 else if EditorList.Count > 1 then
4218 for i := 0 to EditorList.Count - 1 do begin
4219 Ed := TSourceEditor(EditorList[i]);
4220 Remain := EditorList.Count-i-1; // Remaining number of files to go.
4221 r := IDEQuestionDialog(lisSourceModified,
4222 Format(lisSourceOfPageHasChangedSaveEx, [Ed.PageName,Remain]),
4223 mtConfirmation, [mrYes, lisMenuSave,
4224 mrAll, lisSaveAll,
4225 mrNo, lisDiscardChanges,
4226 mrIgnore, lisDiscardChangesAll,
4227 mrAbort]);
4228 case r of
4229 mrYes: SaveEditorFile(Ed, [sfCheckAmbiguousFiles]);
4230 mrNo: ; // don't save
4231 mrAll: begin
4232 MainIDE.DoSaveAll([]);
4233 break;
4234 end;
4235 mrIgnore: break; // don't save anymore
4236 mrAbort, mrCancel: begin
4237 Result := mrAbort;
4238 break;
4239 end;
4240 end;
4241 end;
4242 end;
4243
4244 procedure CloseAll;
4245 // Close editor files
4246 var
4247 Ed: TSourceEditor;
4248 EditorList: TList;
4249 i: Integer;
4250 begin
4251 EditorList := TList.Create;
4252 try
4253 // Collect changed editors into a list and save them after asking from user.
4254 for i := 0 to SourceEditorManager.UniqueSourceEditorCount - 1 do
4255 begin
4256 Ed := TSourceEditor(SourceEditorManager.UniqueSourceEditors[i]);
4257 if CheckEditorNeedsSave(Ed, False) then
4258 EditorList.Add(Ed);
4259 end;
4260 if AskToSaveEditors(EditorList) <> mrOK then Exit;
4261 finally
4262 EditorList.Free;
4263 end;
4264 // Now close them all.
4265 SourceEditorManager.IncUpdateLock;
4266 try
4267 while (SourceEditorManager.SourceEditorCount > 0) and
4268 (CloseEditorFile(SourceEditorManager.SourceEditors[0], []) = mrOk)
4269 do ;
4270 finally
4271 SourceEditorManager.DecUpdateLock;
4272 end;
4273
4274 // Close packages
4275 PkgBoss.DoCloseAllPackageEditors;
4276 end;
4277
4278 procedure InvertedFileClose(PageIndex: LongInt; SrcNoteBook: TSourceNotebook);
4279 // close all source editors except the clicked
4280 var
4281 Ed: TSourceEditor;
4282 EditorList: TList;
4283 i: Integer;
4284 begin
4285 EditorList := TList.Create;
4286 try
4287 // Collect changed editors, except the active one, into a list and maybe save them.
4288 for i := 0 to SrcNoteBook.EditorCount - 1 do begin
4289 Ed := SrcNoteBook.Editors[i];
4290 if (i <> PageIndex) and CheckEditorNeedsSave(Ed, True) then
4291 EditorList.Add(Ed);
4292 end;
4293 if AskToSaveEditors(EditorList) <> mrOK then Exit;
4294 finally
4295 EditorList.Free;
4296 end;
4297 // Now close all editors except the active one.
4298 SourceEditorManager.IncUpdateLock;
4299 try
4300 repeat
4301 i:=SrcNoteBook.PageCount-1;
4302 if i=PageIndex then dec(i);
4303 if i<0 then break;
4304 if CloseEditorFile(SrcNoteBook.FindSourceEditorWithPageIndex(i),[])<>mrOk then exit;
4305 if i<PageIndex then PageIndex:=i;
4306 until false;
4307 finally
4308 SourceEditorManager.DecUpdateLock;
4309 end;
4310 end;
4311
4312 function CreateNewCodeBuffer(Descriptor: TProjectFileDescriptor;
4313 NewOwner: TObject; NewFilename: string;
4314 var NewCodeBuffer: TCodeBuffer; var NewUnitName: string): TModalResult;
4315 var
4316 NewShortFilename: String;
4317 NewFileExt: String;
4318 SearchFlags: TSearchIDEFileFlags;
4319 begin
4320 //debugln('CreateNewCodeBuffer START NewFilename=',NewFilename,' ',Descriptor.DefaultFilename,' ',Descriptor.ClassName);
4321 NewUnitName:='';
4322 NewCodeBuffer:=nil;
4323 if NewFilename='' then begin
4324 // create a new unique filename
4325 SearchFlags:=[siffCheckAllProjects];
4326 if Descriptor.IsPascalUnit then begin
4327 if NewUnitName='' then
4328 NewUnitName:=Descriptor.DefaultSourceName;
4329 NewShortFilename:=lowercase(NewUnitName);
4330 NewFileExt:=Descriptor.DefaultFileExt;
4331 SearchFlags:=SearchFlags+[siffIgnoreExtension];
4332 end else begin
4333 NewFilename:=ExtractFilename(Descriptor.DefaultFilename);
4334 NewShortFilename:=ExtractFilenameOnly(NewFilename);
4335 NewFileExt:=ExtractFileExt(NewFilename);
4336 SearchFlags:=[];
4337 end;
4338 NewFilename:=MainIDE.CreateNewUniqueFilename(NewShortFilename,
4339 NewFileExt,NewOwner,SearchFlags,true);
4340 if NewFilename='' then
4341 RaiseGDBException('');
4342 NewShortFilename:=ExtractFilenameOnly(NewFilename);
4343 // use as unitname the NewShortFilename, but with the case of the
4344 // original unitname. e.g. 'unit12.pas' becomes 'Unit12.pas'
4345 if Descriptor.IsPascalUnit then begin
4346 NewUnitName:=ChompEndNumber(NewUnitName);
4347 NewUnitName:=NewUnitName+copy(NewShortFilename,length(NewUnitName)+1,
4348 length(NewShortFilename));
4349 end;
4350 end;
4351 //debugln('CreateNewCodeBuffer NewFilename=',NewFilename,' NewUnitName=',NewUnitName);
4352
4353 if FilenameIsPascalUnit(NewFilename) then begin
4354 if NewUnitName='' then
4355 NewUnitName:=ExtractFileNameOnly(NewFilename);
4356 if EnvironmentOptions.CharcaseFileAction in [ccfaAsk, ccfaAutoRename] then
4357 NewFilename:=ExtractFilePath(NewFilename)
4358 +lowercase(ExtractFileName(NewFilename));
4359 end;
4360
4361 NewCodeBuffer:=CodeToolBoss.CreateFile(NewFilename);
4362 if NewCodeBuffer=nil then
4363 exit(mrCancel);
4364 Result:=mrOk;
4365 end;
4366
4367 function CreateNewForm(NewUnitInfo: TUnitInfo;
4368 AncestorType: TPersistentClass; ResourceCode: TCodeBuffer;
4369 UseCreateFormStatements, DisableAutoSize: Boolean): TModalResult;
4370 var
4371 NewComponent: TComponent;
4372 new_x, new_y: integer;
4373 MainIDEBarBottom: integer;
4374 r: TRect;
4375 begin
4376 if not AncestorType.InheritsFrom(TComponent) then
4377 RaiseGDBException('CreateNewForm invalid AncestorType');
4378
4379 //debugln('CreateNewForm START ',NewUnitInfo.Filename,' ',AncestorType.ClassName,' ',dbgs(ResourceCode<>nil));
4380 // create a buffer for the new resource file and for the LFM file
4381 if ResourceCode=nil then
4382 ResourceCode:=CodeToolBoss.CreateFile(ChangeFileExt(NewUnitInfo.Filename,
4383 ResourceFileExt));
4384 //debugln('CreateNewForm B ',ResourceCode.Filename);
4385 ResourceCode.Source:='{ '+LRSComment+' }';
4386 CodeToolBoss.CreateFile(ChangeFileExt(NewUnitInfo.Filename,'.lfm'));
4387
4388 // clear formeditor
4389 FormEditor1.ClearSelection;
4390
4391 // Figure out where we want to put the new form
4392 // if there is more place left of the OI put it left, otherwise right
4393 if ObjectInspector1<>nil then begin
4394 new_x:=ObjectInspector1.Left+10;
4395 new_y:=ObjectInspector1.Top+10;
4396 end else begin
4397 new_x:=200;
4398 new_y:=100;
4399 end;
4400 if new_x>Screen.Width div 2 then
4401 new_x:=new_x-500
4402 else if ObjectInspector1<>nil then
4403 new_x:=new_x + ObjectInspector1.Width + GetSystemMetrics(SM_CXFRAME) shl 1;
4404 if Assigned(MainIDEBar) then
4405 begin
4406 MainIDEBarBottom:=MainIDEBar.Top+MainIDEBar.Height+GetSystemMetrics(SM_CYFRAME) shl 1
4407 +GetSystemMetrics(SM_CYCAPTION);
4408 if MainIDEBarBottom < Screen.Height div 2 then
4409 new_y:=Max(new_y,MainIDEBarBottom+10);
4410 end;
4411 r:=Screen.PrimaryMonitor.WorkareaRect;
4412 new_x:=Max(r.Left,Min(new_x,r.Right-400));
4413 new_y:=Max(r.Top,Min(new_y,r.Bottom-400));
4414
4415 // create jit component
4416 NewComponent := FormEditor1.CreateComponent(nil,TComponentClass(AncestorType),
4417 NewUnitInfo.CreateUnitName, new_x, new_y, 0,0,DisableAutoSize);
4418 if NewComponent=nil then begin
4419 DebugLn(['CreateNewForm FormEditor1.CreateComponent failed ',dbgsName(TComponentClass(AncestorType))]);
4420 exit(mrCancel);
4421 end;
4422 FormEditor1.SetComponentNameAndClass(NewComponent,
4423 NewUnitInfo.ComponentName,'T'+NewUnitInfo.ComponentName);
4424 if NewComponent is TCustomForm then
4425 TControl(NewComponent).Visible := False;
4426 if (NewComponent is TControl)
4427 and (csSetCaption in TControl(NewComponent).ControlStyle) then
4428 TControl(NewComponent).Caption:=NewComponent.Name;
4429 NewUnitInfo.Component := NewComponent;
4430 MainIDE.CreateDesignerForComponent(NewUnitInfo,NewComponent);
4431 if NewComponent is TCustomDesignControl then
4432 begin
4433 TCustomDesignControl(NewComponent).DesignTimePPI := Screen.PixelsPerInch;
4434 TCustomDesignControl(NewComponent).PixelsPerInch := Screen.PixelsPerInch;
4435 end;
4436
4437 NewUnitInfo.ComponentName:=NewComponent.Name;
4438 NewUnitInfo.ComponentResourceName:=NewUnitInfo.ComponentName;
4439 if UseCreateFormStatements and (NewComponent is TCustomForm)
4440 and NewUnitInfo.IsPartOfProject
4441 and Project1.AutoCreateForms
4442 and (pfMainUnitHasCreateFormStatements in Project1.Flags) then
4443 begin
4444 Project1.AddCreateFormToProjectFile(NewComponent.ClassName,
4445 NewComponent.Name);
4446 end;
4447 Result:=mrOk;
4448 end;
4449
4450 function NewUniqueComponentName(Prefix: string): string;
4451
4452 function SearchProject(AProject: TProject; const Identifier: string): boolean;
4453 var
4454 i: Integer;
4455 AnUnitInfo: TUnitInfo;
4456 begin
4457 if AProject=nil then exit(false);
4458 Result:=true;
4459 for i:=0 to AProject.UnitCount-1 do
4460 begin
4461 AnUnitInfo:=AProject.Units[i];
4462 if (AnUnitInfo.Component<>nil) then begin
4463 if CompareText(AnUnitInfo.Component.Name,Identifier)=0 then exit;
4464 if CompareText(AnUnitInfo.Component.ClassName,Identifier)=0 then exit;
4465 end else if (AnUnitInfo.ComponentName<>'')
4466 and ((AnUnitInfo.IsPartOfProject) or AnUnitInfo.Loaded) then begin
4467 if SysUtils.CompareText(AnUnitInfo.Unit_Name,Identifier)=0 then exit;
4468 if SysUtils.CompareText(AnUnitInfo.ComponentName,Identifier)=0 then exit;
4469 end;
4470 end;
4471 Result:=false;
4472 end;
4473
4474 function SearchPackage(APackage: TLazPackage; const Identifier: string): boolean;
4475 var
4476 i: Integer;
4477 PkgFile: TPkgFile;
4478 begin
4479 if APackage=nil then exit(false);
4480 Result:=true;
4481 if SysUtils.CompareText(APackage.Name,Identifier)=0 then exit;
4482 for i:=0 to APackage.FileCount-1 do
4483 begin
4484 PkgFile:=APackage.Files[i];
4485 if SysUtils.CompareText(PkgFile.Unit_Name,Identifier)=0 then exit;
4486 end;
4487 Result:=false;
4488 end;
4489
4490 function IdentifierExists(Identifier: string): boolean;
4491 var
4492 i: Integer;
4493 begin
4494 Result:=true;
4495 if GetClass(Identifier)<>nil then exit;
4496 if SearchProject(Project1,Identifier) then exit;
4497 for i:=0 to PackageGraph.Count-1 do
4498 if SearchPackage(PackageGraph[i],Identifier) then exit;
4499 Result:=false;
4500 end;
4501
4502 function IdentifierIsOk(Identifier: string): boolean;
4503 begin
4504 Result:=false;
4505 if not IsValidIdent(Identifier) then exit;
4506 if AllKeyWords.DoIdentifier(PChar(Identifier)) then exit;
4507 if IdentifierExists(Identifier) then exit;
4508 if IdentifierExists('T'+Identifier) then exit;
4509 Result:=true;
4510 end;
4511
4512 var
4513 i: Integer;
4514 begin
4515 if IdentifierIsOk(Prefix) then
4516 exit(Prefix);
4517 while (Prefix<>'') and (Prefix[length(Prefix)] in ['0'..'9']) do
4518 System.Delete(Prefix,length(Prefix),1);
4519 if not IsValidIdent(Prefix) then
4520 Prefix:='Resource';
4521 i:=0;
4522 repeat
4523 inc(i);
4524 Result:=Prefix+IntToStr(i);
4525 until IdentifierIsOk(Result);
4526 end;
4527
4528 function ShowSaveFileAsDialog(var AFilename: string; AnUnitInfo: TUnitInfo;
4529 var LFMCode, LRSCode: TCodeBuffer; CanAbort: boolean): TModalResult;
4530 var
4531 SaveDialog: TIDESaveDialog;
4532 SrcEdit: TSourceEditor;
4533 SaveAsFilename, SaveAsFileExt: string;
4534 NewFilename, NewFileExt: string;
4535 OldUnitName, NewUnitName: string;
4536 ACaption, AText, APath: string;
4537 Filter, AllEditorExt, AllFilter: string;
4538 begin
4539 if (AnUnitInfo<>nil) and (AnUnitInfo.OpenEditorInfoCount>0) then
4540 SrcEdit := TSourceEditor(AnUnitInfo.OpenEditorInfo[0].EditorComponent)
4541 else
4542 SrcEdit:=nil;
4543 //debugln('ShowSaveFileAsDialog ',AnUnitInfo.Filename);
4544
4545 // try to keep the old filename and extension
4546 SaveAsFileExt:=ExtractFileExt(AFileName);
4547 if (SaveAsFileExt='') and (SrcEdit<>nil) then begin
4548 if (SrcEdit.SyntaxHighlighterType in [lshFreePascal, lshDelphi]) then
4549 SaveAsFileExt:=PascalExtension[EnvironmentOptions.PascalFileExtension]
4550 else
4551 SaveAsFileExt:=EditorOpts.HighlighterList.GetDefaultFilextension(
4552 SrcEdit.SyntaxHighlighterType);
4553 end;
4554 if FilenameIsPascalSource(AFilename) then begin
4555 if AnUnitInfo<>nil then
4556 OldUnitName:=AnUnitInfo.ReadUnitNameFromSource(false)
4557 else
4558 OldUnitName:=ExtractFileNameOnly(AFilename);
4559 end else
4560 OldUnitName:='';
4561 //debugln('ShowSaveFileAsDialog sourceunitname=',OldUnitName);
4562 SaveAsFilename:=OldUnitName;
4563 if SaveAsFilename='' then
4564 SaveAsFilename:=ExtractFileNameOnly(AFilename);
4565 if SaveAsFilename='' then
4566 SaveAsFilename:=lisnoname;
4567
4568 //suggest lowercased name if user wants so
4569 if EnvironmentOptions.LowercaseDefaultFilename = true then
4570 SaveAsFilename:=LowerCase(SaveAsFilename);
4571
4572 // let user choose a filename
4573 SaveDialog:=IDESaveDialogClass.Create(nil);
4574 try
4575 InputHistories.ApplyFileDialogSettings(SaveDialog);
4576 SaveDialog.Title:=lisSaveSpace+SaveAsFilename+' (*'+SaveAsFileExt+')';
4577 SaveDialog.FileName:=SaveAsFilename+SaveAsFileExt;
4578
4579 Filter := dlgFilterLazarusUnit + ' (*.pas;*.pp)|*.pas;*.pp';
4580 if (SaveAsFileExt='.lpi') then
4581 Filter:=Filter+ '|' + dlgFilterLazarusProject + ' (*.lpi)|*.lpi';
4582 if (SaveAsFileExt='.lfm') or (SaveAsFileExt='.dfm') then
4583 Filter:=Filter+ '|' + dlgFilterLazarusForm + ' (*.lfm;*.dfm)|*.lfm;*.dfm';
4584 if (SaveAsFileExt='.lpk') then
4585 Filter:=Filter+ '|' + dlgFilterLazarusPackage + ' (*.lpk)|*.lpk';
4586 if (SaveAsFileExt='.lpr') then
4587 Filter:=Filter+ '|' + dlgFilterLazarusProjectSource + ' (*.lpr)|*.lpr';
4588 // append a filter for all editor files
4589 CreateFileDialogFilterForSourceEditorFiles(Filter,AllEditorExt,AllFilter);
4590 if AllEditorExt<>'' then
4591 Filter:=Filter+ '|' + dlgFilterLazarusEditorFile + ' (' + AllEditorExt + ')|' + AllEditorExt;
4592
4593 // append an any file filter *.*
4594 Filter:=Filter+ '|' + dlgFilterAll + ' (' + GetAllFilesMask + ')|' + GetAllFilesMask;
4595
4596 // prepend an all filter
4597 Filter:= dlgFilterLazarusFile + ' ('+AllFilter+')|' + AllFilter + '|' + Filter;
4598 SaveDialog.Filter := Filter;
4599
4600 // if this is a project file, start in project directory
4601 if (AnUnitInfo=nil)
4602 or (AnUnitInfo.IsPartOfProject and (not Project1.IsVirtual)
4603 and (not PathIsInPath(SaveDialog.InitialDir,Project1.Directory)))
4604 then begin
4605 SaveDialog.InitialDir:=Project1.Directory;
4606 end;
4607 // if this is a package file, then start in package directory
4608 APath:=PkgBoss.GetDefaultSaveDirectoryForFile(AFilename);
4609 if (APath<>'') and (not PathIsInPath(SaveDialog.InitialDir,APath)) then
4610 SaveDialog.InitialDir:=APath;
4611
4612 repeat
4613 Result:=mrCancel;
4614 // show save dialog
4615 if (not SaveDialog.Execute) or (ExtractFileName(SaveDialog.Filename)='') then
4616 exit; // user cancels
4617 NewFilename:=ExpandFileNameUTF8(SaveDialog.Filename);
4618
4619 // check file extension
4620 NewFileExt:=ExtractFileExt(NewFilename);
4621 if NewFileExt='' then begin
4622 NewFileExt:=SaveAsFileExt;
4623 NewFilename:=NewFilename+SaveAsFileExt;
4624 end;
4625
4626 // check file path
4627 APath:=ExtractFilePath(NewFilename);
4628 if not DirPathExists(APath) then begin
4629 ACaption:=lisEnvOptDlgDirectoryNotFound;
4630 AText:=Format(lisTheDestinationDirectoryDoesNotExist, [LineEnding, APath]);
4631 Result:=IDEMessageDialogAb(ACaption, AText, mtConfirmation,[mbCancel],CanAbort);
4632 exit;
4633 end;
4634
4635 // check unitname
4636 if (NewFileExt<>'') and IsPascalUnitExt(PChar(NewFileExt)) then begin
4637 NewUnitName:=ExtractFileNameOnly(NewFilename);
4638 // Do not rename the unit if new filename differs from its name only in case
4639 if LowerCase(OldUnitName)=NewUnitName then
4640 NewUnitName:=OldUnitName;
4641 if NewUnitName='' then
4642 exit(mrCancel);
4643 // Is it a valid name? Ask user.
4644 if not IsValidUnitName(NewUnitName) then
4645 begin
4646 Result:=IDEQuestionDialogAb(lisInvalidPascalIdentifierCap,
4647 Format(lisInvalidPascalIdentifierName,[NewUnitName,LineEnding]),
4648 mtConfirmation, [mrIgnore, lisSave,
4649 mrCancel, lisCancel,
4650 mrRetry, lisChooseADifferentName,
4651 mrAbort, lisAbort], not CanAbort);
4652 if Result=mrRetry then
4653 continue;
4654 if Result in [mrCancel,mrAbort] then
4655 exit;
4656 end;
4657 // Does the project already have such unit?
4658 if Project1.IndexOfUnitWithName(NewUnitName,true,AnUnitInfo)>=0 then
4659 begin
4660 Result:=IDEQuestionDialogAb(lisUnitNameAlreadyExistsCap,
4661 Format(lisTheUnitAlreadyExists, [NewUnitName]),
4662 mtConfirmation, [mrIgnore, lisForceRenaming,
4663 mrCancel, lisCancelRenaming,
4664 mrAbort, lisAbort], not CanAbort);
4665 if Result<>mrIgnore then
4666 exit;
4667 end;
4668 end;
4669 until Result<>mrRetry;
4670 finally
4671 InputHistories.StoreFileDialogSettings(SaveDialog);
4672 SaveDialog.Free;
4673 end;
4674
4675 // check filename
4676 if FilenameIsPascalUnit(NewFilename) then begin
4677 AText:=ExtractFileName(NewFilename);
4678 // check if file should be auto renamed
4679 if EnvironmentOptions.CharcaseFileAction = ccfaAsk then begin
4680 if LowerCase(AText)<>AText then begin
4681 Result:=IDEQuestionDialogAb(lisRenameFile,
4682 Format(lisThisLooksLikeAPascalFileItIsRecommendedToUseLowerC,
4683 [LineEnding, LineEnding]),
4684 mtWarning, [mrYes, lisRenameToLowercase,
4685 mrNo, lisKeepName,
4686 mrAbort, lisAbort], not CanAbort);
4687 case Result of
4688 mrYes: NewFileName:=ExtractFilePath(NewFilename)+lowercase(AText);
4689 mrAbort, mrCancel: exit;
4690 end;
4691 Result:=mrOk;
4692 end;
4693 end else begin
4694 if EnvironmentOptions.CharcaseFileAction = ccfaAutoRename then
4695 NewFileName:=ExtractFilePath(NewFilename)+LowerCase(AText);
4696 end;
4697 end;
4698
4699 // check overwrite existing file
4700 if IDESaveDialogClass.NeedOverwritePrompt
4701 and ((not FilenameIsAbsolute(AFilename))
4702 or (CompareFilenames(NewFilename,AFilename)<>0))
4703 and FileExistsUTF8(NewFilename) then
4704 begin
4705 ACaption:=lisOverwriteFile;
4706 AText:=Format(lisAFileAlreadyExistsReplaceIt, [NewFilename, LineEnding]);
4707 Result:=IDEQuestionDialogAb(ACaption, AText, mtConfirmation,
4708 [mrYes, lisOverwriteFileOnDisk,
4709 mrCancel,
4710 mrAbort, lisAbort], not CanAbort);
4711 if Result=mrCancel then exit;
4712 end;
4713
4714 if AnUnitInfo<>nil then begin
4715 // rename unit
4716 Result:=RenameUnit(AnUnitInfo,NewFilename,NewUnitName,LFMCode,LRSCode);
4717 AFilename:=AnUnitInfo.Filename;
4718 if Result<>mrOk then exit;
4719 end else begin
4720 Result:=mrOk;
4721 AFilename:=NewFilename;
4722 end;
4723 end;
4724
4725 type
4726 TTranslateStringItem = record
4727 Name: String;
4728 Value: String;
4729 end;
4730
4731 TTranslateStrings = class
4732 private
4733 FList: array of TTranslateStringItem;
4734 function CalcHash(const S: string): Cardinal;
4735 function GetSourceBytes(const S: string): string;
4736 function GetValue(const S: string): string;
4737 public
4738 destructor Destroy; override;
4739 procedure Add(const AName, AValue: String);
4740 function Count: Integer;
4741 function Text: String;
4742 end;
4743
4744 TLRJGrubber = class(TObject)
4745 private
4746 FGrubbed: TTranslateStrings;
4747 FWriter: TWriter;
4748 public
4749 constructor Create(TheWriter: TWriter);
4750 destructor Destroy; override;
4751 procedure Grub(Sender: TObject; const Instance: TPersistent;
4752 PropInfo: PPropInfo; var Content: string);
4753 property Grubbed: TTranslateStrings read FGrubbed;
4754 property Writer: TWriter read FWriter write FWriter;
4755 end;
4756
CalcHashnull4757 function TTranslateStrings.CalcHash(const S: string): Cardinal;
4758 var
4759 g: Cardinal;
4760 i: Longint;
4761 begin
4762 Result:=0;
4763 for i:=1 to Length(s) do
4764 begin
4765 Result:=Result shl 4;
4766 inc(Result,Ord(S[i]));
4767 g:=Result and ($f shl 28);
4768 if g<>0 then
4769 begin
4770 Result:=Result xor (g shr 24);
4771 Result:=Result xor g;
4772 end;
4773 end;
4774 If Result=0 then
4775 Result:=$ffffffff;
4776 end;
4777
GetSourceBytesnull4778 function TTranslateStrings.GetSourceBytes(const S: string): string;
4779 var
4780 i, l: Integer;
4781 begin
4782 Result:='';
4783 l:=Length(S);
4784 for i:=1 to l do
4785 begin
4786 Result:=Result+IntToStr(Ord(S[i]));
4787 if i<>l then
4788 Result:=Result+',';
4789 end;
4790 end;
4791
GetValuenull4792 function TTranslateStrings.GetValue(const S: string): string;
4793 var
4794 i, l: Integer;
4795 jsonstr: unicodestring;
4796 begin
4797 Result:='';
4798 //input string is assumed to be in UTF-8 encoding
4799 jsonstr:=UTF8ToUTF16(StringToJSONString(S));
4800 l:=Length(jsonstr);
4801 for i:=1 to l do
4802 begin
4803 if (Ord(jsonstr[i])<32) or (Ord(jsonstr[i])>=127) then
4804 Result:=Result+'\u'+HexStr(Ord(jsonstr[i]), 4)
4805 else
4806 Result:=Result+Char(jsonstr[i]);
4807 end;
4808 end;
4809
4810 destructor TTranslateStrings.Destroy;
4811 begin
4812 SetLength(FList,0);
4813 end;
4814
4815 procedure TTranslateStrings.Add(const AName, AValue: String);
4816 begin
4817 SetLength(FList,Length(FList)+1);
4818 with FList[High(FList)] do
4819 begin
4820 Name:=AName;
4821 Value:=AValue;
4822 end;
4823 end;
4824
Countnull4825 function TTranslateStrings.Count: Integer;
4826 begin
4827 Result:=Length(FList);
4828 end;
4829
Textnull4830 function TTranslateStrings.Text: String;
4831 var
4832 i: Integer;
4833 R: TTranslateStringItem;
4834 begin
4835 Result:='';
4836 if Length(FList)=0 then Exit;
4837 Result:='{"version":1,"strings":['+LineEnding;
4838 for i:=Low(FList) to High(FList) do
4839 begin
4840 R:=TTranslateStringItem(FList[i]);
4841 Result:=Result+'{"hash":'+IntToStr(CalcHash(R.Value))+',"name":"'+R.Name+
4842 '","sourcebytes":['+GetSourceBytes(R.Value)+
4843 '],"value":"'+GetValue(R.Value)+'"}';
4844 if i<High(FList) then
4845 Result:=Result+','+LineEnding
4846 else
4847 Result:=Result+LineEnding;
4848 end;
4849 Result:=Result+']}'+LineEnding;
4850 end;
4851
4852 constructor TLRJGrubber.Create(TheWriter: TWriter);
4853 begin
4854 inherited Create;
4855 FGrubbed:=TTranslateStrings.Create;
4856 FWriter:=TheWriter;
4857 FWriter.OnWriteStringProperty:=@Grub;
4858 end;
4859
4860 destructor TLRJGrubber.Destroy;
4861 begin
4862 FGrubbed.Free;
4863 inherited Destroy;
4864 end;
4865
4866 procedure TLRJGrubber.Grub(Sender: TObject; const Instance: TPersistent;
4867 PropInfo: PPropInfo; var Content: string);
4868 var
4869 LRSWriter: TLRSObjectWriter;
4870 Path: String;
4871 begin
4872 if not Assigned(Instance) then exit;
4873 if not Assigned(PropInfo) then exit;
4874 if SysUtils.CompareText(PropInfo^.PropType^.Name,'TTRANSLATESTRING')<>0 then exit;
4875 if (SysUtils.CompareText(Instance.ClassName,'TMENUITEM')=0) and (Content='-') then exit;
4876 if Writer.Driver is TLRSObjectWriter then begin
4877 LRSWriter:=TLRSObjectWriter(Writer.Driver);
4878 Path:=LRSWriter.GetStackPath;
4879 end else begin
4880 Path:=Instance.ClassName+'.'+PropInfo^.Name;
4881 end;
4882 FGrubbed.Add(LowerCase(Path),Content);
4883 end;
4884
4885 function SaveUnitComponent(AnUnitInfo: TUnitInfo;
4886 LRSCode, LFMCode: TCodeBuffer; Flags: TSaveFlags): TModalResult;
4887
4888 function IsI18NEnabled(UnitOwners: TFPList): boolean;
4889 var
4890 i: Integer;
4891 APackage: TLazPackage;
4892 PkgFile: TPkgFile;
4893 begin
4894 if AnUnitInfo.IsPartOfProject then begin
4895 // a project unit
4896 Result:=AnUnitInfo.Project.EnableI18N and AnUnitInfo.Project.EnableI18NForLFM
4897 and (not AnUnitInfo.DisableI18NForLFM);
4898 exit;
4899 end;
4900 if (UnitOwners<>nil) then begin
4901 for i:=0 to UnitOwners.Count-1 do begin
4902 if TObject(UnitOwners[i]) is TLazPackage then begin
4903 // a package unit
4904 APackage:=TLazPackage(UnitOwners[i]);
4905 Result:=false;
4906 if APackage.EnableI18N and APackage.EnableI18NForLFM then begin
4907 PkgFile:=APackage.FindPkgFile(AnUnitInfo.Filename,true,true);
4908 Result:=(PkgFile<>nil) and (not PkgFile.DisableI18NForLFM);
4909 end;
4910 exit;
4911 end;
4912 end;
4913 end;
4914 // a rogue unit
4915 Result:=false;
4916 end;
4917
4918 var
4919 ComponentSavingOk: boolean;
4920 MemStream, BinCompStream, TxtCompStream: TExtMemoryStream;
4921 DestroyDriver: Boolean;
4922 Writer: TWriter;
4923 ACaption, AText: string;
4924 CompResourceCode, LFMFilename, TestFilename: string;
4925 ADesigner: TIDesigner;
4926 Grubber: TLRJGrubber;
4927 LRJFilename: String;
4928 AncestorUnit: TUnitInfo;
4929 Ancestor: TComponent;
4930 HasI18N: Boolean;
4931 UnitOwners: TFPList;
4932 LRSFilename: String;
4933 PropPath: String;
4934 ResType: TResourceType;
4935 begin
4936 Result:=mrCancel;
4937
4938 // save lrs - lazarus resource file and lfm - lazarus form text file
4939 // Note: When there is a bug in the source, the include directive of the
4940 // resource code can not be found, therefore the LFM file should always
4941 // be saved first.
4942 // And therefore each TUnitInfo stores the resource filename (.lrs).
4943
4944 // the lfm file is saved before the lrs file, because the IDE only needs the
4945 // lfm file to recreate the lrs file.
4946 // by VVI - now a LRT file is saved in addition to LFM and LRS
4947 // LRT file format (in present) are lines
4948 // <ClassName>.<PropertyName>=<PropertyValue>
4949 LRSFilename:='';
4950 ResType:=MainBuildBoss.GetResourceType(AnUnitInfo);
4951 LRSCode:=nil;
4952
4953 if (AnUnitInfo.Component<>nil) then begin
4954 // stream component to resource code and to lfm file
4955 ComponentSavingOk:=true;
4956
4957 // clean up component
4958 Result:=RemoveLooseEvents(AnUnitInfo);
4959 if Result<>mrOk then exit;
4960
4961 // save designer form properties to the component
4962 FormEditor1.SaveHiddenDesignerFormProperties(AnUnitInfo.Component);
4963
4964 if ResType=rtLRS then begin
4965 if (sfSaveToTestDir in Flags) then
4966 LRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo)
4967 else
4968 LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,true);
4969 end;
4970
4971 // stream component to binary stream
4972 BinCompStream:=TExtMemoryStream.Create;
4973 if AnUnitInfo.ComponentLastBinStreamSize>0 then
4974 BinCompStream.Capacity:=AnUnitInfo.ComponentLastBinStreamSize+LRSStreamChunkSize;
4975 Writer:=nil;
4976 DestroyDriver:=false;
4977 Grubber:=nil;
4978 UnitOwners:=nil;
4979 try
4980 UnitOwners:=PkgBoss.GetOwnersOfUnit(AnUnitInfo.Filename);
4981 Result:=mrOk;
4982 repeat
4983 try
4984 BinCompStream.Position:=0;
4985 Writer:=AnUnitInfo.UnitResourceFileformat.CreateWriter(BinCompStream,DestroyDriver);
4986 // used to save lrj files
4987 HasI18N:=IsI18NEnabled(UnitOwners);
4988 if HasI18N then
4989 Grubber:=TLRJGrubber.Create(Writer);
4990 Writer.OnWriteMethodProperty:=@FormEditor1.WriteMethodPropertyEvent;
4991 //DebugLn(['SaveUnitComponent AncestorInstance=',dbgsName(AncestorInstance)]);
4992 Writer.OnFindAncestor:=@FormEditor1.WriterFindAncestor;
4993 AncestorUnit:=AnUnitInfo.FindAncestorUnit;
4994 Ancestor:=nil;
4995 if AncestorUnit<>nil then
4996 Ancestor:=AncestorUnit.Component;
4997 //DebugLn(['SaveUnitComponent Writer.WriteDescendent ARoot=',AnUnitInfo.Component,' Ancestor=',DbgSName(Ancestor)]);
4998 Writer.WriteDescendent(AnUnitInfo.Component,Ancestor);
4999 if DestroyDriver then
5000 Writer.Driver.Free;
5001 FreeAndNil(Writer);
5002 AnUnitInfo.ComponentLastBinStreamSize:=BinCompStream.Size;
5003 except
5004 on E: Exception do begin
5005 PropPath:='';
5006 if Writer.Driver is TLRSObjectWriter then
5007 PropPath:=TLRSObjectWriter(Writer.Driver).GetStackPath;
5008 DumpExceptionBackTrace;
5009 ACaption:=lisStreamingError;
5010 AText:=Format(lisUnableToStreamT, [AnUnitInfo.ComponentName,
5011 AnUnitInfo.ComponentName]) + LineEnding + E.Message;
5012 if PropPath<>'' then
5013 AText := Atext + LineEnding + LineEnding + lisPathToInstance
5014 + LineEnding + PropPath;
5015 Result:=IDEMessageDialog(ACaption, AText, mtError,
5016 [mbAbort, mbRetry, mbIgnore]);
5017 if Result=mrAbort then exit;
5018 if Result=mrIgnore then Result:=mrOk;
5019 ComponentSavingOk:=false;
5020 end;
5021 end;
5022 until Result<>mrRetry;
5023
5024 // create lazarus form resource code
5025 if ComponentSavingOk and (LRSFilename<>'') then begin
5026 if LRSCode=nil then begin
5027 LRSCode:=CodeToolBoss.CreateFile(LRSFilename);
5028 ComponentSavingOk:=(LRSCode<>nil);
5029 end;
5030 if ComponentSavingOk then begin
5031 // there is no bug in the source, so the resource code should be changed too
5032 MemStream:=TExtMemoryStream.Create;
5033 if AnUnitInfo.ComponentLastLRSStreamSize>0 then
5034 MemStream.Capacity:=AnUnitInfo.ComponentLastLRSStreamSize+LRSStreamChunkSize;
5035 try
5036 BinCompStream.Position:=0;
5037 BinaryToLazarusResourceCode(BinCompStream,MemStream
5038 ,'T'+AnUnitInfo.ComponentName,'FORMDATA');
5039 AnUnitInfo.ComponentLastLRSStreamSize:=MemStream.Size;
5040 MemStream.Position:=0;
5041 SetLength(CompResourceCode,MemStream.Size);
5042 MemStream.Read(CompResourceCode[1],length(CompResourceCode));
5043 finally
5044 MemStream.Free;
5045 end;
5046 end;
5047 if ComponentSavingOk then begin
5048 {$IFDEF IDE_DEBUG}
5049 debugln('SaveUnitComponent E ',CompResourceCode);
5050 {$ENDIF}
5051 // replace lazarus form resource code in include file (.lrs)
5052 if not (sfSaveToTestDir in Flags) then begin
5053 // if resource name has changed, delete old resource
5054 if (AnUnitInfo.ComponentName<>AnUnitInfo.ComponentResourceName)
5055 and (AnUnitInfo.ComponentResourceName<>'') then begin
5056 CodeToolBoss.RemoveLazarusResource(LRSCode,
5057 'T'+AnUnitInfo.ComponentResourceName);
5058 end;
5059 // add comment to resource file (if not already exists)
5060 if (not CodeToolBoss.AddLazarusResourceHeaderComment(LRSCode,LRSComment)) then
5061 begin
5062 ACaption:=lisResourceSaveError;
5063 AText:=Format(lisUnableToAddResourceHeaderCommentToResourceFile, [
5064 LineEnding, LRSCode.FileName, LineEnding]);
5065 Result:=IDEMessageDialog(ACaption,AText,mtError,[mbIgnore,mbAbort]);
5066 if Result<>mrIgnore then exit;
5067 end;
5068 // add resource to resource file
5069 if (not CodeToolBoss.AddLazarusResource(LRSCode,
5070 'T'+AnUnitInfo.ComponentName,CompResourceCode)) then
5071 begin
5072 ACaption:=lisResourceSaveError;
5073 AText:=Format(lisUnableToAddResourceTFORMDATAToResourceFileProbably,
5074 [AnUnitInfo.ComponentName, LineEnding, LRSCode.FileName, LineEnding] );
5075 Result:=IDEMessageDialog(ACaption, AText, mtError, [mbIgnore, mbAbort]);
5076 if Result<>mrIgnore then exit;
5077 end else begin
5078 AnUnitInfo.ComponentResourceName:=AnUnitInfo.ComponentName;
5079 end;
5080 end else begin
5081 LRSCode.Source:=CompResourceCode;
5082 end;
5083 end;
5084 end;
5085 if ComponentSavingOk then begin
5086 if (not AnUnitInfo.IsVirtual) or (sfSaveToTestDir in Flags) then
5087 begin
5088 // save lfm file
5089 LFMFilename:=AnUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(AnUnitInfo.Filename,false);
5090 if AnUnitInfo.IsVirtual then
5091 LFMFilename:=AppendPathDelim(MainBuildBoss.GetTestBuildDirectory)+LFMFilename;
5092 if LFMCode=nil then begin
5093 LFMCode:=CodeToolBoss.CreateFile(LFMFilename);
5094 if LFMCode=nil then begin
5095 Result:=IDEQuestionDialog(lisUnableToCreateFile,
5096 Format(lisUnableToCreateFile2, [LFMFilename]),
5097 mtWarning, [mrIgnore, lisContinueWithoutLoadingForm,
5098 mrCancel, lisCancelLoadingUnit,
5099 mrAbort, lisAbortAllLoading]);
5100 if Result<>mrIgnore then exit;
5101 end;
5102 end;
5103 if (LFMCode<>nil) then begin
5104 {$IFDEF IDE_DEBUG}
5105 debugln('SaveUnitComponent E2 LFM=',LFMCode.Filename);
5106 {$ENDIF}
5107 if (ResType=rtRes) and (LFMCode.DiskEncoding<>EncodingUTF8) then
5108 begin
5109 // the .lfm file is used by fpcres, which only supports UTF8 without BOM
5110 DebugLn(['SaveUnitComponent fixing encoding of ',LFMCode.Filename,' from ',LFMCode.DiskEncoding,' to ',EncodingUTF8]);
5111 LFMCode.DiskEncoding:=EncodingUTF8;
5112 end;
5113
5114 Result:=mrOk;
5115 repeat
5116 try
5117 // transform binary to text
5118 TxtCompStream:=TExtMemoryStream.Create;
5119 if AnUnitInfo.ComponentLastLFMStreamSize>0 then
5120 TxtCompStream.Capacity:=AnUnitInfo.ComponentLastLFMStreamSize
5121 +LRSStreamChunkSize;
5122 try
5123 BinCompStream.Position:=0;
5124 AnUnitInfo.UnitResourceFileformat.BinStreamToTextStream(BinCompStream,TxtCompStream);
5125 AnUnitInfo.ComponentLastLFMStreamSize:=TxtCompStream.Size;
5126 // stream text to file
5127 TxtCompStream.Position:=0;
5128 LFMCode.LoadFromStream(TxtCompStream);
5129 Result:=SaveCodeBufferToFile(LFMCode,LFMCode.Filename,true);
5130 if not Result=mrOk then exit;
5131 Result:=mrCancel;
5132 finally
5133 TxtCompStream.Free;
5134 end;
5135 except
5136 on E: Exception do begin
5137 // added to get more feedback on issue 7009
5138 Debugln('SaveFileResources E3: ', E.Message);
5139 DumpExceptionBackTrace;
5140 ACaption:=lisStreamingError;
5141 AText:=Format(
5142 lisUnableToTransformBinaryComponentStreamOfTIntoText, [
5143 AnUnitInfo.ComponentName, AnUnitInfo.ComponentName])
5144 +LineEnding+E.Message;
5145 Result:=IDEMessageDialog(ACaption, AText, mtError,
5146 [mbAbort, mbRetry, mbIgnore]);
5147 if Result=mrAbort then exit;
5148 if Result=mrIgnore then Result:=mrOk;
5149 end;
5150 end;
5151 until Result<>mrRetry;
5152 end;
5153 end;
5154 end;
5155 // Now the most important file (.lfm) is saved.
5156 // Now save the secondary files
5157
5158 // save the .lrj file containing the list of all translatable strings of
5159 // the component
5160 if ComponentSavingOk
5161 and (Grubber<>nil) and (Grubber.Grubbed.Count>0)
5162 and (not (sfSaveToTestDir in Flags))
5163 and (not AnUnitInfo.IsVirtual) then begin
5164 LRJFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lrj');
5165 DebugLn(['SaveUnitComponent save lrj: ',LRJFilename]);
5166 Result:=SaveStringToFile(LRJFilename,Grubber.Grubbed.Text,
5167 [mbIgnore,mbAbort],AnUnitInfo.Filename);
5168 if (Result<>mrOk) and (Result<>mrIgnore) then exit;
5169 end;
5170
5171 finally
5172 try
5173 FreeAndNil(BinCompStream);
5174 if DestroyDriver and (Writer<>nil) then Writer.Driver.Free;
5175 FreeAndNil(Writer);
5176 FreeAndNil(Grubber);
5177 FreeAndNil(UnitOwners);
5178 except
5179 on E: Exception do begin
5180 debugln('SaveUnitComponent Error cleaning up: ',E.Message);
5181 end;
5182 end;
5183 end;
5184 end;
5185 {$IFDEF IDE_DEBUG}
5186 if ResourceCode<>nil then
5187 debugln('SaveUnitComponent F ',ResourceCode.Modified);
5188 {$ENDIF}
5189 // save binary stream (.lrs)
5190 if LRSCode<>nil then begin
5191 if (not (sfSaveToTestDir in Flags)) then
5192 begin
5193 if (LRSCode.Modified) then begin
5194 if FilenameIsAbsolute(LRSCode.Filename) then
5195 LRSFilename:=LRSCode.Filename
5196 else if LRSFilename='' then
5197 LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,true);
5198 if (LRSFilename<>'') and FilenameIsAbsolute(LRSFilename) then
5199 begin
5200 Result:=ForceDirectoryInteractive(ExtractFilePath(LRSFilename),[mbRetry]);
5201 if not Result=mrOk then exit;
5202 Result:=SaveCodeBufferToFile(LRSCode,LRSFilename);
5203 if not Result=mrOk then exit;
5204 end;
5205 end;
5206 end else begin
5207 TestFilename:=MainBuildBoss.GetTestUnitFilename(AnUnitInfo);
5208 LRSFilename:=ChangeFileExt(TestFilename,ExtractFileExt(LRSCode.Filename));
5209 Result:=SaveCodeBufferToFile(LRSCode,LRSFilename);
5210 if not Result=mrOk then exit;
5211 end;
5212 end;
5213 // mark designer unmodified
5214 ADesigner:=FindRootDesigner(AnUnitInfo.Component);
5215 if ADesigner<>nil then
5216 ADesigner.DefaultFormBoundsValid:=false;
5217
5218 Result:=mrOk;
5219 {$IFDEF IDE_DEBUG}
5220 debugln('SaveUnitComponent G ',LFMCode<>nil);
5221 {$ENDIF}
5222 end;
5223
5224 function RemoveLooseEvents(AnUnitInfo: TUnitInfo): TModalResult;
5225 var
5226 ComponentModified: boolean;
5227 ActiveSrcEdit: TSourceEditor;
5228 ActiveUnitInfo: TUnitInfo;
5229 begin
5230 Result:=mrOk;
5231 if (AnUnitInfo.Component=nil) then exit;
5232 ActiveSrcEdit:=nil;
5233 if not MainIDE.BeginCodeTool(ActiveSrcEdit,ActiveUnitInfo,[]) then exit;
5234 // unselect methods in ObjectInspector1
5235 if (ObjectInspector1<>nil)
5236 and (ObjectInspector1.PropertyEditorHook.LookupRoot=AnUnitInfo.Component) then
5237 begin
5238 ObjectInspector1.EventGrid.ItemIndex:=-1;
5239 ObjectInspector1.FavoriteGrid.ItemIndex:=-1;
5240 end;
5241 //debugln('RemoveLooseEvents ',AnUnitInfo.Filename,' ',dbgsName(AnUnitInfo.Component));
5242 // remove dangling methods
5243 Result:=RemoveDanglingEvents(AnUnitInfo.Component, AnUnitInfo.Source, True,
5244 ComponentModified);
5245 // update ObjectInspector1
5246 if ComponentModified
5247 and (ObjectInspector1<>nil)
5248 and (ObjectInspector1.PropertyEditorHook.LookupRoot=AnUnitInfo.Component) then
5249 begin
5250 ObjectInspector1.EventGrid.RefreshPropertyValues;
5251 ObjectInspector1.FavoriteGrid.RefreshPropertyValues;
5252 end;
5253 end;
5254
5255 function RenameUnit(AnUnitInfo: TUnitInfo; NewFilename, NewUnitName: string;
5256 var LFMCode, LRSCode: TCodeBuffer): TModalResult;
5257 var
5258 NewLFMFilename: String;
5259 OldSourceCode: String;
5260 NewSource: TCodeBuffer;
5261 NewFilePath: String;
5262 NewLRSFilePath: String;
5263 OldFilePath: String;
5264 OldLRSFilePath: String;
5265 OldFilename: String;
5266 NewLRSFilename: String;
5267 NewHighlighter: TLazSyntaxHighlighter;
5268 AmbiguousFiles: TStringList;
5269 AmbiguousText: string;
5270 i: Integer;
5271 AmbiguousFilename: String;
5272 OldUnitPath: String;
5273 OldLFMFilename: String;
5274 OldLRSFilename: String;
5275 OldPPUFilename: String;
5276 OutDir: string;
5277 Owners: TFPList;
5278 OldFileExisted: Boolean;
5279 ConvTool: TConvDelphiCodeTool;
5280 begin
5281 // Project is marked as changed already here. ToDo: Mark changed only if really renamed.
5282 Project1.BeginUpdate(true);
5283 try
5284 OldFilename:=AnUnitInfo.Filename;
5285 OldFilePath:=ExtractFilePath(OldFilename);
5286 OldLFMFilename:='';
5287 // ToDo: use UnitResources
5288 if FilenameIsPascalUnit(OldFilename) then begin
5289 OldLFMFilename:=ChangeFileExt(OldFilename,'.lfm');
5290 if not FileExistsUTF8(OldLFMFilename) then
5291 OldLFMFilename:=ChangeFileExt(OldFilename,'.dfm');
5292 end;
5293 if NewUnitName='' then
5294 NewUnitName:=AnUnitInfo.Unit_Name;
5295 debugln(['RenameUnit ',AnUnitInfo.Filename,' NewUnitName=',NewUnitName,' OldUnitName=',AnUnitInfo.Unit_Name,' LFMCode=',LFMCode<>nil,' LRSCode=',LRSCode<>nil,' NewFilename="',NewFilename,'"']);
5296
5297 // check new resource file
5298 NewLFMFilename:='';
5299 if FilenameIsPascalUnit(NewFilename) then
5300 NewLFMFilename:=ChangeFileExt(NewFilename,'.lfm');
5301 if AnUnitInfo.ComponentName='' then begin
5302 // unit has no component
5303 // -> remove lfm file, so that it will not be auto loaded on next open
5304 if (FileExistsUTF8(NewLFMFilename))
5305 and (not DeleteFileUTF8(NewLFMFilename))
5306 and (IDEMessageDialog(lisPkgMangDeleteFailed,
5307 Format(lisDeletingOfFileFailed, [NewLFMFilename]),
5308 mtError, [mbIgnore, mbCancel])=mrCancel)
5309 then
5310 exit(mrCancel);
5311 end;
5312
5313 // create new source with the new filename
5314 OldSourceCode:=AnUnitInfo.Source.Source;
5315 NewSource:=CodeToolBoss.CreateFile(NewFilename);
5316 if NewSource=nil then begin
5317 Result:=IDEMessageDialog(lisUnableToCreateFile,
5318 Format(lisCanNotCreateFile, [NewFilename]),
5319 mtError,[mbCancel,mbAbort]);
5320 exit;
5321 end;
5322 NewSource.Source:=OldSourceCode;
5323 if (AnUnitInfo.Source.DiskEncoding<>'') and (AnUnitInfo.Source.DiskEncoding<>EncodingUTF8)
5324 then begin
5325 NewSource.DiskEncoding:=AnUnitInfo.Source.DiskEncoding;
5326 InputHistories.FileEncodings[NewFilename]:=NewSource.DiskEncoding;
5327 end else
5328 InputHistories.FileEncodings.Remove(NewFilename);
5329
5330 // get final filename
5331 NewFilename:=NewSource.Filename;
5332 NewFilePath:=ExtractFilePath(NewFilename);
5333 EnvironmentOptions.RemoveFromRecentOpenFiles(OldFilename);
5334 EnvironmentOptions.AddToRecentOpenFiles(NewFilename);
5335 MainIDE.SetRecentFilesMenu;
5336
5337 // add new path to unit path
5338 if AnUnitInfo.IsPartOfProject
5339 and (FilenameIsPascalUnit(NewFilename))
5340 and (CompareFilenames(NewFilePath,Project1.Directory)<>0) then begin
5341 OldUnitPath:=Project1.CompilerOptions.GetUnitPath(false);
5342 if SearchDirectoryInSearchPath(OldUnitPath,NewFilePath,1)<1 then
5343 AddPathToBuildModes(NewFilePath, False);
5344 end;
5345
5346 // rename lfm file
5347 if FilenameIsAbsolute(NewLFMFilename) then begin
5348 if (LFMCode=nil)
5349 and (OldLFMFilename<>'')
5350 and FilenameIsAbsolute(OldLFMFilename) and FileExistsUTF8(OldLFMFilename) then
5351 LFMCode:=CodeToolBoss.LoadFile(OldLFMFilename,false,false);
5352 if (LFMCode<>nil) then begin
5353 Result:=SaveCodeBufferToFile(LFMCode,NewLFMFilename,true);
5354 if not (Result in [mrOk,mrIgnore]) then begin
5355 DebugLn(['RenameUnit SaveCodeBufferToFile failed for "',NewLFMFilename,'"']);
5356 exit;
5357 end;
5358 LFMCode:=CodeToolBoss.LoadFile(NewLFMFilename,true,false);
5359 if LFMCode<>nil then
5360 NewLFMFilename:=LFMCode.Filename;
5361 ConvTool:=TConvDelphiCodeTool.Create(NewSource);
5362 try
5363 if not ConvTool.RenameResourceDirectives then
5364 debugln(['RenameUnit WARNING: unable to rename resource directive in "',NewSource.Filename,'"']);
5365 finally
5366 ConvTool.Free;
5367 end;
5368 end;
5369 end;
5370
5371 // rename Resource file (.lrs)
5372 if (LRSCode<>nil) then begin
5373 // the resource include line in the code will be changed later after
5374 // changing the unitname
5375 if AnUnitInfo.IsPartOfProject
5376 and (not Project1.IsVirtual)
5377 and (pfLRSFilesInOutputDirectory in Project1.Flags) then begin
5378 NewLRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo);
5379 NewLRSFilename:=AppendPathDelim(ExtractFilePath(NewLRSFilename))
5380 +ExtractFileNameOnly(NewFilename)+ResourceFileExt;
5381 end else begin
5382 OldLRSFilePath:=ExtractFilePath(LRSCode.Filename);
5383 NewLRSFilePath:=OldLRSFilePath;
5384 if FilenameIsAbsolute(OldFilePath)
5385 and PathIsInPath(OldLRSFilePath,OldFilePath) then begin
5386 // resource code was in the same or in a sub directory of source
5387 // -> try to keep this relationship
5388 NewLRSFilePath:=NewFilePath
5389 +copy(LRSCode.Filename,length(OldFilePath)+1,length(LRSCode.Filename));
5390 if not DirPathExists(NewLRSFilePath) then
5391 NewLRSFilePath:=NewFilePath;
5392 end else begin
5393 // resource code was not in the same or in a sub directory of source
5394 // copy resource into the same directory as the source
5395 NewLRSFilePath:=NewFilePath;
5396 end;
5397 NewLRSFilename:=NewLRSFilePath+ExtractFileNameOnly(NewFilename)+ResourceFileExt;
5398 end;
5399 Result:=ForceDirectoryInteractive(ExtractFilePath(NewLRSFilename),[mbRetry,mbIgnore]);
5400 if Result=mrCancel then exit;
5401 if Result=mrOk then begin
5402 if not CodeToolBoss.SaveBufferAs(LRSCode,NewLRSFilename,LRSCode) then
5403 DebugLn(['RenameUnit CodeToolBoss.SaveBufferAs failed: NewResFilename="',NewLRSFilename,'"']);
5404 end;
5405
5406 {$IFDEF IDE_DEBUG}
5407 debugln(['RenameUnit C ',ResourceCode<>nil]);
5408 debugln([' NewResFilePath="',NewResFilePath,'" NewResFilename="',NewResFilename,'"']);
5409 if ResourceCode<>nil then debugln('*** ResourceFileName ',ResourceCode.Filename);
5410 {$ENDIF}
5411 end else begin
5412 NewLRSFilename:='';
5413 end;
5414 // rename unit name of jit class
5415 if (AnUnitInfo.Component<>nil) then
5416 FormEditor1.RenameJITComponentUnitname(AnUnitInfo.Component,NewUnitName);
5417 {$IFDEF IDE_DEBUG}
5418 if AnUnitInfo.Component<>nil then debugln('*** AnUnitInfo.Component ',dbgsName(AnUnitInfo.Component),' ClassUnitname=',GetClassUnitName(AnUnitInfo.Component.ClassType));
5419 debugln(['RenameUnit D ',ResourceCode<>nil]);
5420 {$ENDIF}
5421
5422 // set new codebuffer in unitinfo and sourceeditor
5423 AnUnitInfo.Source:=NewSource;
5424 if AnUnitInfo.IsPartOfProject then
5425 Project1.Modified:=true
5426 else
5427 Project1.SessionModified:=true;
5428 AnUnitInfo.ClearModifieds;
5429 for i := 0 to AnUnitInfo.EditorInfoCount -1 do
5430 if AnUnitInfo.EditorInfo[i].EditorComponent <> nil then
5431 TSourceEditor(AnUnitInfo.EditorInfo[i].EditorComponent).CodeBuffer := NewSource;
5432 // the code is not changed, therefore the marks are kept
5433
5434 // change unitname in lpi and in main source file
5435 AnUnitInfo.Unit_Name:=NewUnitName;
5436 if LRSCode<>nil then begin
5437 // change resource filename in the source include directive
5438 if not CodeToolBoss.RenameMainInclude(AnUnitInfo.Source,
5439 ExtractFilename(LRSCode.Filename),false)
5440 then
5441 DebugLn(['RenameUnit CodeToolBoss.RenameMainInclude failed: AnUnitInfo.Source="',AnUnitInfo.Source,'" ResourceCode="',ExtractFilename(LRSCode.Filename),'"']);
5442 end;
5443
5444 // change unitname on SourceNotebook
5445 if AnUnitInfo.OpenEditorInfoCount > 0 then
5446 UpdateSourceNames;
5447
5448 // change syntax highlighter
5449 NewHighlighter:=FilenameToLazSyntaxHighlighter(NewFilename);
5450 AnUnitInfo.UpdateDefaultHighlighter(NewHighlighter);
5451 for i := 0 to AnUnitInfo.EditorInfoCount - 1 do
5452 if (AnUnitInfo.EditorInfo[i].EditorComponent <> nil) and
5453 (not AnUnitInfo.EditorInfo[i].CustomHighlighter)
5454 then
5455 TSourceEditor(AnUnitInfo.EditorInfo[i].EditorComponent).SyntaxHighlighterType :=
5456 AnUnitInfo.EditorInfo[i].SyntaxHighlighter;
5457
5458 // save file
5459 if not NewSource.IsVirtual then begin
5460 // notify packages
5461 Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,AnUnitInfo,sefsBeforeWrite);
5462 if Result<>mrOk then exit;
5463 // actual write
5464 Result:=AnUnitInfo.WriteUnitSource;
5465 if Result<>mrOk then exit;
5466 AnUnitInfo.Modified:=false;
5467 // notify packages
5468 Result:=MainIDEInterface.CallSaveEditorFileHandler(LazarusIDE,AnUnitInfo,sefsAfterWrite);
5469 if Result<>mrOk then exit;
5470 end;
5471
5472 // change lpks containing the file
5473 Result:=PkgBoss.OnRenameFile(OldFilename,AnUnitInfo.Filename,
5474 AnUnitInfo.IsPartOfProject);
5475 if Result=mrAbort then exit;
5476
5477 OldFileExisted:=FilenameIsAbsolute(OldFilename) and FileExistsUTF8(OldFilename);
5478
5479 // delete ambiguous files
5480 NewFilePath:=ExtractFilePath(NewFilename);
5481 AmbiguousFiles:=
5482 FindFilesCaseInsensitive(NewFilePath,ExtractFilename(NewFilename),true);
5483 if AmbiguousFiles<>nil then begin
5484 try
5485 if (AmbiguousFiles.Count=1)
5486 and (CompareFilenames(OldFilePath,NewFilePath)=0)
5487 and (CompareFilenames(AmbiguousFiles[0],ExtractFilename(OldFilename))=0)
5488 then
5489 AmbiguousText:=Format(lisDeleteOldFile, [ExtractFilename(OldFilename)])
5490 else
5491 AmbiguousText:=Format(lisThereAreOtherFilesInTheDirectoryWithTheSameName,
5492 [LineEnding, LineEnding, AmbiguousFiles.Text, LineEnding]);
5493 Result:=IDEMessageDialog(lisAmbiguousFilesFound, AmbiguousText,
5494 mtWarning,[mbYes,mbNo,mbAbort]);
5495 if Result=mrAbort then exit;
5496 if Result=mrYes then begin
5497 NewFilePath:=AppendPathDelim(ExtractFilePath(NewFilename));
5498 for i:=0 to AmbiguousFiles.Count-1 do begin
5499 AmbiguousFilename:=NewFilePath+AmbiguousFiles[i];
5500 if (FileExistsUTF8(AmbiguousFilename))
5501 and (not DeleteFileUTF8(AmbiguousFilename))
5502 and (IDEMessageDialog(lisPkgMangDeleteFailed,
5503 Format(lisDeletingOfFileFailed, [AmbiguousFilename]),
5504 mtError, [mbIgnore, mbCancel])=mrCancel)
5505 then
5506 exit(mrCancel);
5507 end;
5508 end;
5509 finally
5510 AmbiguousFiles.Free;
5511 end;
5512 end;
5513
5514 // remove old path from unit path
5515 if AnUnitInfo.IsPartOfProject
5516 and (FilenameIsPascalUnit(OldFilename))
5517 and (OldFilePath<>'') then begin
5518 //DebugLn('RenameUnit OldFilePath="',OldFilePath,'" SourceDirs="',Project1.SourceDirectories.CreateSearchPathFromAllFiles,'"');
5519 if (SearchDirectoryInSearchPath(
5520 Project1.SourceDirectories.CreateSearchPathFromAllFiles,OldFilePath,1)<1)
5521 then
5522 //DebugLn('RenameUnit OldFilePath="',OldFilePath,'" UnitPath="',Project1.CompilerOptions.GetUnitPath(false),'"');
5523 if (SearchDirectoryInSearchPath(Project1.CompilerOptions.GetUnitPath(false),OldFilePath,1)<1)
5524 then
5525 if IDEMessageDialog(lisCleanUpUnitPath,
5526 Format(lisTheDirectoryIsNoLongerNeededInTheUnitPathRemoveIt,[OldFilePath,LineEnding]),
5527 mtConfirmation,[mbYes,mbNo])=mrYes
5528 then
5529 Project1.CompilerOptions.RemoveFromUnitPaths(OldUnitPath);
5530 end;
5531
5532 // delete old pas, .pp, .ppu
5533 if (CompareFilenames(NewFilename,OldFilename)<>0)
5534 and OldFileExisted then begin
5535 if IDEMessageDialog(lisDeleteOldFile2, Format(lisDeleteOldFile,[OldFilename]),
5536 mtConfirmation,[mbYes,mbNo])=mrYes then
5537 begin
5538 Result:=DeleteFileInteractive(OldFilename,[mbAbort]);
5539 if Result=mrAbort then exit;
5540 // delete old lfm
5541 //debugln(['RenameUnit NewLFMFilename=',NewLFMFilename,' exists=',FileExistsUTF8(NewLFMFilename),' Old=',OldLFMFilename,' exists=',FileExistsUTF8(OldLFMFilename)]);
5542 if FileExistsUTF8(NewLFMFilename) then begin
5543 // the new file has a lfm, so it is safe to delete the old
5544 // (if NewLFMFilename does not exist, it didn't belong to the unit
5545 // or there was an error during delete. Never delete files in doubt.)
5546 OldLFMFilename:=ChangeFileExt(OldFilename,'.lfm');
5547 if FileExistsUTF8(OldLFMFilename) then begin
5548 Result:=DeleteFileInteractive(OldLFMFilename,[mbAbort]);
5549 if Result=mrAbort then exit;
5550 end;
5551 end;
5552 // delete old lrs
5553 if (LRSCode<>nil) and FileExistsUTF8(LRSCode.Filename) then begin
5554 // the new file has a lrs, so it is safe to delete the old
5555 // (if the new lrs does not exist, it didn't belong to the unit
5556 // or there was an error during delete. Never delete files in doubt.)
5557 OldLRSFilename:=ChangeFileExt(OldFilename,ResourceFileExt);
5558 if FileExistsUTF8(OldLRSFilename) then begin
5559 Result:=DeleteFileInteractive(OldLRSFilename,[mbAbort]);
5560 if Result=mrAbort then exit;
5561 end;
5562 end;
5563 // delete ppu in source directory
5564 OldPPUFilename:=ChangeFileExt(OldFilename,'.ppu');
5565 if FileExistsUTF8(OldPPUFilename) then begin
5566 Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]);
5567 if Result=mrAbort then exit;
5568 end;
5569 OldPPUFilename:=ChangeFileExt(OldPPUFilename,'.o');
5570 if FileExistsUTF8(OldPPUFilename) then begin
5571 Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]);
5572 if Result=mrAbort then exit;
5573 end;
5574 Owners:=PkgBoss.GetOwnersOfUnit(NewFilename);
5575 try
5576 if Owners<>nil then begin
5577 for i:=0 to Owners.Count-1 do begin
5578 OutDir:='';
5579 if TObject(Owners[i]) is TProject then begin
5580 // delete old files in project output directory
5581 OutDir:=TProject(Owners[i]).CompilerOptions.GetUnitOutPath(false);
5582 end else if TObject(Owners[i]) is TLazPackage then begin
5583 // delete old files in package output directory
5584 OutDir:=TLazPackage(Owners[i]).CompilerOptions.GetUnitOutPath(false);
5585 end;
5586 if (OutDir<>'') and FilenameIsAbsolute(OutDir) then begin
5587 OldPPUFilename:=AppendPathDelim(OutDir)+ChangeFileExt(ExtractFilenameOnly(OldFilename),'.ppu');
5588 if FileExistsUTF8(OldPPUFilename) then begin
5589 Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]);
5590 if Result=mrAbort then exit;
5591 end;
5592 OldPPUFilename:=ChangeFileExt(OldPPUFilename,'.o');
5593 if FileExistsUTF8(OldPPUFilename) then begin
5594 Result:=DeleteFileInteractive(OldPPUFilename,[mbAbort]);
5595 if Result=mrAbort then exit;
5596 end;
5597 OldLRSFilename:=ChangeFileExt(OldPPUFilename,ResourceFileExt);
5598 if FileExistsUTF8(OldLRSFilename) then begin
5599 Result:=DeleteFileInteractive(OldLRSFilename,[mbAbort]);
5600 if Result=mrAbort then exit;
5601 end;
5602 end;
5603 end;
5604 end;
5605 finally
5606 Owners.Free;
5607 end;
5608 end;
5609 end;
5610
5611 finally
5612 Project1.EndUpdate;
5613 end;
5614 Result:=mrOk;
5615 end;
5616
5617 function RenameUnitLowerCase(AnUnitInfo: TUnitInfo; AskUser: boolean): TModalresult;
5618 var
5619 OldFilename: String;
5620 OldShortFilename: String;
5621 NewFilename: String;
5622 NewShortFilename: String;
5623 LFMCode, LRSCode: TCodeBuffer;
5624 NewUnitName: String;
5625 begin
5626 Result:=mrOk;
5627 OldFilename:=AnUnitInfo.Filename;
5628 // check if file is unit
5629 if not FilenameIsPascalUnit(OldFilename) then exit;
5630 // check if file is already lowercase (or it does not matter in current OS)
5631 OldShortFilename:=ExtractFilename(OldFilename);
5632 NewShortFilename:=lowercase(OldShortFilename);
5633 if CompareFilenames(OldShortFilename,NewShortFilename)=0 then exit;
5634 // create new filename
5635 NewFilename:=ExtractFilePath(OldFilename)+NewShortFilename;
5636
5637 // rename unit
5638 if AskUser then begin
5639 Result:=IDEQuestionDialog(lisFileNotLowercase,
5640 Format(lisTheUnitIsNotLowercaseTheFreePascalCompiler,
5641 [OldFilename, LineEnding, LineEnding+LineEnding]),
5642 mtConfirmation,[mrYes,mrIgnore,rsmbNo,mrAbort],'');
5643 if Result<>mrYes then exit;
5644 end;
5645 NewUnitName:=AnUnitInfo.Unit_Name;
5646 if NewUnitName='' then begin
5647 AnUnitInfo.ReadUnitNameFromSource(false);
5648 NewUnitName:=AnUnitInfo.CreateUnitName;
5649 end;
5650 LFMCode:=nil;
5651 LRSCode:=nil;
5652 Result:=RenameUnit(AnUnitInfo,NewFilename,NewUnitName,LFMCode,LRSCode);
5653 end;
5654
5655 function CheckLFMInEditor(LFMUnitInfo: TUnitInfo; Quiet: boolean): TModalResult;
5656 var
5657 LFMChecker: TLFMChecker;
5658 UnitFilename: String;
5659 PascalBuf: TCodeBuffer;
5660 i: integer;
5661 LFMFilename: String;
5662 SrcEdit: TSourceEditor;
5663 begin
5664 if (LFMUnitInfo<>nil)
5665 and FilenameIsPascalUnit(LFMUnitInfo.Filename) then begin
5666 LFMFilename:=ChangeFileExt(LFMUnitInfo.Filename,'.lfm');
5667 if FileExistsInIDE(LFMFilename,[])
5668 and (OpenEditorFile(LFMFilename,-1,-1,nil,[])=mrOk)
5669 and (SourceEditorManager.ActiveEditor<>nil)
5670 then begin
5671 SrcEdit:=SourceEditorManager.ActiveEditor;
5672 LFMUnitInfo:=Project1.UnitInfoWithFilename(SrcEdit.FileName);
5673 end;
5674 end;
5675
5676 // check, if a .lfm file is opened in the source editor
5677 if (LFMUnitInfo=nil) or
5678 ((CompareFileExt(LFMUnitInfo.Filename,'.lfm',false)<>0) and
5679 (CompareFileExt(LFMUnitInfo.Filename,'.dfm',false)<>0)) then
5680 begin
5681 if not Quiet then
5682 begin
5683 IDEMessageDialog(lisNoLFMFile,
5684 lisThisFunctionNeedsAnOpenLfmFileInTheSourceEditor,
5685 mtError,[mbCancel]);
5686 end;
5687 Result:=mrCancel;
5688 exit;
5689 end;
5690 // try to find the pascal unit
5691 for i:=Low(PascalFileExt) to High(PascalFileExt) do begin
5692 UnitFilename:=ChangeFileExt(LFMUnitInfo.Filename,PascalFileExt[i]);
5693 if FileExistsCached(UnitFilename) then
5694 break
5695 else
5696 UnitFilename:='';
5697 end;
5698 if UnitFilename='' then begin
5699 IDEMessageDialog(lisNoPascalFile,
5700 Format(lisUnableToFindPascalUnitPasPpForLfmFile,[LineEnding, LFMUnitInfo.Filename]),
5701 mtError,[mbCancel]);
5702 Result:=mrCancel;
5703 exit;
5704 end;
5705
5706 if MainIDE.ToolStatus<>itNone then begin
5707 DebugLn(['CheckLFMInEditor ToolStatus<>itNone']);
5708 Result:=mrCancel;
5709 exit;
5710 end;
5711 // load the pascal unit
5712 SaveEditorChangesToCodeCache(nil);
5713 Result:=LoadCodeBuffer(PascalBuf,UnitFilename,[],false);
5714 if Result<>mrOk then exit;
5715
5716 // open messages window
5717 SourceEditorManager.ClearErrorLines;
5718 if MessagesView<>nil then
5719 MessagesView.Clear;
5720 ArrangeSourceEditorAndMessageView(false);
5721
5722 // parse the LFM file and the pascal unit
5723 LFMChecker:=TLFMChecker.Create(PascalBuf,LFMUnitInfo.Source);
5724 try
5725 LFMChecker.ShowMessages:=true;
5726 LFMChecker.RootMustBeClassInUnit:=true;
5727 LFMChecker.RootMustBeClassInIntf:=true;
5728 LFMChecker.ObjectsMustExist:=true;
5729 if LFMChecker.Repair=mrOk then begin
5730 if not Quiet then begin
5731 IDEMessageDialog(lisLFMIsOk,
5732 lisClassesAndPropertiesExistValuesWereNotChecked,
5733 mtInformation,[mbOk],'');
5734 end;
5735 end else begin
5736 MainIDE.DoJumpToCompilerMessage(true);
5737 Result:=mrAbort;
5738 exit;
5739 end;
5740 finally
5741 LFMChecker.Free;
5742 end;
5743 Result:=mrOk;
5744 end;
5745
5746 function LoadResourceFile(AnUnitInfo: TUnitInfo; var LFMCode, LRSCode: TCodeBuffer;
5747 AutoCreateResourceCode, ShowAbort: boolean): TModalResult;
5748 var
5749 LFMFilename: string;
5750 LRSFilename: String;
5751 ResType: TResourceType;
5752 begin
5753 LFMCode:=nil;
5754 LRSCode:=nil;
5755 //DebugLn(['LoadResourceFile ',AnUnitInfo.Filename,' HasResources=',AnUnitInfo.HasResources,' IgnoreSourceErrors=',IgnoreSourceErrors,' AutoCreateResourceCode=',AutoCreateResourceCode]);
5756 // Load the lfm file (without parsing)
5757 if not AnUnitInfo.IsVirtual then begin // and (AnUnitInfo.Component<>nil)
5758 LFMFilename:=AnUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(AnUnitInfo.Filename,true);
5759 if (FileExistsCached(LFMFilename)) then begin
5760 Result:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],ShowAbort);
5761 if not (Result in [mrOk,mrIgnore]) then
5762 exit;
5763 end;
5764 end;
5765 if AnUnitInfo.HasResources then begin
5766 //debugln('LoadResourceFile A "',AnUnitInfo.Filename,'" "',AnUnitInfo.ResourceFileName,'"');
5767 ResType:=MainBuildBoss.GetResourceType(AnUnitInfo);
5768 if ResType=rtLRS then begin
5769 LRSFilename:=MainBuildBoss.FindLRSFilename(AnUnitInfo,false);
5770 if LRSFilename<>'' then begin
5771 Result:=LoadCodeBuffer(LRSCode,LRSFilename,[lbfUpdateFromDisk],ShowAbort);
5772 if Result<>mrOk then exit;
5773 end else begin
5774 LRSFilename:=MainBuildBoss.GetDefaultLRSFilename(AnUnitInfo);
5775 if AutoCreateResourceCode then begin
5776 LRSCode:=CodeToolBoss.CreateFile(LRSFilename);
5777 end else begin
5778 DebugLn(['LoadResourceFile .lrs file not found of unit ',AnUnitInfo.Filename]);
5779 exit(mrCancel);
5780 end;
5781 end;
5782 end else begin
5783 LRSFilename:='';
5784 LRSCode:=nil;
5785 end;
5786 end;
5787 Result:=mrOk;
5788 end;
5789
5790 function LoadLFM(AnUnitInfo: TUnitInfo; OpenFlags: TOpenFlags;
5791 CloseFlags: TCloseFlags): TModalResult;
5792 // if there is a .lfm file, open the resource
5793 var
5794 UnitResourceFilename: string;
5795 UnitResourceFileformat: TUnitResourcefileFormatClass;
5796 LFMBuf: TCodeBuffer;
5797 CanAbort: boolean;
5798 begin
5799 CanAbort:=[ofProjectLoading,ofMultiOpen]*OpenFlags<>[];
5800
5801 UnitResourceFileformat:=AnUnitInfo.UnitResourceFileformat;
5802 // Note: think about virtual and normal .lfm files.
5803 UnitResourceFilename:=UnitResourceFileformat.GetUnitResourceFilename(AnUnitInfo.Filename,true);
5804 LFMBuf:=nil;
5805 if not FileExistsInIDE(UnitResourceFilename,[pfsfOnlyEditorFiles]) then begin
5806 // there is no LFM file -> ok
5807 {$IFDEF IDE_DEBUG}
5808 debugln('LoadLFM there is no LFM file for "',AnUnitInfo.Filename,'"');
5809 {$ENDIF}
5810 Result:=mrOk;
5811 exit;
5812 end;
5813
5814 // there is a lazarus form text file -> load it
5815 Result:=LoadIDECodeBuffer(LFMBuf,UnitResourceFilename,[lbfUpdateFromDisk],CanAbort);
5816 if Result<>mrOk then begin
5817 DebugLn(['LoadLFM LoadIDECodeBuffer failed']);
5818 exit;
5819 end;
5820 Result:=LoadLFM(AnUnitInfo,LFMBuf,OpenFlags,CloseFlags);
5821 end;
5822
5823 function LoadLFM(AnUnitInfo: TUnitInfo; LFMBuf: TCodeBuffer;
5824 OpenFlags: TOpenFlags; CloseFlags: TCloseFlags): TModalResult;
5825 const
5826 BufSize = 4096; // allocating mem in 4k chunks helps many mem managers
5827
5828 ShowCommands: array[TWindowState] of Integer =
5829 (SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED, SW_SHOWFULLSCREEN);
5830
5831 var
5832 TxtLFMStream, BinStream: TExtMemoryStream;
5833 NewComponent: TComponent;
5834 AncestorType: TComponentClass;
5835 DesignerForm: TCustomForm;
5836 NewClassName: String;
5837 LFMType: String;
5838 ACaption, AText: String;
5839 NewUnitName: String;
5840 AncestorUnitInfo, NestedUnitInfo, LFMUnitInfo: TUnitInfo;
5841 ReferencesLocked: Boolean;
5842 LCLVersion: string;
5843 MissingClasses: TStrings;
5844 LFMComponentName: string;
5845 i: Integer;
5846 NestedClassName: string;
5847 NestedClass: TComponentClass;
5848 DisableAutoSize: Boolean;
5849 PreventAutoSize: Boolean;
5850 NewControl: TControl;
5851 ARestoreVisible: Boolean;
5852 AncestorClass: TComponentClass;
5853 DsgControl: TCustomDesignControl;
5854 {$IF (FPC_FULLVERSION >= 30003)}
5855 DsgDataModule: TDataModule;
5856 {$ENDIF}
5857 begin
5858 {$IFDEF IDE_DEBUG}
5859 debugln('LoadLFM A ',AnUnitInfo.Filename,' IsPartOfProject=',dbgs(AnUnitInfo.IsPartOfProject),' ');
5860 {$ENDIF}
5861
5862 ReferencesLocked:=false;
5863 MissingClasses:=nil;
5864 NewComponent:=nil;
5865 try
5866 if (ofRevert in OpenFlags) and (AnUnitInfo.Component<>nil) then begin
5867 // the component must be destroyed and recreated => store references
5868 ReferencesLocked:=true;
5869 Project1.LockUnitComponentDependencies;
5870 Project1.UpdateUnitComponentDependencies;
5871
5872 // close old designer form
5873 Result:=CloseUnitComponent(AnUnitInfo,CloseFlags);
5874 if Result<>mrOk then begin
5875 DebugLn(['LoadLFM CloseUnitComponent failed']);
5876 exit;
5877 end;
5878 end;
5879
5880 // check installed packages
5881 if EnvironmentOptions.CheckPackagesOnFormCreate and
5882 (AnUnitInfo.Component = nil) and
5883 AnUnitInfo.IsPartOfProject and
5884 (not (ofProjectLoading in OpenFlags)) then
5885 begin
5886 // opening a form of the project -> check installed packages
5887 Result := PkgBoss.CheckProjectHasInstalledPackages(Project1,
5888 OpenFlags * [ofProjectLoading, ofQuiet] = []);
5889 if not (Result in [mrOk, mrIgnore]) then
5890 begin
5891 DebugLn(['LoadLFM PkgBoss.CheckProjectHasInstalledPackages failed']);
5892 exit;
5893 end;
5894 end;
5895 {$IFDEF VerboseLFMSearch}
5896 debugln('LoadLFM LFM file loaded, parsing "',LFMBuf.Filename,'" ...');
5897 {$ENDIF}
5898
5899 // someone created a .lfm file -> Update HasResources
5900 AnUnitInfo.HasResources:=true;
5901
5902 // find the classname of the LFM, and check for inherited form
5903 AnUnitInfo.UnitResourceFileformat.QuickCheckResourceBuffer(
5904 AnUnitInfo.Source,LFMBuf,LFMType,LFMComponentName,
5905 NewClassName,LCLVersion,MissingClasses);
5906
5907 {$IFDEF VerboseLFMSearch}
5908 debugln('LoadLFM LFM="',LFMBuf.Source,'"');
5909 {$ENDIF}
5910 if AnUnitInfo.Component=nil then begin
5911 // load/create new instance
5912
5913 if (NewClassName='') or (LFMType='') then begin
5914 DebugLn(['LoadLFM LFM file corrupt']);
5915 Result:=IDEMessageDialog(lisLFMFileCorrupt,
5916 Format(lisUnableToFindAValidClassnameIn, [LFMBuf.Filename]),
5917 mtError,[mbIgnore,mbCancel,mbAbort]);
5918 exit;
5919 end;
5920
5921 // load missing component classes (e.g. ancestor and frames)
5922 Result:=LoadAncestorDependencyHidden(AnUnitInfo,NewClassName,OpenFlags,
5923 AncestorType,AncestorUnitInfo);
5924 if Result<>mrOk then begin
5925 DebugLn(['LoadLFM DoLoadAncestorDependencyHidden failed for ',AnUnitInfo.Filename]);
5926 exit;
5927 end;
5928
5929 if MissingClasses<>nil then begin
5930 {$IFDEF VerboseLFMSearch}
5931 DebugLn(['LoadLFM has nested: ',AnUnitInfo.Filename]);
5932 {$ENDIF}
5933 for i:=MissingClasses.Count-1 downto 0 do begin
5934 NestedClassName:=MissingClasses[i];
5935 {$IFDEF VerboseLFMSearch}
5936 DebugLn(['LoadLFM nested ',i,' ',MissingClasses.Count,': ',NestedClassName]);
5937 {$ENDIF}
5938 if SysUtils.CompareText(NestedClassName,AncestorType.ClassName)=0 then
5939 begin
5940 MissingClasses.Delete(i);
5941 end else begin
5942 DebugLn(['LoadLFM loading nested class ',NestedClassName,' needed by ',AnUnitInfo.Filename]);
5943 NestedClass:=nil;
5944 NestedUnitInfo:=nil;
5945 Result:=LoadComponentDependencyHidden(AnUnitInfo,NestedClassName,
5946 OpenFlags,
5947 {$IFDEF EnableNestedComponentsWithoutLFM}
5948 false,
5949 {$ELSE}
5950 true,
5951 {$ENDIF}
5952 NestedClass,NestedUnitInfo,AncestorClass);
5953 if Result<>mrOk then begin
5954 DebugLn(['LoadLFM DoLoadComponentDependencyHidden NestedClassName=',NestedClassName,' failed for ',AnUnitInfo.Filename]);
5955 exit;
5956 end;
5957 if NestedClass<>nil then
5958 MissingClasses.Objects[i]:=TObject(Pointer(NestedClass))
5959 else if AncestorClass<>nil then
5960 MissingClasses.Objects[i]:=TObject(Pointer(AncestorClass));
5961 end;
5962 end;
5963 //DebugLn(['LoadLFM had nested: ',AnUnitInfo.Filename]);
5964 if AnUnitInfo.ComponentFallbackClasses<>nil then begin
5965 AnUnitInfo.ComponentFallbackClasses.Free;
5966 AnUnitInfo.ComponentFallbackClasses:=nil;
5967 end;
5968 AnUnitInfo.ComponentFallbackClasses:=MissingClasses;
5969 MissingClasses:=nil;
5970 end;
5971
5972 BinStream:=nil;
5973 try
5974 // convert text to binary format
5975 BinStream:=TExtMemoryStream.Create;
5976 TxtLFMStream:=TExtMemoryStream.Create;
5977 try
5978 {$IFDEF VerboseIDELFMConversion}
5979 DebugLn(['LoadLFM LFMBuf START =======================================']);
5980 DebugLn(LFMBuf.Source);
5981 DebugLn(['LoadLFM LFMBuf END =======================================']);
5982 {$ENDIF}
5983 LFMBuf.SaveToStream(TxtLFMStream);
5984 AnUnitInfo.ComponentLastLFMStreamSize:=TxtLFMStream.Size;
5985 TxtLFMStream.Position:=0;
5986
5987 try
5988 if AnUnitInfo.ComponentLastBinStreamSize>0 then
5989 BinStream.Capacity:=AnUnitInfo.ComponentLastBinStreamSize+BufSize;
5990 AnUnitInfo.UnitResourceFileformat.TextStreamToBinStream(TxtLFMStream, BinStream);
5991 AnUnitInfo.ComponentLastBinStreamSize:=BinStream.Size;
5992 BinStream.Position:=0;
5993
5994 {$IFDEF VerboseIDELFMConversion}
5995 DebugLn(['LoadLFM Binary START =======================================']);
5996 debugln(dbgMemStream(BinStream,BinStream.Size));
5997 DebugLn(['LoadLFM Binary END =======================================']);
5998 BinStream.Position:=0;
5999 {$ENDIF}
6000
6001 Result:=mrOk;
6002 except
6003 on E: Exception do begin
6004 DumpExceptionBackTrace;
6005 ACaption:=lisFormatError;
6006 AText:=Format(lisUnableToConvertTextFormDataOfFileIntoBinaryStream,
6007 [LineEnding, LFMBuf.Filename, LineEnding, E.Message]);
6008 Result:=IDEMessageDialog(ACaption, AText, mtError, [mbOk, mbCancel]);
6009 if Result=mrCancel then Result:=mrAbort;
6010 exit;
6011 end;
6012 end;
6013 finally
6014 TxtLFMStream.Free;
6015 end;
6016 if ([ofProjectLoading,ofLoadHiddenResource]*OpenFlags=[]) then
6017 FormEditor1.ClearSelection;
6018
6019 // create JIT component
6020 NewUnitName:=AnUnitInfo.Unit_Name;
6021 if NewUnitName='' then
6022 NewUnitName:=ExtractFileNameOnly(AnUnitInfo.Filename);
6023 DisableAutoSize:=true;
6024 NewComponent:=FormEditor1.CreateRawComponentFromStream(BinStream,
6025 AnUnitInfo.UnitResourceFileformat,
6026 AncestorType,copy(NewUnitName,1,255),true,true,DisableAutoSize,AnUnitInfo);
6027 if (NewComponent is TControl) then begin
6028 NewControl:=TControl(NewComponent);
6029 if ofLoadHiddenResource in OpenFlags then
6030 NewControl.ControlStyle:=NewControl.ControlStyle+[csNoDesignVisible];
6031 if DisableAutoSize then
6032 begin
6033 PreventAutoSize := (IDETabMaster <> nil)
6034 and (NewControl is TCustomDesignControl)
6035 and IDETabMaster.AutoSizeInShowDesigner(NewControl);
6036 if not PreventAutoSize then
6037 NewControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster Delayed'){$ENDIF};
6038 end;
6039 end;
6040
6041 if NewComponent is TCustomDesignControl then
6042 begin
6043 DsgControl := TCustomDesignControl(NewComponent);
6044 if (Project1.Scaled or EnvironmentOptions.ForceDPIScalingInDesignTime)
6045 and DsgControl.Scaled and (DsgControl.DesignTimePPI<>Screen.PixelsPerInch) then
6046 begin
6047 DsgControl.AutoAdjustLayout(lapAutoAdjustForDPI, DsgControl.DesignTimePPI, Screen.PixelsPerInch, 0, 0);
6048 DesignerProcs.ScaleNonVisual(DsgControl, DsgControl.DesignTimePPI, Screen.PixelsPerInch);
6049 end;
6050 DsgControl.DesignTimePPI := Screen.PixelsPerInch;
6051 DsgControl.PixelsPerInch := Screen.PixelsPerInch;
6052 end;
6053 {$IF (FPC_FULLVERSION >= 30003)} // TDataModule.DesignPPI was added in FPC 3.0.3
6054 if NewComponent is TDataModule then
6055 begin
6056 DsgDataModule := TDataModule(NewComponent);
6057 if (DsgDataModule.DesignPPI<>Screen.PixelsPerInch) then
6058 begin
6059 DesignerProcs.ScaleNonVisual(DsgDataModule, DsgDataModule.DesignPPI, Screen.PixelsPerInch);
6060 DsgDataModule.DesignOffset := Point(
6061 MulDiv(DsgDataModule.DesignOffset.x, Screen.PixelsPerInch, DsgDataModule.DesignPPI),
6062 MulDiv(DsgDataModule.DesignOffset.y, Screen.PixelsPerInch, DsgDataModule.DesignPPI));
6063 DsgDataModule.DesignSize := Point(
6064 MulDiv(DsgDataModule.DesignSize.x, Screen.PixelsPerInch, DsgDataModule.DesignPPI),
6065 MulDiv(DsgDataModule.DesignSize.y, Screen.PixelsPerInch, DsgDataModule.DesignPPI));
6066 DsgDataModule.DesignPPI := Screen.PixelsPerInch;
6067 end;
6068 end;
6069 {$ENDIF}
6070
6071 AnUnitInfo.ResourceBaseClass:=GetComponentBaseClass(NewComponent.ClassType);
6072
6073 Project1.InvalidateUnitComponentDesignerDependencies;
6074 AnUnitInfo.Component:=NewComponent;
6075 if (AncestorUnitInfo<>nil) then
6076 AnUnitInfo.AddRequiresComponentDependency(AncestorUnitInfo,[ucdtAncestor]);
6077 if NewComponent<>nil then begin
6078 // component loaded, now load the referenced units
6079 Result:=MainIDE.DoFixupComponentReferences(AnUnitInfo.Component,OpenFlags);
6080 if Result<>mrOk then begin
6081 DebugLn(['LoadLFM DoFixupComponentReferences failed']);
6082 exit;
6083 end;
6084 end else begin
6085 // error streaming component -> examine lfm file
6086 DebugLn('ERROR: streaming failed lfm="',LFMBuf.Filename,'"');
6087 // open lfm file in editor
6088 if AnUnitInfo.OpenEditorInfoCount > 0 then
6089 Result:=OpenEditorFile(LFMBuf.Filename,
6090 AnUnitInfo.OpenEditorInfo[0].PageIndex+1,
6091 AnUnitInfo.OpenEditorInfo[0].WindowID, Nil,
6092 OpenFlags+[ofOnlyIfExists,ofQuiet,ofRegularFile], True)
6093 else
6094 Result:=OpenEditorFile(LFMBuf.Filename, -1, -1, nil,
6095 OpenFlags+[ofOnlyIfExists,ofQuiet,ofRegularFile]);
6096 if Result<>mrOk then begin
6097 DebugLn(['LoadLFM DoOpenEditorFile failed']);
6098 exit;
6099 end;
6100 LFMUnitInfo:=Project1.UnitWithEditorComponent(SourceEditorManager.ActiveEditor);
6101 Result:=CheckLFMInEditor(LFMUnitInfo, true);
6102 if Result=mrOk then Result:=mrCancel;
6103 exit;
6104 end;
6105 finally
6106 BinStream.Free;
6107 end;
6108 end else if SysUtils.CompareText(AnUnitInfo.Component.ClassName,NewClassName)<>0
6109 then begin
6110 // lfm and current designer are about different classes
6111 debugln(['LoadLFM unit="',AnUnitInfo.Filename,'": loaded component has class "',AnUnitInfo.Component.ClassName,'", lfm has class "',NewClassName,'"']);
6112 // keep old instance, add a designer, so user can see current component
6113 end else begin
6114 // make hidden component visible, keep old instance, add a designer
6115 DebugLn(['LoadLFM Creating designer for hidden component of ',AnUnitInfo.Filename]);
6116 end;
6117 finally
6118 MissingClasses.Free;
6119 if ReferencesLocked then begin
6120 if Project1<>nil then
6121 Project1.UnlockUnitComponentDependencies;
6122 end;
6123 end;
6124
6125 NewComponent:=AnUnitInfo.Component;
6126 // create the designer (if not already done)
6127 if ([ofProjectLoading,ofLoadHiddenResource]*OpenFlags=[]) then
6128 FormEditor1.ClearSelection;
6129 {$IFDEF IDE_DEBUG}
6130 DebugLn('SUCCESS: streaming lfm="',LFMBuf.Filename,'"');
6131 {$ENDIF}
6132 AnUnitInfo.ComponentName:=NewComponent.Name;
6133 AnUnitInfo.ComponentResourceName:=AnUnitInfo.ComponentName;
6134 DesignerForm := nil;
6135 MainIDE.LastFormActivated := nil;
6136 if not (ofLoadHiddenResource in OpenFlags) then
6137 begin
6138 DesignerForm := FormEditor1.GetDesignerForm(NewComponent);
6139 if (DesignerForm=nil) or (DesignerForm.Designer=nil) then
6140 DesignerForm := MainIDE.CreateDesignerForComponent(AnUnitInfo,NewComponent);
6141 end;
6142
6143 // select the new form (object inspector, formeditor, control selection)
6144 if (DesignerForm <> nil)
6145 and ([ofProjectLoading,ofLoadHiddenResource] * OpenFlags=[]) then
6146 begin
6147 MainIDE.DisplayState := dsForm;
6148 GlobalDesignHook.LookupRoot := NewComponent;
6149 TheControlSelection.AssignPersistent(NewComponent);
6150 end;
6151
6152 // show new form
6153 if DesignerForm <> nil then
6154 begin
6155 DesignerForm.ControlStyle := DesignerForm.ControlStyle - [csNoDesignVisible];
6156 if NewComponent is TControl then
6157 TControl(NewComponent).ControlStyle:= TControl(NewComponent).ControlStyle - [csNoDesignVisible];
6158 if (DesignerForm.WindowState in [wsMinimized]) then
6159 begin
6160 ARestoreVisible := DesignerForm.Visible;
6161 DesignerForm.Visible := False;
6162 DesignerForm.ShowOnTop;
6163 DesignerForm.Visible := ARestoreVisible;
6164 DesignerForm.WindowState := wsMinimized;
6165 end else
6166 if IDETabMaster = nil then
6167 LCLIntf.ShowWindow(DesignerForm.Handle, ShowCommands[AnUnitInfo.ComponentState]);
6168 MainIDE.LastFormActivated := DesignerForm;
6169 end;
6170
6171 {$IFDEF IDE_DEBUG}
6172 debugln('[LoadLFM] LFM end');
6173 {$ENDIF}
6174 Result:=mrOk;
6175 end;
6176
6177 function OpenComponent(const UnitFilename: string;
6178 OpenFlags: TOpenFlags; CloseFlags: TCloseFlags; out Component: TComponent): TModalResult;
6179 var
6180 AnUnitInfo: TUnitInfo;
6181 LFMFilename: String;
6182 UnitCode: TCodeBuffer;
6183 LFMCode: TCodeBuffer;
6184 AFilename: String;
6185 begin
6186 if Project1=nil then exit(mrCancel);
6187 // try to find a unit name without expaning the path. this is required if unit is virtual
6188 // in other case file name will be expanded with the wrong path
6189 AFilename := UnitFilename;
6190 AnUnitInfo:=Project1.UnitInfoWithFilename(AFilename);
6191 if AnUnitInfo = nil then
6192 begin
6193 AFilename:=TrimAndExpandFilename(UnitFilename);
6194 if (AFilename='') or (not FileExistsInIDE(AFilename,[])) then begin
6195 DebugLn(['OpenComponent file not found ',AFilename]);
6196 exit(mrCancel);
6197 end;
6198 AnUnitInfo:=Project1.UnitInfoWithFilename(AFilename);
6199 end;
6200 if (not (ofRevert in OpenFlags))
6201 and (AnUnitInfo<>nil) and (AnUnitInfo.Component<>nil) then begin
6202 // already open
6203 Component:=AnUnitInfo.Component;
6204 Result:=mrOk;
6205 exit;
6206 end;
6207
6208 // ToDo: use UnitResources
6209 LFMFilename:=ChangeFileExt(AFilename,'.lfm');
6210 if not FileExistsInIDE(LFMFilename,[]) then
6211 LFMFilename:=ChangeFileExt(AFilename,'.dfm');
6212 if not FileExistsInIDE(LFMFilename,[]) then begin
6213 DebugLn(['OpenComponent file not found ',LFMFilename]);
6214 exit(mrCancel);
6215 end;
6216
6217 // load unit source
6218 Result:=LoadCodeBuffer(UnitCode,AFilename,[lbfCheckIfText],true);
6219 if Result<>mrOk then begin
6220 debugln('OpenComponent Failed loading ',AFilename);
6221 exit;
6222 end;
6223
6224 // create unit info
6225 if AnUnitInfo=nil then begin
6226 AnUnitInfo:=TUnitInfo.Create(UnitCode);
6227 AnUnitInfo.ReadUnitNameFromSource(true);
6228 Project1.AddFile(AnUnitInfo,false);
6229 end;
6230
6231 // load lfm source
6232 Result:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],true);
6233 if Result<>mrOk then begin
6234 debugln('OpenComponent Failed loading ',LFMFilename);
6235 exit;
6236 end;
6237
6238 // load resource
6239 Result:=LoadLFM(AnUnitInfo,LFMCode,OpenFlags,CloseFlags);
6240 if Result<>mrOk then begin
6241 debugln('OpenComponent DoLoadLFM failed ',LFMFilename);
6242 exit;
6243 end;
6244
6245 Component:=AnUnitInfo.Component;
6246 if Component<>nil then
6247 Result:=mrOk
6248 else
6249 Result:=mrCancel;
6250 end;
6251
6252 function UpdateUnitInfoResourceBaseClass(AnUnitInfo: TUnitInfo; Quiet: boolean): boolean;
6253 var
6254 LFMFilename, LFMClassName, LFMType, Ancestor, LFMComponentName: String;
6255 LFMCode, Code: TCodeBuffer;
6256 LoadFileFlags: TLoadBufferFlags;
6257 ClearOldInfo: Boolean;
6258 Tool: TCodeTool;
6259 Node: TCodeTreeNode;
6260 ListOfPFindContext: TFPList;
6261 i: Integer;
6262 Context: PFindContext;
6263 begin
6264 Result:=false;
6265 if AnUnitInfo.Component<>nil then
6266 exit(true); // a loaded resource is always uptodate
6267 if AnUnitInfo.IsVirtual then
6268 exit(true); // a new unit is always uptodate
6269 ListOfPFindContext:=nil;
6270 ClearOldInfo:=true;
6271 try
6272 // find lfm file
6273 if not FilenameIsPascalUnit(AnUnitInfo.Filename) then
6274 exit(true); // not a unit -> clear info
6275 LFMFilename:=AnUnitInfo.UnitResourceFileformat.GetUnitResourceFilename(
6276 AnUnitInfo.Filename,true);
6277 if (LFMFilename='') or not FileExistsCached(LFMFilename) then
6278 exit(true); // no lfm -> clear info
6279 finally
6280 if ClearOldInfo then begin
6281 AnUnitInfo.ResourceBaseClass:=pfcbcNone;
6282 AnUnitInfo.ComponentName:='';
6283 AnUnitInfo.ComponentResourceName:='';
6284 end;
6285 end;
6286 try
6287 if (CompareFileExt(LFMFilename,'lfm')<>0) then
6288 begin
6289 // no lfm format -> keep old info
6290 exit(true);
6291 end;
6292 // clear old info
6293 AnUnitInfo.ResourceBaseClass:=pfcbcNone;
6294 AnUnitInfo.ComponentName:='';
6295 AnUnitInfo.ComponentResourceName:='';
6296 // load lfm
6297 LoadFileFlags:=[lbfUpdateFromDisk,lbfCheckIfText];
6298 if Quiet then
6299 Include(LoadFileFlags,lbfQuiet);
6300 if LoadCodeBuffer(LFMCode,LFMFilename,LoadFileFlags,false)<>mrOk then
6301 exit; // lfm read error
6302 // read lfm header
6303 ReadLFMHeader(LFMCode.Source,LFMType,LFMComponentName,LFMClassName);
6304 if LFMClassName='' then
6305 exit; // lfm syntax error
6306
6307 // LFM component name
6308 AnUnitInfo.ComponentName:=LFMComponentName;
6309 AnUnitInfo.ComponentResourceName:=LFMComponentName;
6310
6311 // check ancestors
6312 if LoadCodeBuffer(Code,AnUnitInfo.Filename,LoadFileFlags,false)<>mrOk then
6313 exit; // pas read error
6314 CodeToolBoss.Explore(Code,Tool,false,true);
6315 if Tool=nil then
6316 exit; // pas load error
6317 try
6318 Node:=Tool.FindDeclarationNodeInInterface(LFMClassName,true);
6319 if Node=nil then
6320 exit(Tool.FindImplementationNode<>nil); // class not found, reliable if whole interface was read
6321
6322 if (Node=nil) or (Node.Desc<>ctnTypeDefinition)
6323 or (Node.FirstChild=nil) or (Node.FirstChild.Desc<>ctnClass) then
6324 exit(true); // this is not a class
6325 Tool.FindClassAndAncestors(Node.FirstChild,ListOfPFindContext,false);
6326 if ListOfPFindContext=nil then
6327 exit; // ancestor not found -> probably syntax error
6328
6329 for i:=0 to ListOfPFindContext.Count-1 do begin
6330 Context:=PFindContext(ListOfPFindContext[i]);
6331 Ancestor:=UpperCase(Context^.Tool.ExtractClassName(Context^.Node,false));
6332 if (Ancestor='TFORM') then begin
6333 AnUnitInfo.ResourceBaseClass:=pfcbcForm;
6334 exit(true);
6335 end else if (Ancestor='TCUSTOMFORM') then begin
6336 AnUnitInfo.ResourceBaseClass:=pfcbcCustomForm;
6337 exit(true);
6338 end else if Ancestor='TDATAMODULE' then begin
6339 AnUnitInfo.ResourceBaseClass:=pfcbcDataModule;
6340 exit(true);
6341 end else if (Ancestor='TFRAME') or (Ancestor='TCUSTOMFRAME') then begin
6342 AnUnitInfo.ResourceBaseClass:=pfcbcFrame;
6343 exit(true);
6344 end else if Ancestor='TCOMPONENT' then
6345 exit(true);
6346 end;
6347 except
6348 exit; // syntax error or unit not found
6349 end;
6350 finally
6351 FreeListOfPFindContext(ListOfPFindContext);
6352 end;
6353 end;
6354
6355 function FindBaseComponentClass(AnUnitInfo: TUnitInfo; const AComponentClassName,
6356 DescendantClassName: string; out AComponentClass: TComponentClass): boolean;
6357 // returns false if an error occured
6358 // Important: returns true even if AComponentClass=nil
6359 begin
6360 AComponentClass:=nil;
6361 // find the ancestor class
6362 if AnUnitInfo.UnitResourceFileformat<>nil then
6363 begin
6364 AComponentClass:=AnUnitInfo.UnitResourceFileformat.FindComponentClass(AComponentClassName);
6365 if AComponentClass<>nil then
6366 exit(true);
6367 end;
6368 if AComponentClassName<>'' then begin
6369 if (DescendantClassName<>'')
6370 and (SysUtils.CompareText(AComponentClassName,'TCustomForm')=0) then begin
6371 // this is a common user mistake
6372 IDEMessageDialog(lisCodeTemplError,
6373 Format(lisTheResourceClassDescendsFromProbablyThisIsATypoFor,
6374 [DescendantClassName, AComponentClassName]),
6375 mtError,[mbCancel]);
6376 Result:=false;
6377 exit;
6378 end else if (DescendantClassName<>'')
6379 and (SysUtils.CompareText(AComponentClassName,'TComponent')=0) then begin
6380 // this is not yet implemented
6381 IDEMessageDialog(lisCodeTemplError,
6382 Format(lisUnableToOpenDesignerTheClassDoesNotDescendFromADes,
6383 [LineEnding, DescendantClassName]),
6384 mtError,[mbCancel]);
6385 Result:=false;
6386 exit;
6387 end else begin
6388 // search in the registered base classes
6389 AComponentClass:=FormEditor1.FindDesignerBaseClassByName(AComponentClassName,true);
6390 end;
6391 end else begin
6392 // default is TForm
6393 AComponentClass:=BaseFormEditor1.StandardDesignerBaseClasses[DesignerBaseClassId_TForm];
6394 end;
6395 Result:=true;
6396 end;
6397
6398 function LoadAncestorDependencyHidden(AnUnitInfo: TUnitInfo;
6399 const aComponentClassName: string;
6400 OpenFlags: TOpenFlags;
6401 out AncestorClass: TComponentClass;
6402 out AncestorUnitInfo: TUnitInfo): TModalResult;
6403 var
6404 AncestorClassName, IgnoreBtnText: String;
6405 CodeBuf: TCodeBuffer;
6406 GrandAncestorClass, DefAncestorClass: TComponentClass;
6407 begin
6408 AncestorClassName:='';
6409 AncestorClass:=nil;
6410 AncestorUnitInfo:=nil;
6411
6412 // find the ancestor type in the source
6413 if AnUnitInfo.Source=nil then begin
6414 Result:=LoadCodeBuffer(CodeBuf,AnUnitInfo.Filename,
6415 [lbfUpdateFromDisk,lbfCheckIfText],true);
6416 if Result<>mrOk then exit;
6417 AnUnitInfo.Source:=CodeBuf;
6418 end;
6419 if not CodeToolBoss.FindFormAncestor(AnUnitInfo.Source,aComponentClassName,
6420 AncestorClassName,true)
6421 then begin
6422 DebugLn('LoadAncestorDependencyHidden Filename="',AnUnitInfo.Filename,'" ClassName=',aComponentClassName,'. Unable to find ancestor class: ',CodeToolBoss.ErrorMessage);
6423 end;
6424
6425 // try the base designer classes
6426 if not FindBaseComponentClass(AnUnitInfo,AncestorClassName,
6427 aComponentClassName,AncestorClass) then
6428 begin
6429 DebugLn(['LoadAncestorDependencyHidden FindUnitComponentClass failed for AncestorClassName=',AncestorClassName]);
6430 exit(mrCancel);
6431 end;
6432
6433 // try loading the ancestor first (unit, lfm and component instance)
6434
6435 if AnUnitInfo.UnitResourceFileformat<>nil then
6436 DefAncestorClass:=AnUnitInfo.UnitResourceFileformat.DefaultComponentClass
6437 else
6438 DefAncestorClass:=nil;
6439 // use TForm as default ancestor
6440 if DefAncestorClass=nil then
6441 DefAncestorClass:=BaseFormEditor1.StandardDesignerBaseClasses[DesignerBaseClassId_TForm];
6442
6443 if (AncestorClass=nil) then begin
6444 IgnoreBtnText:='';
6445 if DefAncestorClass<>nil then
6446 IgnoreBtnText:=Format(lisIgnoreUseAsAncestor, [DefAncestorClass.ClassName]);
6447
6448 Result:=LoadComponentDependencyHidden(AnUnitInfo,AncestorClassName,
6449 OpenFlags,false,AncestorClass,AncestorUnitInfo,GrandAncestorClass,
6450 IgnoreBtnText);
6451 if Result<>mrOk then begin
6452 DebugLn(['LoadAncestorDependencyHidden DoLoadComponentDependencyHidden failed AnUnitInfo=',AnUnitInfo.Filename]);
6453 end;
6454 case Result of
6455 mrAbort: exit;
6456 mrOk: ;
6457 mrIgnore:
6458 AncestorUnitInfo:=nil;
6459 else
6460 // cancel
6461 Result:=mrCancel;
6462 exit;
6463 end;
6464 end;
6465
6466 //DebugLn('LoadAncestorDependencyHidden Filename="',AnUnitInfo.Filename,'" AncestorClassName=',AncestorClassName,' AncestorClass=',dbgsName(AncestorClass));
6467 if AncestorClass=nil then
6468 AncestorClass:=DefAncestorClass;
6469 Result:=mrOk;
6470 end;
6471
6472 function FindComponentClass(AnUnitInfo: TUnitInfo; const AComponentClassName: string;
6473 Quiet: boolean; out ComponentUnitInfo: TUnitInfo; out AComponentClass: TComponentClass;
6474 out LFMFilename: string; out AncestorClass: TComponentClass): TModalResult;
6475 { Possible results:
6476 mrOk:
6477 - AComponentClass<>nil and ComponentUnitInfo<>nil
6478 designer component
6479 - AComponentClass<>nil and ComponentUnitInfo=nil
6480 registered componentclass
6481 - LFMFilename<>''
6482 lfm of an used unit
6483 - AncestorClass<>nil
6484 componentclass does not exist, but the ancestor is a registered class
6485 mrCancel:
6486 not found
6487 mrAbort:
6488 not found, error already shown
6489 }
6490 var
6491 CTErrorMsg: String;
6492 CTErrorCode: TCodeBuffer;
6493 CTErrorLine: Integer;
6494 CTErrorCol: Integer;
6495
6496 procedure StoreCodetoolsError;
6497 begin
6498 {$IFDEF VerboseLFMSearch}
6499 debugln([' StoreCodetoolsError: ',CodeToolBoss.ErrorMessage]);
6500 {$ENDIF}
6501 if CTErrorMsg<>'' then exit;
6502 if CodeToolBoss.ErrorMessage<>'' then begin
6503 CTErrorMsg:=CodeToolBoss.ErrorMessage;
6504 CTErrorCode:=CodeToolBoss.ErrorCode;
6505 CTErrorLine:=CodeToolBoss.ErrorLine;
6506 CTErrorCol:=CodeToolBoss.ErrorColumn;
6507 end;
6508 end;
6509
6510 function TryUnitComponent(const UnitFilename: string;
6511 out TheModalResult: TModalResult): boolean;
6512 // returns true if the unit contains the component class and sets
6513 // TheModalResult to the result of the loading
6514 var
6515 CurUnitInfo: TUnitInfo;
6516 begin
6517 {$IFDEF VerboseLFMSearch}
6518 debugln([' TryUnitComponent UnitFilename="',UnitFilename,'"']);
6519 {$ENDIF}
6520 Result:=false;
6521 TheModalResult:=mrCancel;
6522 if not FilenameIsPascalUnit(UnitFilename) then exit;
6523
6524 CurUnitInfo:=Project1.UnitInfoWithFilename(UnitFilename);
6525 if (CurUnitInfo=nil) or (CurUnitInfo.Component=nil) then exit;
6526 // unit with loaded component found -> check if it is the right one
6527 //DebugLn(['FindComponentClass unit with a component found CurUnitInfo=',CurUnitInfo.Filename,' ',dbgsName(CurUnitInfo.Component)]);
6528 if SysUtils.CompareText(CurUnitInfo.Component.ClassName,AComponentClassName)<>0
6529 then exit;
6530 // component found (it was already loaded)
6531 ComponentUnitInfo:=CurUnitInfo;
6532 AComponentClass:=TComponentClass(ComponentUnitInfo.Component.ClassType);
6533 TheModalResult:=mrOk;
6534 Result:=true;
6535 end;
6536
6537 function TryRegisteredClasses(aClassName: string;
6538 out FoundComponentClass: TComponentClass;
6539 out TheModalResult: TModalResult): boolean;
6540 var
6541 RegComp: TRegisteredComponent;
6542 begin
6543 {$IFDEF VerboseLFMSearch}
6544 debugln([' TryRegisteredClasses aClassName="',aClassName,'"']);
6545 {$ENDIF}
6546 Result:=false;
6547 TheModalResult:=mrCancel;
6548 FoundComponentClass:=nil;
6549 if AnUnitInfo.UnitResourceFileformat<>nil then
6550 FoundComponentClass:=AnUnitInfo.UnitResourceFileformat.FindComponentClass(aClassName);
6551 if FoundComponentClass=nil then
6552 begin
6553 RegComp:=IDEComponentPalette.FindComponent(aClassName);
6554 if RegComp<>nil then
6555 FoundComponentClass:=RegComp.ComponentClass;
6556 end;
6557 if FoundComponentClass=nil then
6558 FoundComponentClass:=FormEditor1.FindDesignerBaseClassByName(aClassName,true);
6559 if FoundComponentClass<>nil then begin
6560 DebugLn(['FindComponentClass.TryRegisteredClasses found: ',FoundComponentClass.ClassName]);
6561 TheModalResult:=mrOk;
6562 Result:=true;
6563 end;
6564 end;
6565
6566 function TryLFM(const UnitFilename, AClassName: string;
6567 out TheModalResult: TModalResult): boolean;
6568 var
6569 CurLFMFilename: String;
6570 LFMCode: TCodeBuffer;
6571 LFMClassName: String;
6572 LFMType: String;
6573 begin
6574 {$IFDEF VerboseLFMSearch}
6575 debugln([' TryLFM UnitFilename="',UnitFilename,'" AClassName=',AClassName]);
6576 {$ENDIF}
6577 Result:=false;
6578 TheModalResult:=mrCancel;
6579 if not FilenameIsPascalSource(UnitFilename) then
6580 begin
6581 {$IFDEF VerboseLFMSearch}
6582 debugln([' TryLFM UnitFilename="',UnitFilename,'" is not a unit']);
6583 {$ENDIF}
6584 exit;
6585 end;
6586 // ToDo: use UnitResources
6587 CurLFMFilename:=ChangeFileExt(UnitFilename,'.lfm');
6588 if not FileExistsCached(CurLFMFilename) then
6589 begin
6590 {$IFDEF VerboseLFMSearch}
6591 debugln([' TryLFM CurLFMFilename="',CurLFMFilename,'" does not exist']);
6592 {$ENDIF}
6593 CurLFMFilename:=ChangeFileExt(UnitFilename,'.dfm');
6594 if not FileExistsCached(CurLFMFilename) then
6595 begin
6596 {$IFDEF VerboseLFMSearch}
6597 debugln([' TryLFM CurLFMFilename="',CurLFMFilename,'" does not exist']);
6598 {$ENDIF}
6599 exit;
6600 end;
6601 end;
6602 // load the lfm file
6603 TheModalResult:=LoadCodeBuffer(LFMCode,CurLFMFilename,[lbfCheckIfText],true);
6604 if TheModalResult<>mrOk then
6605 begin
6606 debugln('FindComponentClass Failed loading ',CurLFMFilename);
6607 exit;
6608 end;
6609 // read the LFM classname
6610 ReadLFMHeader(LFMCode.Source,LFMClassName,LFMType);
6611 if LFMType='' then ;
6612 if SysUtils.CompareText(LFMClassName,AClassName)<>0 then
6613 begin
6614 {$IFDEF VerboseLFMSearch}
6615 debugln([' TryLFM CurLFMFilename="',CurLFMFilename,'" LFMClassName="',LFMClassName,'" does not match']);
6616 {$ENDIF}
6617 exit;
6618 end;
6619
6620 // .lfm found
6621 LFMFilename:=CurLFMFilename;
6622 Result:=true;
6623 end;
6624
6625 function TryFindDeclaration(out TheModalResult: TModalResult): boolean;
6626 var
6627 Tool: TCodeTool;
6628
6629 function FindTypeNode(Node: TCodeTreeNode; Level: integer): TCodeTreeNode;
6630 var
6631 TypeNode: TCodeTreeNode;
6632 Child: TCodeTreeNode;
6633 begin
6634 Result:=nil;
6635 if Node=nil then exit;
6636 if Node.Desc=ctnVarDefinition then begin
6637 TypeNode:=Tool.FindTypeNodeOfDefinition(Node);
6638 if (TypeNode=nil) or (TypeNode.Desc<>ctnIdentifier) then exit;
6639 if Tool.CompareSrcIdentifiers(TypeNode.StartPos,PChar(AComponentClassName))
6640 then exit(TypeNode);
6641 end else if Node.Desc=ctnTypeDefinition then begin
6642 if Tool.CompareSrcIdentifiers(Node.StartPos,PChar(AComponentClassName))
6643 then exit(Node);
6644 end;
6645 // increase level on identifier nodes
6646 if Node.Desc in AllIdentifierDefinitions then begin
6647 if Level=1 then exit; // ignore nested vars
6648 inc(Level);
6649 end;
6650 Child:=Node.FirstChild;
6651 while Child<>nil do begin
6652 Result:=FindTypeNode(Child,Level);
6653 if Result<>nil then exit;
6654 Child:=Child.NextBrother;
6655 end;
6656 end;
6657
6658 var
6659 Code: TCodeBuffer;
6660 Params: TFindDeclarationParams;
6661 NewNode: TCodeTreeNode;
6662 NewTool: TFindDeclarationTool;
6663 InheritedNode: TCodeTreeNode;
6664 ClassNode: TCodeTreeNode;
6665 AncestorNode: TCodeTreeNode;
6666 AncestorClassName: String;
6667 Node: TCodeTreeNode;
6668 ok: Boolean;
6669 begin
6670 Result:=false;
6671 TheModalResult:=mrCancel;
6672 // parse interface current unit
6673 Code:=CodeToolBoss.LoadFile(AnUnitInfo.Filename,false,false);
6674 if Code=nil then begin
6675 debugln(['FindComponentClass unable to load ',AnUnitInfo.Filename]);
6676 exit;
6677 end;
6678 if not CodeToolBoss.Explore(Code,Tool,false,true) then begin
6679 {$IFDEF VerboseLFMSearch}
6680 debugln([' CodeToolBoss.Explore failed: ',Code.Filename]);
6681 {$ENDIF}
6682 StoreCodetoolsError;
6683 exit;
6684 end;
6685 Params:=TFindDeclarationParams.Create;
6686 try
6687 ok:=false;
6688 try
6689 // search a class reference in the unit
6690 Node:=Tool.FindInterfaceNode;
6691 if Node=nil then
6692 Node:=Tool.Tree.Root;
6693 Node:=FindTypeNode(Node,0);
6694 if Node=nil then begin
6695 debugln('FindComponentClass Failed finding reference of ',AComponentClassName,' in ',Code.Filename);
6696 exit;
6697 end;
6698 if Node.Desc=ctnIdentifier then begin
6699 //debugln(['TryFindDeclaration found reference of ',AComponentClassName,' at ',Tool.CleanPosToStr(Node.StartPos)]);
6700 Params.ContextNode:=Node;
6701 Params.Flags:=[fdfSearchInParentNodes,fdfExceptionOnNotFound,
6702 fdfExceptionOnPredefinedIdent,
6703 fdfTopLvlResolving,fdfSearchInAncestors,
6704 fdfIgnoreCurContextNode];
6705 Params.SetIdentifier(Tool,@Tool.Src[Node.StartPos],nil);
6706 if not Tool.FindIdentifierInContext(Params) then begin
6707 debugln(['FindComponentClass find declaration failed at ',Tool.CleanPosToStr(Node.StartPos,true)]);
6708 exit;
6709 end;
6710 NewNode:=Params.NewNode;
6711 NewTool:=Params.NewCodeTool;
6712 end else begin
6713 NewNode:=Node;
6714 NewTool:=Tool;
6715 end;
6716 ok:=true;
6717 except
6718 on E: Exception do
6719 CodeToolBoss.HandleException(E);
6720 end;
6721 if not ok then begin
6722 {$IFDEF VerboseLFMSearch}
6723 debugln([' find declaration failed.']);
6724 {$ENDIF}
6725 StoreCodetoolsError;
6726 exit;
6727 end;
6728 // declaration found
6729 ClassNode:=NewNode.FirstChild;
6730 if (NewNode.Desc<>ctnTypeDefinition)
6731 or (ClassNode=nil) or (ClassNode.Desc<>ctnClass) then
6732 begin
6733 debugln(['FindComponentClass ',AComponentClassName,' is not a class at ',NewTool.CleanPosToStr(NewNode.StartPos,true)]);
6734 exit;
6735 end;
6736 // find inheritance list
6737 InheritedNode:=ClassNode.FirstChild;
6738 while (InheritedNode<>nil) and (InheritedNode.Desc<>ctnClassInheritance) do
6739 InheritedNode:=InheritedNode.NextBrother;
6740 if (InheritedNode=nil) or (InheritedNode.FirstChild=nil) then begin
6741 debugln(['FindComponentClass ',AComponentClassName,' is not a TComponent at ',NewTool.CleanPosToStr(NewNode.StartPos,true)]);
6742 exit;
6743 end;
6744 AncestorNode:=InheritedNode.FirstChild;
6745 AncestorClassName:=GetIdentifier(@NewTool.Src[AncestorNode.StartPos]);
6746 //debugln(['TryFindDeclaration declaration of ',AComponentClassName,' found at ',NewTool.CleanPosToStr(NewNode.StartPos),' ancestor="',AncestorClassName,'"']);
6747
6748 // try unit component
6749 if TryUnitComponent(NewTool.MainFilename,TheModalResult) then
6750 exit(true);
6751
6752 // try lfm
6753 if TryLFM(NewTool.MainFilename,AComponentClassName,TheModalResult) then
6754 exit(true);
6755
6756 // search ancestor in registered classes
6757 if TryRegisteredClasses(AncestorClassName,AncestorClass,TheModalResult) then
6758 exit(true);
6759
6760 {$IFDEF VerboseLFMSearch}
6761 debugln(['TryFindDeclaration declaration of ',AComponentClassName,' found at ',NewTool.CleanPosToStr(NewNode.StartPos),' Ancestor="',AncestorClassName,'", but no lfm and no registered class found']);
6762 {$ENDIF}
6763 finally
6764 Params.Free;
6765 end;
6766 end;
6767
6768 function TryUsedUnitInterface(UnitFilename: string; out TheModalResult: TModalResult): boolean;
6769 var
6770 Code: TCodeBuffer;
6771 AncestorClassName: string;
6772 begin
6773 {$IFDEF VerboseLFMSearch}
6774 debugln([' TryUsedUnitInterface UnitFilename="',UnitFilename,'"']);
6775 {$ENDIF}
6776 Result:=false;
6777 TheModalResult:=mrCancel;
6778 if not FilenameIsPascalSource(UnitFilename) then
6779 begin
6780 {$IFDEF VerboseLFMSearch}
6781 debugln([' TryUsedUnitInterface UnitFilename="',UnitFilename,'" is not a unit']);
6782 {$ENDIF}
6783 exit;
6784 end;
6785 AncestorClassName:='';
6786 Code:=CodeToolBoss.LoadFile(UnitFilename,true,false);
6787 if Code=nil then begin
6788 debugln(['FindComponentClass unable to load ',AnUnitInfo.Filename]);
6789 exit;
6790 end;
6791 if not CodeToolBoss.FindFormAncestor(Code,AComponentClassName,AncestorClassName,true) then
6792 begin
6793 {$IFDEF VerboseLFMSearch}
6794 debugln([' TryUsedUnitInterface FindFormAncestor failed for "',AComponentClassName,'"']);
6795 {$ENDIF}
6796 StoreCodetoolsError;
6797 exit;
6798 end;
6799 if AncestorClassName='' then begin
6800 {$IFDEF VerboseLFMSearch}
6801 debugln([' TryUsedUnitInterface FindFormAncestor failed silently for "',AComponentClassName,'"']);
6802 {$ENDIF}
6803 exit;
6804 end;
6805 if TryRegisteredClasses(AncestorClassName,AncestorClass,TheModalResult) then
6806 exit(true);
6807 end;
6808
6809 var
6810 UsedUnitFilenames: TStrings;
6811 i: Integer;
6812 begin
6813 Result:=mrCancel;
6814 AComponentClass:=nil;
6815 ComponentUnitInfo:=nil;
6816 AncestorClass:=nil;
6817 LFMFilename:='';
6818 CTErrorMsg:='';
6819 CTErrorCode:=nil;
6820 CTErrorLine:=0;
6821 CTErrorCol:=0;
6822
6823 if not IsValidIdent(AComponentClassName) then
6824 begin
6825 DebugLn(['FindComponentClass invalid component class name "',AComponentClassName,'"']);
6826 exit(mrCancel);
6827 end;
6828
6829 // search component lfm
6830 {$ifdef VerboseFormEditor}
6831 debugln('FindComponentClass START ',AnUnitInfo.Filename,' AComponentClassName=',AComponentClassName);
6832 {$endif}
6833 // first search the resource of ComponentUnitInfo
6834 if AnUnitInfo<>nil then begin
6835 if TryUnitComponent(AnUnitInfo.Filename,Result) then exit;
6836 end;
6837
6838 // then try registered global classes
6839 if TryRegisteredClasses(AComponentClassName,AComponentClass,Result) then exit;
6840
6841 // search in used units
6842 UsedUnitFilenames:=nil;
6843 try
6844 if not CodeToolBoss.FindUsedUnitFiles(AnUnitInfo.Source,UsedUnitFilenames)
6845 then begin
6846 MainIDE.DoJumpToCodeToolBossError;
6847 Result:=mrCancel;
6848 exit;
6849 end;
6850
6851 {$IFDEF VerboseLFMSearch}
6852 if (UsedUnitFilenames=nil) or (UsedUnitFilenames.Count=0) then
6853 debugln(['FindComponentClass unit has no main uses']);
6854 {$ENDIF}
6855
6856 if (UsedUnitFilenames<>nil) then begin
6857 // search for every used unit the .lfm file
6858 for i:=UsedUnitFilenames.Count-1 downto 0 do begin
6859 if TryLFM(UsedUnitFilenames[i],AComponentClassName,Result) then exit;
6860 end;
6861 // search class via codetools
6862 if TryFindDeclaration(Result) then exit;
6863 // search the class in every used unit
6864 for i:=UsedUnitFilenames.Count-1 downto 0 do begin
6865 if TryUsedUnitInterface(UsedUnitFilenames[i],Result) then exit;
6866 end;
6867 end;
6868 finally
6869 UsedUnitFilenames.Free;
6870 end;
6871
6872 // not found
6873 if Quiet then exit(mrCancel);
6874
6875 // show codetool error
6876 if (CTErrorMsg<>'') and (not Quiet) then begin
6877 CodeToolBoss.SetError(20170421203251,CTErrorCode,CTErrorLine,CTErrorCol,CTErrorMsg);
6878 MainIDE.DoJumpToCodeToolBossError;
6879 Result:=mrAbort;
6880 exit;
6881 end;
6882
6883 // just not found
6884 Result:=mrCancel;
6885 end;
6886
6887 function LoadComponentDependencyHidden(AnUnitInfo: TUnitInfo;
6888 const AComponentClassName: string; Flags: TOpenFlags; MustHaveLFM: boolean;
6889 out AComponentClass: TComponentClass; out ComponentUnitInfo: TUnitInfo;
6890 out AncestorClass: TComponentClass; const IgnoreBtnText: string): TModalResult;
6891 { Possible results:
6892 mrOk:
6893 - AComponentClass<>nil and ComponentUnitInfo<>nil
6894 designer component
6895 - AComponentClass<>nil and ComponentUnitInfo=nil
6896 registered componentclass
6897 - Only for MustHaveLFM=false: AncestorClass<>nil
6898 componentclass does not exist, but the ancestor is a registered class
6899 mrCancel:
6900 not found, skip this form
6901 mrAbort:
6902 not found, user wants to stop all pending operations
6903 mrIgnore:
6904 not found, user wants to skip this step and continue
6905 }
6906
6907 function TryLFM(LFMFilename: string; out TheModalResult: TModalResult): boolean;
6908 var
6909 UnitFilename: String;
6910 CurUnitInfo: TUnitInfo;
6911 LFMCode: TCodeBuffer;
6912 LFMClassName: String;
6913 LFMType: String;
6914 UnitCode: TCodeBuffer;
6915 begin
6916 Result:=false;
6917 TheModalResult:=mrCancel;
6918 // load lfm
6919 TheModalResult:=LoadCodeBuffer(LFMCode,LFMFilename,[lbfCheckIfText],true);
6920 if TheModalResult<>mrOk then begin
6921 {$IFDEF VerboseLFMSearch}
6922 debugln([' TryLFM LoadCodeBuffer failed ',LFMFilename]);
6923 {$ENDIF}
6924 exit(TheModalResult=mrAbort);
6925 end;
6926 // check if the unit component is already loaded
6927 UnitFilename:=ChangeFileExt(LFMFilename,'.pas');
6928 CurUnitInfo:=Project1.UnitInfoWithFilename(UnitFilename);
6929 if CurUnitInfo=nil then begin
6930 UnitFilename:=ChangeFileExt(LFMFilename,'.pp');
6931 CurUnitInfo:=Project1.UnitInfoWithFilename(UnitFilename);
6932 end;
6933 ReadLFMHeader(LFMCode.Source,LFMClassName,LFMType);
6934 if CurUnitInfo<>nil then begin
6935 if (CurUnitInfo.Component<>nil) then begin
6936 // component already loaded
6937 if SysUtils.CompareText(CurUnitInfo.Component.ClassName,LFMClassName)<>0
6938 then begin
6939 {$IFDEF VerboseLFMSearch}
6940 debugln([' TryLFM ERROR lfmclass=',LFMClassName,' unit.component=',DbgSName(CurUnitInfo.Component)]);
6941 {$ENDIF}
6942 IDEMessageDialog('Error','Unable to load "'+LFMFilename+'".'
6943 +' The component '+DbgSName(CurUnitInfo.Component)
6944 +' is already loaded for unit "'+CurUnitInfo.Filename+'"'#13
6945 +'LFM contains a different class name "'+LFMClassName+'".',
6946 mtError,[mbCancel]);
6947 TheModalResult:=mrAbort;
6948 exit(true);
6949 end;
6950 ComponentUnitInfo:=CurUnitInfo;
6951 AComponentClass:=TComponentClass(ComponentUnitInfo.Component.ClassType);
6952 TheModalResult:=mrOK;
6953 exit(true);
6954 end;
6955 end else begin
6956 // load unit source
6957 UnitFilename:=ChangeFileExt(LFMFilename,'.pas');
6958 if not FileExistsCached(UnitFilename) then
6959 UnitFilename:=ChangeFileExt(LFMFilename,'.pp');
6960 TheModalResult:=LoadCodeBuffer(UnitCode,UnitFilename,[lbfCheckIfText],true);
6961 if TheModalResult<>mrOk then exit(TheModalResult=mrAbort);
6962 // create unit info
6963 CurUnitInfo:=TUnitInfo.Create(UnitCode);
6964 CurUnitInfo.ReadUnitNameFromSource(true);
6965 Project1.AddFile(CurUnitInfo,false);
6966 end;
6967
6968 // load resource hidden
6969 TheModalResult:=LoadLFM(CurUnitInfo,LFMCode,
6970 Flags+[ofLoadHiddenResource],[]);
6971 if (TheModalResult=mrOk) then begin
6972 ComponentUnitInfo:=CurUnitInfo;
6973 AComponentClass:=TComponentClass(ComponentUnitInfo.Component.ClassType);
6974 {$if defined(VerboseFormEditor) or defined(VerboseLFMSearch)}
6975 debugln('LoadComponentDependencyHidden Wanted=',AComponentClassName,' Class=',AComponentClass.ClassName);
6976 {$endif}
6977 TheModalResult:=mrOk;
6978 exit(true);
6979 end else begin
6980 debugln('LoadComponentDependencyHidden Failed to load component ',AComponentClassName);
6981 TheModalResult:=mrCancel;
6982 end;
6983 end;
6984
6985 var
6986 Quiet, HideAbort: Boolean;
6987 LFMFilename, MsgText: string;
6988 begin
6989 Result:=mrCancel;
6990 AComponentClass:=nil;
6991 Quiet:=([ofProjectLoading,ofQuiet]*Flags<>[]);
6992 HideAbort:=not (ofProjectLoading in Flags);
6993
6994 if not IsValidIdent(AComponentClassName) then
6995 begin
6996 DebugLn(['LoadComponentDependencyHidden invalid component class name "',AComponentClassName,'"']);
6997 exit(mrCancel);
6998 end;
6999
7000 // check for cycles
7001 if AnUnitInfo.LoadingComponent then begin
7002 Result:=IDEQuestionDialogAb(lisCodeTemplError,
7003 Format(lisUnableToLoadTheComponentClassBecauseItDependsOnIts, [AComponentClassName]),
7004 mtError, [mrCancel, lisCancelLoadingThisComponent], HideAbort);
7005 exit;
7006 end;
7007
7008 AnUnitInfo.LoadingComponent:=true;
7009 try
7010 // search component lfm
7011 {$if defined(VerboseFormEditor) or defined(VerboseLFMSearch)}
7012 debugln('LoadComponentDependencyHidden ',AnUnitInfo.Filename,' AComponentClassName=',AComponentClassName,' AComponentClass=',dbgsName(AComponentClass));
7013 {$endif}
7014 Result:=FindComponentClass(AnUnitInfo,AComponentClassName,Quiet,
7015 ComponentUnitInfo,AComponentClass,LFMFilename,AncestorClass);
7016 { $if defined(VerboseFormEditor) or defined(VerboseLFMSearch)}
7017 debugln('LoadComponentDependencyHidden ',AnUnitInfo.Filename,' AComponentClassName=',AComponentClassName,' AComponentClass=',dbgsName(AComponentClass),' AncestorClass=',DbgSName(AncestorClass),' LFMFilename=',LFMFilename);
7018 { $endif}
7019
7020 //- AComponentClass<>nil and ComponentUnitInfo<>nil
7021 // designer component
7022 //- AComponentClass<>nil and ComponentUnitInfo=nil
7023 // registered componentclass
7024 //- LFMFilename<>''
7025 // lfm of an used unit
7026 //- AncestorClass<>nil
7027 // componentclass does not exist, but the ancestor is a registered class
7028
7029 if (Result=mrOk) and (AComponentClass=nil) and (LFMFilename<>'') then begin
7030 TryLFM(LFMFilename,Result);
7031 exit;
7032 end;
7033
7034 if MustHaveLFM and (AComponentClass=nil) then
7035 Result:=mrCancel;
7036 if Result=mrAbort then exit;
7037 if Result<>mrOk then begin
7038 MsgText:=Format(lisUnableToFindTheComponentClassItIsNotRegisteredViaR, [
7039 AComponentClassName, LineEnding, LineEnding, LineEnding, AnUnitInfo.Filename]);
7040 if IgnoreBtnText<>'' then
7041 Result:=IDEQuestionDialogAb(lisCodeTemplError, MsgText, mtError,
7042 [mrCancel, lisCancelLoadingThisComponent,
7043 mrIgnore, IgnoreBtnText], HideAbort)
7044 else
7045 Result:=IDEQuestionDialogAb(lisCodeTemplError, MsgText, mtError,
7046 [mrCancel, lisCancelLoadingThisComponent], HideAbort);
7047 end;
7048 finally
7049 AnUnitInfo.LoadingComponent:=false;
7050 end;
7051 end;
7052
7053 function LoadIDECodeBuffer(var ACodeBuffer: TCodeBuffer;
7054 const AFilename: string; Flags: TLoadBufferFlags; ShowAbort: boolean): TModalResult;
7055 begin
7056 if (Project1<>nil)
7057 and (Project1.UnitInfoWithFilename(AFilename,[pfsfOnlyEditorFiles])<>nil) then
7058 Exclude(Flags,lbfUpdateFromDisk);
7059 Result:=LoadCodeBuffer(ACodeBuffer,AFilename,Flags,ShowAbort);
7060 end;
7061
7062 function CloseUnitComponent(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult;
7063
7064 procedure FreeUnusedComponents;
7065 var
7066 CompUnitInfo: TUnitInfo;
7067 begin
7068 CompUnitInfo:=Project1.FirstUnitWithComponent;
7069 Project1.UpdateUnitComponentDependencies;
7070 while CompUnitInfo<>nil do begin
7071 //DebugLn(['FreeUnusedComponents ',CompUnitInfo.Filename,' ',dbgsName(CompUnitInfo.Component),' UnitComponentIsUsed=',UnitComponentIsUsed(CompUnitInfo,true)]);
7072 if not UnitComponentIsUsed(CompUnitInfo,true) then begin
7073 // close the unit component
7074 CloseUnitComponent(CompUnitInfo,Flags);
7075 // this has recursively freed all components, so exit here
7076 exit;
7077 end;
7078 CompUnitInfo:=CompUnitInfo.NextUnitWithComponent;
7079 end;
7080 end;
7081
7082 var
7083 OldDesigner: TIDesigner;
7084 AForm: TCustomForm;
7085 LookupRoot: TComponent;
7086 ComponentStillUsed: Boolean;
7087 begin
7088 LookupRoot:=AnUnitInfo.Component;
7089 if LookupRoot=nil then exit(mrOk);
7090 {$IFDEF VerboseIDEMultiForm}
7091 DebugLn(['CloseUnitComponent ',AnUnitInfo.Filename,' ',dbgsName(LookupRoot)]);
7092 {$ENDIF}
7093
7094 Project1.LockUnitComponentDependencies; // avoid circles
7095 try
7096 // save
7097 if (cfSaveFirst in Flags) and (AnUnitInfo.OpenEditorInfoCount > 0)
7098 and (not AnUnitInfo.IsReverting) then begin
7099 Result:=SaveEditorFile(AnUnitInfo.OpenEditorInfo[0].EditorComponent,[sfCheckAmbiguousFiles]);
7100 if Result<>mrOk then begin
7101 DebugLn(['CloseUnitComponent DoSaveEditorFile failed']);
7102 exit;
7103 end;
7104 end;
7105
7106 // close dependencies
7107 if cfCloseDependencies in Flags then begin
7108 {$IFDEF VerboseIDEMultiForm}
7109 DebugLn(['CloseUnitComponent cfCloseDependencies ',AnUnitInfo.Filename,' ',dbgsName(LookupRoot)]);
7110 {$ENDIF}
7111 Result:=CloseDependingUnitComponents(AnUnitInfo,Flags);
7112 if Result<>mrOk then begin
7113 DebugLn(['CloseUnitComponent CloseDependingUnitComponents failed']);
7114 exit;
7115 end;
7116 // now only soft dependencies are left. The component can be freed.
7117 end;
7118
7119 AForm:=FormEditor1.GetDesignerForm(LookupRoot);
7120 if AForm<>nil then
7121 OldDesigner:=AForm.Designer
7122 else
7123 OldDesigner:=nil;
7124 if MainIDE.LastFormActivated=AForm then
7125 MainIDE.LastFormActivated:=nil;
7126 ComponentStillUsed:=(not (cfCloseDependencies in Flags))
7127 and UnitComponentIsUsed(AnUnitInfo,false);
7128 {$IFDEF VerboseTFrame}
7129 DebugLn(['CloseUnitComponent ',AnUnitInfo.Filename,' ComponentStillUsed=',ComponentStillUsed,' UnitComponentIsUsed=',UnitComponentIsUsed(AnUnitInfo,false),' ',dbgs(AnUnitInfo.Flags),' DepAncestor=',AnUnitInfo.FindUsedByComponentDependency([ucdtAncestor])<>nil,' DepInline=',AnUnitInfo.FindUsedByComponentDependency([ucdtInlineClass])<>nil]);
7130 {$ENDIF}
7131 if (OldDesigner=nil) then begin
7132 // hidden component
7133 //DebugLn(['CloseUnitComponent freeing hidden component without designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7134 if ComponentStillUsed then begin
7135 // hidden component is still used => keep it
7136 {$IFDEF VerboseIDEMultiForm}
7137 DebugLn(['CloseUnitComponent hidden component is still used => keep it ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7138 {$ENDIF}
7139 end else begin
7140 // hidden component is not used => free it
7141 {$IFDEF VerboseIDEMultiForm}
7142 DebugLn(['CloseUnitComponent hidden component is not used => free it ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7143 {$ENDIF}
7144 try
7145 FormEditor1.DeleteComponent(LookupRoot,true);
7146 finally
7147 AnUnitInfo.Component:=nil;
7148 end;
7149 FreeUnusedComponents;
7150 end;
7151 end else begin
7152 // component with designer
7153 AnUnitInfo.LoadedDesigner:=false;
7154 if ComponentStillUsed then begin
7155 // free designer, keep component hidden
7156 {$IFDEF VerboseIDEMultiForm}
7157 DebugLn(['CloseUnitComponent hiding component and freeing designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7158 {$ENDIF}
7159 OldDesigner.PrepareFreeDesigner(false);
7160 end else begin
7161 // free designer and design form
7162 {$IFDEF VerboseIDEMultiForm}
7163 DebugLn(['CloseUnitComponent freeing component and designer: ',AnUnitInfo.Filename,' ',DbgSName(AnUnitInfo.Component)]);
7164 {$ENDIF}
7165 try
7166 OldDesigner.PrepareFreeDesigner(true);
7167 finally
7168 AnUnitInfo.Component:=nil;
7169 end;
7170 end;
7171 Project1.InvalidateUnitComponentDesignerDependencies;
7172 FreeUnusedComponents;
7173 end;
7174 finally
7175 Project1.UnlockUnitComponentDependencies;
7176 end;
7177 Result:=mrOk;
7178 end;
7179
7180 function CloseDependingUnitComponents(AnUnitInfo: TUnitInfo; Flags: TCloseFlags): TModalResult;
7181 var
7182 UserAsked: Boolean;
7183
7184 function CloseNext(var ModResult: TModalresult;
7185 Types: TUnitCompDependencyTypes): boolean;
7186 var
7187 DependingUnitInfo: TUnitInfo;
7188 DependenciesFlags: TCloseFlags;
7189 begin
7190 ModResult:=mrOk;
7191 repeat
7192 DependingUnitInfo:=Project1.UnitUsingComponentUnit(AnUnitInfo,Types);
7193 if DependingUnitInfo=nil then break;
7194 if (not UserAsked) and (not (cfQuiet in Flags))
7195 and (not DependingUnitInfo.IsReverting) then begin
7196 // ToDo: collect in advance all components to close and show user the list
7197 ModResult:=IDEQuestionDialog('Close component?',
7198 'Close component '+dbgsName(DependingUnitInfo.Component)+'?',
7199 mtConfirmation,[mrYes,mrAbort]);
7200 if ModResult<>mrYes then exit(false);
7201 UserAsked:=true;
7202 end;
7203 // close recursively
7204 DependenciesFlags:=Flags+[cfCloseDependencies];
7205 if cfSaveDependencies in Flags then
7206 Include(DependenciesFlags,cfSaveFirst);
7207 ModResult:=CloseUnitComponent(DependingUnitInfo,DependenciesFlags);
7208 if ModResult<>mrOk then exit(false);
7209 until false;
7210 ModResult:=mrOk;
7211 Result:=true;
7212 end;
7213
7214 begin
7215 Result:=mrOk;
7216 UserAsked:=false;
7217 Project1.LockUnitComponentDependencies;
7218 try
7219 // Important:
7220 // This function is called recursively.
7221 // It is important that first the hard, non cyclic dependencies
7222 // are freed in the correct order.
7223 // After that the soft, cyclic dependencies can be freed in any order.
7224
7225 // first close all descendants recursively
7226 // This must happen in the right order (descendants before ancestor)
7227 if not CloseNext(Result,[ucdtAncestor]) then exit;
7228
7229 // then close all nested descendants recursively
7230 // This must happen in the right order (nested descendants before ancestor)
7231 if not CloseNext(Result,[ucdtInlineClass]) then exit;
7232
7233 // then close all referring components
7234 // These can build cycles and can be freed in any order.
7235 if not CloseNext(Result,[ucdtProperty]) then exit;
7236 finally
7237 Project1.UnlockUnitComponentDependencies;
7238 end;
7239 Result:=mrOk;
7240 end;
7241
7242 function UnitComponentIsUsed(AnUnitInfo: TUnitInfo;
7243 CheckHasDesigner: boolean): boolean;
7244 // if CheckHasDesigner=true and AnUnitInfo has a designer (visible) return true
7245 // otherwise check if another unit needs AnUnitInfo
7246 var
7247 LookupRoot: TComponent;
7248 begin
7249 Result:=false;
7250 LookupRoot:=AnUnitInfo.Component;
7251 if LookupRoot=nil then exit;
7252 // check if a designer or another component uses this component
7253 Project1.UpdateUnitComponentDependencies;
7254 if Project1.UnitComponentIsUsed(AnUnitInfo,CheckHasDesigner) then
7255 exit(true);
7256 //DebugLn(['UnitComponentIsUsed ',AnUnitInfo.Filename,' ',dbgs(AnUnitInfo.Flags)]);
7257 end;
7258
7259 function RemoveFilesFromProject(UnitInfos: TFPList): TModalResult;
7260 var
7261 AnUnitInfo: TUnitInfo;
7262 ShortUnitName, UnitPath: String;
7263 ObsoleteUnitPaths, ObsoleteIncPaths: String;
7264 i: Integer;
7265 Dummy: Boolean;
7266 begin
7267 Result:=mrOk;
7268 if UnitInfos=nil then exit;
7269 // check if something will change
7270 i:=UnitInfos.Count-1;
7271 while (i>=0) and (not TUnitInfo(UnitInfos[i]).IsPartOfProject) do dec(i);
7272 if i<0 then exit;
7273 // check ToolStatus
7274 if (MainIDE.ToolStatus in [itCodeTools,itCodeToolAborting]) then begin
7275 debugln('RemoveUnitsFromProject wrong ToolStatus ',dbgs(ord(MainIDE.ToolStatus)));
7276 exit;
7277 end;
7278 // commit changes from source editor to codetools
7279 SaveEditorChangesToCodeCache(nil);
7280
7281 ObsoleteUnitPaths:='';
7282 ObsoleteIncPaths:='';
7283 Project1.BeginUpdate(true);
7284 try
7285 for i:=0 to UnitInfos.Count-1 do begin
7286 AnUnitInfo:=TUnitInfo(UnitInfos[i]);
7287 //debugln(['RemoveUnitsFromProject Unit ',AnUnitInfo.Filename]);
7288 if not AnUnitInfo.IsPartOfProject then continue;
7289 UnitPath:=ChompPathDelim(ExtractFilePath(AnUnitInfo.Filename));
7290 AnUnitInfo.IsPartOfProject:=false;
7291 Project1.Modified:=true;
7292 if FilenameIsPascalUnit(AnUnitInfo.Filename) then begin
7293 if FilenameIsAbsolute(AnUnitInfo.Filename) then
7294 ObsoleteUnitPaths:=MergeSearchPaths(ObsoleteUnitPaths,UnitPath);
7295 // remove from project's unit section
7296 if (Project1.MainUnitID>=0)
7297 and (pfMainUnitIsPascalSource in Project1.Flags)
7298 then begin
7299 ShortUnitName:=ExtractFileNameOnly(AnUnitInfo.Filename);
7300 //debugln(['RemoveUnitsFromProject UnitName=',ShortUnitName]);
7301 if (ShortUnitName<>'') then begin
7302 Dummy:=CodeToolBoss.RemoveUnitFromAllUsesSections(
7303 Project1.MainUnitInfo.Source,ShortUnitName);
7304 if not Dummy then begin
7305 MainIDE.DoJumpToCodeToolBossError;
7306 exit(mrCancel);
7307 end;
7308 end;
7309 end;
7310 // remove CreateForm statement from project
7311 if (Project1.MainUnitID>=0)
7312 and (pfMainUnitHasCreateFormStatements in Project1.Flags)
7313 and (AnUnitInfo.ComponentName<>'') then begin
7314 Dummy:=Project1.RemoveCreateFormFromProjectFile(
7315 'T'+AnUnitInfo.ComponentName,AnUnitInfo.ComponentName);
7316 if not Dummy then begin
7317 MainIDE.DoJumpToCodeToolBossError;
7318 exit(mrCancel);
7319 end;
7320 end;
7321 end;
7322 if CompareFileExt(AnUnitInfo.Filename,'.inc',false)=0 then
7323 // include file
7324 if FilenameIsAbsolute(AnUnitInfo.Filename) then
7325 ObsoleteIncPaths:=MergeSearchPaths(ObsoleteIncPaths,UnitPath);
7326 end;
7327
7328 // removed directories still used for ObsoleteUnitPaths, ObsoleteIncPaths
7329 AnUnitInfo:=Project1.FirstPartOfProject;
7330 while AnUnitInfo<>nil do begin
7331 if FilenameIsAbsolute(AnUnitInfo.Filename) then begin
7332 UnitPath:=ChompPathDelim(ExtractFilePath(AnUnitInfo.Filename));
7333 if FilenameIsPascalUnit(AnUnitInfo.Filename) then
7334 ObsoleteUnitPaths:=RemoveSearchPaths(ObsoleteUnitPaths,UnitPath);
7335 if CompareFileExt(AnUnitInfo.Filename,'.inc',false)=0 then
7336 ObsoleteIncPaths:=RemoveSearchPaths(ObsoleteIncPaths,UnitPath);
7337 end;
7338 AnUnitInfo:=AnUnitInfo.NextPartOfProject;
7339 end;
7340
7341 // check if compiler options contain paths of ObsoleteUnitPaths
7342 if ObsoleteUnitPaths<>'' then
7343 RemovePathFromBuildModes(ObsoleteUnitPaths, pcosUnitPath);
7344 // or paths of ObsoleteIncPaths
7345 if ObsoleteIncPaths<>'' then
7346 RemovePathFromBuildModes(ObsoleteIncPaths, pcosIncludePath);
7347
7348 finally
7349 // all changes were handled automatically by events, just clear the logs
7350 CodeToolBoss.SourceCache.ClearAllSourceLogEntries;
7351 Project1.EndUpdate;
7352 end;
7353 end;
7354
7355 // methods for open project, create project from source
7356
7357 function CompleteLoadingProjectInfo: TModalResult;
7358 begin
7359 MainIDE.UpdateCaption;
7360 EnvironmentOptions.LastSavedProjectFile:=Project1.ProjectInfoFile;
7361 MainIDE.SaveEnvironment;
7362
7363 MainBuildBoss.SetBuildTargetProject1(false);
7364
7365 // load required packages
7366 PkgBoss.OpenProjectDependencies(Project1,true);
7367
7368 Project1.DefineTemplates.AllChanged;
7369 //DebugLn('CompleteLoadingProjectInfo ',Project1.IDAsString);
7370 Project1.DefineTemplates.Active:=true;
7371
7372 Result:=mrOk;
7373 end;
7374
7375 // Methods for 'save project'
7376
7377 function SaveProjectInfo(var Flags: TSaveFlags): TModalResult;
7378 var
7379 MainUnitInfo: TUnitInfo;
7380 MainUnitSrcEdit: TSourceEditor;
7381 DestFilename: String;
7382 SkipSavingMainSource: Boolean;
7383 begin
7384 Result:=mrOk;
7385 Project1.ActiveWindowIndexAtStart := SourceEditorManager.ActiveSourceWindowIndex;
7386
7387 // update source notebook page names
7388 UpdateSourceNames;
7389
7390 // find mainunit
7391 GetMainUnit(MainUnitInfo,MainUnitSrcEdit,true);
7392
7393 // save project specific settings of the source editor
7394 SaveSourceEditorProjectSpecificSettings;
7395
7396 if Project1.IsVirtual
7397 and (not (sfDoNotSaveVirtualFiles in Flags)) then
7398 Include(Flags,sfSaveAs);
7399 if ([sfSaveAs,sfSaveToTestDir]*Flags=[sfSaveAs]) then begin
7400 // let user choose a filename
7401 Result:=ShowSaveProjectAsDialog(sfSaveMainSourceAs in Flags);
7402 if Result<>mrOk then begin
7403 debugln(['Info: (lazarus) [SaveProjectInfo] ShowSaveProjectAsDialog failed']);
7404 exit;
7405 end;
7406 Flags:=Flags-[sfSaveAs,sfSaveMainSourceAs];
7407 end;
7408
7409 // update HasResources information
7410 UpdateProjectResourceInfo;
7411
7412 // save project info file
7413 //debugln(['SaveProjectInfo ',Project1.ProjectInfoFile,' Test=',sfSaveToTestDir in Flags,' Virt=',Project1.IsVirtual]);
7414 if (not (sfSaveToTestDir in Flags))
7415 and (not Project1.IsVirtual) then begin
7416 Result:=Project1.WriteProject([],'',EnvironmentOptions.BuildMatrixOptions);
7417 if Result=mrAbort then begin
7418 debugln(['Info: (lazarus) [SaveProjectInfo] Project1.WriteProject failed']);
7419 exit;
7420 end;
7421 EnvironmentOptions.LastSavedProjectFile:=Project1.ProjectInfoFile;
7422 IDEProtocolOpts.LastProjectLoadingCrashed := False;
7423 AddRecentProjectFile(Project1.ProjectInfoFile);
7424 MainIDE.SaveIncludeLinks;
7425 MainIDE.UpdateCaption;
7426 end;
7427
7428 // save main source
7429 if (MainUnitInfo<>nil) and (not (sfDoNotSaveVirtualFiles in flags)) then
7430 begin
7431 if not (sfSaveToTestDir in Flags) then
7432 DestFilename := MainUnitInfo.Filename
7433 else
7434 DestFilename := MainBuildBoss.GetTestUnitFilename(MainUnitInfo);
7435
7436 if MainUnitInfo.OpenEditorInfoCount > 0 then
7437 begin
7438 // loaded in source editor
7439 Result:=SaveEditorFile(MainUnitInfo.OpenEditorInfo[0].EditorComponent,
7440 [sfProjectSaving]+[sfSaveToTestDir,sfCheckAmbiguousFiles]*Flags);
7441 if Result=mrAbort then begin
7442 debugln(['Info: (lazarus) [SaveProjectInfo] SaveEditorFile MainUnitInfo failed "',DestFilename,'"']);
7443 exit;
7444 end;
7445 end else
7446 begin
7447 // not loaded in source editor (hidden)
7448 SkipSavingMainSource := false;
7449 if not (sfSaveToTestDir in Flags) and not MainUnitInfo.NeedsSaveToDisk then
7450 SkipSavingMainSource := true;
7451 if (not SkipSavingMainSource) and (MainUnitInfo.Source<>nil) then
7452 begin
7453 Result:=SaveCodeBufferToFile(MainUnitInfo.Source, DestFilename);
7454 if Result=mrAbort then begin
7455 debugln(['Info: (lazarus) [SaveProjectInfo] SaveEditorFile failed "',DestFilename,'"']);
7456 exit;
7457 end;
7458 end;
7459 end;
7460
7461 // clear modified flags
7462 if not (sfSaveToTestDir in Flags) then
7463 begin
7464 if (Result=mrOk) then begin
7465 if MainUnitInfo<>nil then MainUnitInfo.ClearModifieds;
7466 if MainUnitSrcEdit<>nil then MainUnitSrcEdit.Modified:=false;
7467 end;
7468 end;
7469 end;
7470 end;
7471
7472 procedure GetMainUnit(out MainUnitInfo: TUnitInfo;
7473 out MainUnitSrcEdit: TSourceEditor; UpdateModified: boolean);
7474 begin
7475 MainUnitSrcEdit:=nil;
7476 if Project1.MainUnitID>=0 then begin
7477 MainUnitInfo:=Project1.MainUnitInfo;
7478 if MainUnitInfo.OpenEditorInfoCount > 0 then begin
7479 MainUnitSrcEdit := TSourceEditor(MainUnitInfo.OpenEditorInfo[0].EditorComponent);
7480 if UpdateModified and MainUnitSrcEdit.Modified then
7481 MainUnitSrcEdit.UpdateCodeBuffer;
7482 end;
7483 end else
7484 MainUnitInfo:=nil;
7485 end;
7486
7487 procedure SaveSrcEditorProjectSpecificSettings(AnEditorInfo: TUnitEditorInfo);
7488 var
7489 ASrcEdit: TSourceEditor;
7490 begin
7491 ASrcEdit := TSourceEditor(AnEditorInfo.EditorComponent);
7492 if ASrcEdit=nil then exit;
7493 AnEditorInfo.TopLine:=ASrcEdit.EditorComponent.TopLine;
7494 AnEditorInfo.CursorPos:=ASrcEdit.EditorComponent.CaretXY;
7495 AnEditorInfo.FoldState := ASrcEdit.EditorComponent.FoldState;
7496 end;
7497
7498 procedure SaveSourceEditorProjectSpecificSettings;
7499 var
7500 i: Integer;
7501 begin
7502 for i := 0 to Project1.AllEditorsInfoCount - 1 do
7503 SaveSrcEditorProjectSpecificSettings(Project1.AllEditorsInfo[i]);
7504 end;
7505
7506 procedure UpdateProjectResourceInfo;
7507 var
7508 AnUnitInfo: TUnitInfo;
7509 LFMFilename: String;
7510 begin
7511 AnUnitInfo:=Project1.FirstPartOfProject;
7512 while AnUnitInfo<>nil do begin
7513 if (not AnUnitInfo.HasResources)
7514 and (not AnUnitInfo.IsVirtual) and FilenameIsPascalUnit(AnUnitInfo.Filename)
7515 then begin
7516 LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lfm');
7517 if not FileExistsCached(LFMFilename) then
7518 LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.dfm');
7519 AnUnitInfo.HasResources:=FileExistsCached(LFMFilename);
7520 end;
7521 AnUnitInfo:=AnUnitInfo.NextPartOfProject;
7522 end;
7523 end;
7524
7525 function ShowSaveProjectAsDialog(UseMainSourceFile: boolean): TModalResult;
7526 var
7527 MainUnitSrcEdit: TSourceEditor;
7528 MainUnitInfo: TUnitInfo;
7529 SaveDialog: TIDESaveDialog;
7530 NewBuf, OldBuf: TCodeBuffer;
7531 TitleWasDefault: Boolean;
7532 NewLPIFilename, NewProgramFN, NewProgramName, AFilename, NewTargetFN: String;
7533 AText, ACaption, Ext: string;
7534 OldSourceCode, OldProjectDir, prDir: string;
7535 i: Integer;
7536 begin
7537 Project1.BeginUpdate(false);
7538 try
7539 OldProjectDir:=Project1.Directory;
7540
7541 if Project1.MainUnitInfo = nil then
7542 UseMainSourceFile := False;
7543
7544 SaveDialog:=IDESaveDialogClass.Create(nil);
7545 try
7546 InputHistories.ApplyFileDialogSettings(SaveDialog);
7547 AFilename:='';
7548 // build a nice project info filename suggestion
7549 if UseMainSourceFile and (Project1.MainUnitID>=0) then
7550 AFilename:=Project1.MainUnitInfo.Unit_Name;
7551 if AFilename='' then
7552 AFilename:=ExtractFileName(Project1.ProjectInfoFile);
7553 if AFilename='' then
7554 AFilename:=ExtractFileName(Project1.MainFilename);
7555 if AFilename='' then
7556 AFilename:=Trim(Project1.GetTitle);
7557 if AFilename='' then
7558 AFilename:='project1';
7559 Ext := LowerCase(ExtractFileExt(AFilename));
7560 if UseMainSourceFile then
7561 begin
7562 if (Ext = '') or (not FilenameIsPascalSource(AFilename)) then
7563 AFilename := ChangeFileExt(AFilename, '.pas');
7564 end else
7565 begin
7566 if (Ext = '') or FilenameIsPascalSource(AFilename) then
7567 AFilename := ChangeFileExt(AFilename, '.lpi');
7568 end;
7569 Ext := ExtractFileExt(AFilename);
7570 SaveDialog.Title := Format(lisSaveProject, [Project1.GetTitleOrName, Ext]);
7571 SaveDialog.FileName := AFilename;
7572 // Note: add *.* filter, so users can see the files in the target directory
7573 SaveDialog.Filter := '*' + Ext + '|' + '*' + Ext
7574 + '|' + dlgFilterAll + ' (' + GetAllFilesMask + ')|' + GetAllFilesMask;
7575 SaveDialog.DefaultExt := Ext;
7576 if not Project1.IsVirtual then
7577 SaveDialog.InitialDir := Project1.Directory;
7578
7579 repeat
7580 Result:=mrCancel;
7581 NewLPIFilename:=''; // the project info file name
7582 NewProgramName:=''; // the pascal program identifier
7583 NewProgramFN:=''; // the program source filename
7584
7585 if not SaveDialog.Execute then
7586 exit; // user cancels
7587 AFilename:=ExpandFileNameUTF8(SaveDialog.FileName);
7588 Assert(FilenameIsAbsolute(AFilename),'ShowSaveProjectAsDialog: buggy ExpandFileNameUTF8');
7589
7590 // check program name
7591 NewProgramName:=ExtractFileNameOnly(AFilename);
7592 if (NewProgramName='') or (not IsValidUnitName(NewProgramName)) then begin
7593 Result:=IDEMessageDialog(lisInvalidProjectFilename,
7594 Format(lisisAnInvalidProjectNamePleaseChooseAnotherEGProject,[SaveDialog.Filename,LineEnding]),
7595 mtInformation,[mbRetry,mbAbort]);
7596 if Result=mrAbort then exit;
7597 continue; // try again
7598 end;
7599
7600 // append default extension
7601 if UseMainSourceFile then
7602 begin
7603 NewLPIFilename:=ChangeFileExt(AFilename,'.lpi');
7604 end else
7605 begin
7606 NewLPIFilename:=AFilename;
7607 if ExtractFileExt(NewLPIFilename)='' then
7608 NewLPIFilename:=NewLPIFilename+'.lpi';
7609 end;
7610
7611 // apply naming conventions
7612 // rename to lowercase is not needed for main source
7613
7614 if Project1.MainUnitID >= 0 then
7615 begin
7616 // check mainunit filename
7617 Ext := ExtractFileExt(Project1.MainUnitInfo.Filename);
7618 if Ext = '' then Ext := '.pas';
7619 if UseMainSourceFile then
7620 NewProgramFN := ExtractFileName(AFilename)
7621 else
7622 NewProgramFN := NewProgramName + Ext;
7623 NewProgramFN := ExtractFilePath(NewLPIFilename) + NewProgramFN;
7624 if (CompareFilenames(NewLPIFilename, NewProgramFN) = 0) then
7625 begin
7626 ACaption:=lisChooseADifferentName;
7627 AText:=Format(lisTheProjectInfoFileIsEqualToTheProjectMainSource,[NewLPIFilename,LineEnding]);
7628 Result:=IDEMessageDialog(ACaption, AText, mtError, [mbAbort,mbRetry]);
7629 if Result=mrAbort then exit;
7630 continue; // try again
7631 end;
7632 // check programname
7633 //if FilenameIsPascalUnit(NewProgramFN)
7634 if (Project1.IndexOfUnitWithName(NewProgramName,true,
7635 Project1.MainUnitInfo)>=0) then
7636 begin
7637 ACaption:=lisUnitIdentifierExists;
7638 AText:=Format(lisThereIsAUnitWithTheNameInTheProjectPleaseChoose,[NewProgramName,LineEnding]);
7639 Result:=IDEMessageDialog(ACaption,AText,mtError,[mbRetry,mbAbort]);
7640 if Result=mrAbort then exit;
7641 continue; // try again
7642 end;
7643 Result:=mrOk;
7644 end else begin
7645 NewProgramFN:='';
7646 Result:=mrOk;
7647 end;
7648 until Result<>mrRetry;
7649 finally
7650 InputHistories.StoreFileDialogSettings(SaveDialog);
7651 SaveDialog.Free;
7652 end;
7653
7654 //DebugLn(['ShowSaveProjectAsDialog NewLPI=',NewLPIFilename,' NewProgramName=',NewProgramName,' NewMainSource=',NewProgramFN]);
7655
7656 // check if info file or source file already exists
7657 // Note: if user confirms overwriting .lpi do not ask for overwriting .lpr
7658 if FileExistsUTF8(NewLPIFilename) then
7659 begin
7660 if IDESaveDialogClass.NeedOverwritePrompt then
7661 begin
7662 ACaption:=lisOverwriteFile;
7663 AText:=Format(lisAFileAlreadyExistsReplaceIt, [NewLPIFilename, LineEnding]);
7664 Result:=IDEMessageDialog(ACaption, AText, mtConfirmation, [mbOk, mbCancel]);
7665 if Result=mrCancel then exit;
7666 end;
7667 end
7668 else
7669 begin
7670 if FileExistsUTF8(NewProgramFN) then
7671 begin
7672 ACaption:=lisOverwriteFile;
7673 AText:=Format(lisAFileAlreadyExistsReplaceIt, [NewProgramFN, LineEnding]);
7674 Result:=IDEMessageDialog(ACaption, AText, mtConfirmation,[mbOk,mbCancel]);
7675 if Result=mrCancel then exit;
7676 end;
7677 end;
7678
7679 TitleWasDefault := Project1.TitleIsDefault(true);
7680
7681 // set new project target filename
7682 if (Project1.TargetFilename<>'')
7683 and ( (CompareText(ExtractFileNameOnly(Project1.TargetFilename),
7684 ExtractFileNameOnly(Project1.ProjectInfoFile))=0)
7685 or (Project1.ProjectInfoFile='') ) then
7686 begin
7687 // target file is default => change to all build modes, but keep sub directories
7688 // And keep old extension.
7689 NewTargetFN:=ExtractFilePath(Project1.TargetFilename)+ExtractFileNameOnly(NewProgramFN)
7690 +ExtractFileExt(Project1.TargetFilename);
7691 for i := 0 to Project1.BuildModes.Count-1 do
7692 Project1.BuildModes[i].CompilerOptions.TargetFilename:=NewTargetFN;
7693 //DebugLn(['ShowSaveProjectAsDialog changed targetfilename to ',Project1.TargetFilename]);
7694 end;
7695
7696 // set new project filename
7697 Project1.ProjectInfoFile:=NewLPIFilename;
7698 EnvironmentOptions.AddToRecentProjectFiles(NewLPIFilename);
7699 MainIDE.SetRecentProjectFilesMenu;
7700
7701 // change main source
7702 if (Project1.MainUnitID >= 0) then
7703 begin
7704 GetMainUnit(MainUnitInfo, MainUnitSrcEdit, true);
7705 if not Project1.ProjResources.RenameDirectives(MainUnitInfo.Filename,NewProgramFN)
7706 then begin
7707 DebugLn(['ShowSaveProjectAsDialog failed renaming directives Old="',MainUnitInfo.Filename,'" New="',NewProgramFN,'"']);
7708 // silently ignore
7709 end;
7710
7711 // Save old source code, to prevent overwriting it,
7712 // if the file name didn't actually change.
7713 OldBuf := MainUnitInfo.Source;
7714 OldSourceCode := OldBuf.Source;
7715
7716 // switch MainUnitInfo.Source to new code
7717 NewBuf := CodeToolBoss.CreateFile(NewProgramFN);
7718 if NewBuf=nil then begin
7719 Result:=IDEMessageDialog(lisErrorCreatingFile,
7720 Format(lisUnableToCreateFile3, [LineEnding, NewProgramFN]),
7721 mtError, [mbCancel]);
7722 exit;
7723 end;
7724
7725 // copy the source to the new buffer
7726 NewBuf.Source:=OldSourceCode;
7727 if (OldBuf.DiskEncoding<>'') and (OldBuf.DiskEncoding<>EncodingUTF8) then
7728 begin
7729 NewBuf.DiskEncoding:=OldBuf.DiskEncoding;
7730 InputHistories.FileEncodings[NewProgramFN]:=NewBuf.DiskEncoding;
7731 end else
7732 InputHistories.FileEncodings[NewProgramFN]:='';
7733
7734 // assign the new buffer to the MainUnit
7735 MainUnitInfo.Source:=NewBuf;
7736 if MainUnitSrcEdit<>nil then
7737 MainUnitSrcEdit.CodeBuffer:=NewBuf;
7738
7739 // change program name
7740 MainUnitInfo.Unit_Name:=NewProgramName;
7741 MainUnitInfo.Modified:=true;
7742
7743 // update source notebook page names
7744 UpdateSourceNames;
7745 end;
7746
7747 // update paths
7748 prDir := Project1.Directory;
7749 with Project1.CompilerOptions do begin
7750 OtherUnitFiles:=RebaseSearchPath(OtherUnitFiles,OldProjectDir,prDir,true);
7751 IncludePath :=RebaseSearchPath(IncludePath,OldProjectDir,prDir,true);
7752 Libraries :=RebaseSearchPath(Libraries,OldProjectDir,prDir,true);
7753 ObjectPath :=RebaseSearchPath(ObjectPath,OldProjectDir,prDir,true);
7754 SrcPath :=RebaseSearchPath(SrcPath,OldProjectDir,prDir,true);
7755 DebugPath :=RebaseSearchPath(DebugPath,OldProjectDir,prDir,true);
7756 end;
7757 // change title
7758 if TitleWasDefault then begin
7759 Project1.Title:=Project1.GetDefaultTitle;
7760 // title does not need to be removed from source, because it was default
7761 end;
7762
7763 // invalidate cached substituted macros
7764 IncreaseCompilerParseStamp;
7765 finally
7766 Project1.EndUpdate;
7767 end;
7768 Result:=mrOk;
7769 //DebugLn(['ShowSaveProjectAsDialog END OK']);
7770 end;
7771
7772 function AskSaveProject(const ContinueText, ContinueBtn: string): TModalResult;
7773 var
7774 DataModified: Boolean;
7775 SrcModified: Boolean;
7776 begin
7777 if Project1=nil then exit(mrOk);
7778 if not SomethingOfProjectIsModified then exit(mrOk);
7779
7780 DataModified:=Project1.SomeDataModified(false);
7781 SrcModified:=SourceEditorManager.SomethingModified(false);
7782
7783 if Project1.IsVirtual
7784 and (not DataModified)
7785 and (not SrcModified) then begin
7786 // only session changed of a new project => ignore
7787 exit(mrOk)
7788 end;
7789
7790 if (Project1.SessionStorage=pssInProjectInfo)
7791 or DataModified
7792 then begin
7793 // lpi file will change => ask
7794 Result:=IDEQuestionDialog(lisProjectChanged,
7795 Format(lisSaveChangesToProject, [Project1.GetTitleOrName]),
7796 mtConfirmation, [mrYes,
7797 mrNoToAll, rsmbNo,
7798 mrCancel], '');
7799 if Result=mrNoToAll then exit(mrOk);
7800 if Result<>mrYes then exit(mrCancel);
7801 end
7802 else if SrcModified then
7803 begin
7804 // some non project files were changes in the source editor
7805 Result:=IDEQuestionDialog(lisSaveChangedFiles,lisSaveChangedFiles,
7806 mtConfirmation, [mrYes,
7807 mrNoToAll, rsmbNo,
7808 mrCancel], '');
7809 if Result=mrNoToAll then exit(mrOk);
7810 if Result<>mrYes then exit(mrCancel);
7811 end
7812 else begin
7813 // only session data changed
7814 if Project1.SessionStorage=pssNone then
7815 // session is not saved => skip
7816 exit(mrOk)
7817 else if not SomethingOfProjectIsModified then
7818 // no change
7819 exit(mrOk)
7820 else begin
7821 // session is saved separately
7822 if EnvironmentOptions.AskSaveSessionOnly then begin
7823 Result:=IDEQuestionDialog(lisProjectSessionChanged,
7824 Format(lisSaveSessionChangesToProject, [Project1.GetTitleOrName]),
7825 mtConfirmation, [mrYes,
7826 mrNoToAll, rsmbNo,
7827 mrCancel], '');
7828 if Result=mrNoToAll then exit(mrOk);
7829 if Result<>mrYes then exit(mrCancel);
7830 end;
7831 end;
7832 end;
7833 Result:=SaveProject([sfCanAbort]);
7834 if Result=mrAbort then exit;
7835 if Result<>mrOk then begin
7836 Result:=IDEQuestionDialog(lisChangesWereNotSaved, ContinueText,
7837 mtConfirmation, [mrOk, ContinueBtn, mrAbort]);
7838 if Result<>mrOk then exit(mrCancel);
7839 end;
7840 end;
7841
7842 function SaveEditorChangesToCodeCache(AEditor: TSourceEditorInterface): boolean;
7843 // save all open sources to code tools cache
7844
7845 procedure SaveChanges(SaveEditor: TSourceEditorInterface);
7846 var
7847 AnUnitInfo: TUnitInfo;
7848 begin
7849 AnUnitInfo := Project1.UnitWithEditorComponent(SaveEditor);
7850 if (AnUnitInfo<>nil) then
7851 begin
7852 //debugln(['SaveChanges ',AnUnitInfo.Filename,' ',SaveEditor.NeedsUpdateCodeBuffer]);
7853 if SaveEditor.NeedsUpdateCodeBuffer then
7854 begin
7855 SaveEditorChangesToCodeCache:=true;
7856 SaveEditor.UpdateCodeBuffer;
7857 //debugln(['SaveEditorChangesToCodeCache.SaveChanges ',AnUnitInfo.Filename,' Step=',TCodeBuffer(SaveEditor.CodeToolsBuffer).ChangeStep]);
7858 end;
7859 end;
7860 end;
7861
7862 var
7863 i: integer;
7864 begin
7865 Result:=false;
7866 //debugln(['SaveEditorChangesToCodeCache ']);
7867 if AEditor = nil then begin
7868 for i:=0 to SourceEditorManager.SourceEditorCount - 1 do
7869 SaveChanges(SourceEditorManager.SourceEditors[i]);
7870 end else begin
7871 SaveChanges(AEditor);
7872 end;
7873 end;
7874
7875 function ReplaceUnitUse(OldFilename, OldUnitName, NewFilename, NewUnitName: string;
7876 IgnoreErrors, Quiet, Confirm: boolean): TModalResult;
7877 // Replaces all references to a unit
7878 var
7879 OwnerList: TFPList;
7880 ExtraFiles: TStrings;
7881 Files: TStringList;
7882 OldCode: TCodeBuffer;
7883 OldCodeCreated: Boolean;
7884 PascalReferences: TAVLTree;
7885 i: Integer;
7886 MsgResult: TModalResult;
7887 OnlyEditorFiles: Boolean;
7888 aFilename: String;
7889 begin
7890 // compare unitnames case sensitive, maybe only the case changed
7891 if (CompareFilenames(OldFilename,NewFilename)=0) and (OldUnitName=NewUnitName) then
7892 exit(mrOk);
7893 // this was a new file, files on disk can not refer to it
7894 OnlyEditorFiles:=not FilenameIsAbsolute(OldFilename);
7895
7896 OwnerList:=nil;
7897 OldCode:=nil;
7898 OldCodeCreated:=false;
7899 PascalReferences:=nil;
7900 Files:=TStringList.Create;
7901 try
7902 if OnlyEditorFiles then begin
7903 // search only in open files
7904 for i:=0 to SourceEditorManagerIntf.UniqueSourceEditorCount-1 do begin
7905 aFilename:=SourceEditorManagerIntf.UniqueSourceEditors[i].FileName;
7906 if not FilenameIsPascalSource(aFilename) then continue;
7907 Files.Add(aFileName);
7908 end;
7909 // add project's main source file
7910 if (Project1<>nil) and (Project1.MainUnitID>=0) then
7911 Files.Add(Project1.MainFilename);
7912 end else begin
7913 // get owners of unit
7914 OwnerList:=PkgBoss.GetOwnersOfUnit(NewFilename);
7915 if OwnerList=nil then exit(mrOk);
7916 PkgBoss.ExtendOwnerListWithUsedByOwners(OwnerList);
7917 ReverseList(OwnerList);
7918
7919 // get source files of packages and projects
7920 ExtraFiles:=PkgBoss.GetSourceFilesOfOwners(OwnerList);
7921 try
7922 if ExtraFiles<>nil then
7923 Files.AddStrings(ExtraFiles);
7924 finally
7925 ExtraFiles.Free;
7926 end;
7927 end;
7928 for i:=Files.Count-1 downto 0 do begin
7929 if (CompareFilenames(Files[i],OldFilename)=0)
7930 or (CompareFilenames(Files[i],NewFilename)=0) then
7931 Files.Delete(i);
7932 end;
7933 //DebugLn(['ReplaceUnitUse ',Files.Text]);
7934
7935 // commit source editor to codetools
7936 SaveEditorChangesToCodeCache(nil);
7937
7938 // load or create old unit
7939 OldCode:=CodeToolBoss.LoadFile(OldFilename,true,false);
7940 if OldCode=nil then begin
7941 // create old file in memory so that unit search can find it
7942 OldCode:=CodeToolBoss.CreateFile(OldFilename);
7943 OldCodeCreated:=true;
7944 end;
7945
7946 // search pascal source references
7947 Result:=GatherUnitReferences(Files,OldCode,false,IgnoreErrors,true,PascalReferences);
7948 if (not IgnoreErrors) and (not Quiet) and (CodeToolBoss.ErrorMessage<>'') then
7949 MainIDE.DoJumpToCodeToolBossError;
7950 if Result<>mrOk then begin
7951 debugln('ReplaceUnitUse GatherUnitReferences failed');
7952 exit;
7953 end;
7954
7955 // replace
7956 if (PascalReferences<>nil) and (PascalReferences.Count>0) then begin
7957 if Confirm then begin
7958 MsgResult:=IDEQuestionDialog(lisUpdateReferences,
7959 Format(lisTheUnitIsUsedByOtherFilesUpdateReferencesAutomatic,
7960 [OldUnitName, LineEnding]),
7961 mtConfirmation, [mrYes,mrNo,mrYesToAll,mrNoToAll],'');
7962 case MsgResult of
7963 mrYes: ;
7964 mrYesToAll: EnvironmentOptions.UnitRenameReferencesAction:=urraAlways;
7965 mrNoToAll:
7966 begin
7967 EnvironmentOptions.UnitRenameReferencesAction:=urraNever;
7968 Result:=mrOk;
7969 exit;
7970 end;
7971 else
7972 Result:=mrOk;
7973 exit;
7974 end;
7975 end;
7976 if not CodeToolBoss.RenameIdentifier(PascalReferences,
7977 OldUnitName,NewUnitName)
7978 then begin
7979 if (not IgnoreErrors) and (not Quiet) then
7980 MainIDE.DoJumpToCodeToolBossError;
7981 debugln('ReplaceUnitUse unable to commit');
7982 if not IgnoreErrors then begin
7983 Result:=mrCancel;
7984 exit;
7985 end;
7986 end;
7987 end;
7988
7989 finally
7990 if OldCodeCreated then
7991 OldCode.IsDeleted:=true;
7992 CodeToolBoss.FreeTreeOfPCodeXYPosition(PascalReferences);
7993 OwnerList.Free;
7994 Files.Free;
7995 end;
7996 //PkgBoss.GetOwnersOfUnit(NewFilename);
7997 Result:=mrOk;
7998 end;
7999
8000 function DesignerUnitIsVirtual(aLookupRoot: TComponent): Boolean;
8001 var
8002 ActiveSourceEditor: TSourceEditor;
8003 ActiveUnitInfo: TUnitInfo;
8004 begin
8005 Assert(Assigned(aLookupRoot),'DesignerUnitIsVirtual: aLookupRoot is not assigned');
8006 MainIDE.GetUnitWithPersistent(aLookupRoot, ActiveSourceEditor, ActiveUnitInfo);
8007 Result := ActiveUnitInfo.IsVirtual;
8008 end;
8009
8010 end.
8011
8012