diff --git a/RevisionLog.txt b/RevisionLog.txt index 7117025f..a8d5cd39 100644 --- a/RevisionLog.txt +++ b/RevisionLog.txt @@ -1,6 +1,13 @@ Revision log. (single changes can be tracked with the GH commit messages) +r27: +- all sources: common defines are set by ce_defines.inc +- ce_dmdwrap.pas: every option likely to represent a path can contain a symbolic string. +- ce_project: added a routine for patching invalid sources. +- all sources: session options use the application-wide subject/observer system. +- ce_main: expandSymbolicString, only empty results are back-quoted. + r26: - ce_libmaneditor: added support for folder of library (e.g derelict). - ce_main: run project, fix, projects couldn't be executed when not saved. diff --git a/lazproj/coedit.lpi b/lazproj/coedit.lpi index b8ea8b7e..def73ba9 100644 --- a/lazproj/coedit.lpi +++ b/lazproj/coedit.lpi @@ -135,7 +135,7 @@ - + @@ -291,6 +291,11 @@ + + + + + diff --git a/lazproj/coedit.lpr b/lazproj/coedit.lpr index b3e3bee9..f5010cc4 100644 --- a/lazproj/coedit.lpr +++ b/lazproj/coedit.lpr @@ -7,7 +7,7 @@ uses cthreads, {$ENDIF}{$ENDIF} Interfaces, Forms, lazcontrols, runtimetypeinfocontrols, - ce_dcd, ce_observer, ce_main, ce_writableComponent; + ce_dcd, ce_observer, ce_main, ce_writableComponent, ce_options; {$R *.res} diff --git a/src/ce_common.pas b/src/ce_common.pas index bbbb5d8e..49dd64e1 100644 --- a/src/ce_common.pas +++ b/src/ce_common.pas @@ -1,6 +1,6 @@ unit ce_common; -{$MODE OBJFPC}{$H+} +{$I ce_defines.inc} interface @@ -13,6 +13,9 @@ uses const DdiagFilter = 'D source|*.d|D interface|*.di|All files|*.*'; + exeExt = {$IFDEF WINDOWS} '.exe' {$ELSE} '' {$ENDIF}; + objExt = {$IFDEF WINDOWS} '.obj' {$ELSE} '.o' {$ENDIF}; + libExt = {$IFDEF WINDOWS} '.lib' {$ELSE} '.a' {$ENDIF}; var DExtList: TStringList; @@ -59,6 +62,24 @@ type procedure Assign(aValue: TPersistent); end; + (** + * Makes TReader.ReadProperties visible + *) + TReaderEx = class helper for TReader + public + procedure ReadPersistent(aValue: TPersistent); + end; + + (** + * Makes TWriter.WriteProperties visible + * W + *) + TWriterEx = class helper for TWriter + public + // works as bin but raises because of 'ObjectBinaryToText' + procedure WritePersistent(aValue: TPersistent); + end; + (** * Save a component with a readable aspect. *) @@ -159,6 +180,8 @@ type implementation +// https://stackoverflow.com/questions/25438091/objectbinarytotext-error-with-a-treader-twriter-helper-class +// http://forum.lazarus.freepascal.org/index.php/topic,25557.0.html procedure TProcessEx.Assign(aValue: TPersistent); var src: TProcess; @@ -190,6 +213,20 @@ begin else inherited; end; +procedure TReaderEx.ReadPersistent(aValue: TPersistent); +begin + ReadListBegin; + while not EndOfList do ReadProperty(aValue); + ReadListEnd; +end; + +procedure TWriterEx.WritePersistent(aValue: TPersistent); +begin + WriteListBegin; + WriteProperties(aValue); + WriteListEnd; +end; + constructor TMRUList.Create; begin fMaxCount := 10; @@ -337,14 +374,10 @@ begin result := patchProc(result, '/'); result := patchProc(result, ':'); {$ENDIF} - {$IFDEF LINUX} + {$IFDEF POSIX} result := patchProc(result, '\'); result := patchProc(result, ':'); {$ENDIF} - {$IFDEF MACOS} - result := patchProc(result, '\'); - result := patchProc(result, '/'); - {$ENDIF} end; procedure patchPlateformPaths(const sPaths: TStrings); @@ -607,20 +640,17 @@ begin end; function exeInSysPath(anExeName: string): boolean; -{$IFDEF WINDOWS} var ext: string; -{$ENDIF} begin - {$IFDEF WINDOWS} ext := extractFileExt(anExeName); - if ext = '' then - anExeName += '.exe'; - {$ENDIF} + if ext <> exeExt then + anExeName += exeExt; exit(ExeSearch(anExeName, '') <> ''); end; initialization + RegisterClasses([TMRUList, TMRUFileList]); DExtList := TStringList.Create; DExtList.Add('.d'); DExtList.Add('.di'); diff --git a/src/ce_customtools.pas b/src/ce_customtools.pas index ff446eac..3e87599f 100644 --- a/src/ce_customtools.pas +++ b/src/ce_customtools.pas @@ -1,7 +1,6 @@ unit ce_customtools; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface diff --git a/src/ce_d2syn.pas b/src/ce_d2syn.pas index 9cc6cace..72530c20 100644 --- a/src/ce_d2syn.pas +++ b/src/ce_d2syn.pas @@ -1,6 +1,6 @@ unit ce_d2syn; -{$MODE OBJFPC}{$H+} +{$I ce_defines.inc} interface diff --git a/src/ce_dcd.pas b/src/ce_dcd.pas index f896b161..732e6c39 100644 --- a/src/ce_dcd.pas +++ b/src/ce_dcd.pas @@ -1,10 +1,11 @@ unit ce_dcd; -{$MODE OBJFPC}{$H+} + +{$I ce_defines.inc} interface uses - Classes, SysUtils, process, forms, strutils; + Classes, SysUtils, process, forms, strutils, ce_common; (** @@ -67,8 +68,7 @@ begin if DCD_server <> nil then FreeAndNil(DCD_server); DCD_server := TProcess.Create(nil); - DCD_server.Executable := extractFilePath(application.ExeName) + directorySeparator - + 'dcd-server'{$IFDEF WINDOWS}+ '.exe'{$ENDIF}; + DCD_server.Executable := extractFilePath(application.ExeName) + directorySeparator + 'dcd-server' + exeExt; DCD_server.Options := [poUsePipes{$IFDEF WINDOWS}, poNewConsole{$ENDIF}]; DCD_server.ShowWindow := swoHIDE; end; @@ -192,8 +192,7 @@ end; initialization createServer; DCD_client := TProcess.Create(nil); - DCD_client.Executable := extractFilePath(application.ExeName) + directorySeparator - + 'dcd-client'{$IFDEF WINDOWS}+ '.exe'{$ENDIF}; + DCD_client.Executable := extractFilePath(application.ExeName) + directorySeparator + 'dcd-client' + exeExt; DCD_client.Options := [poUsePipes{$IFDEF WINDOWS}, poNewConsole{$ENDIF}]; DCD_client.ShowWindow := swoHIDE; dcdOn := fileExists(DCD_server.Executable) and fileExists(DCD_client.Executable); diff --git a/src/ce_defines.inc b/src/ce_defines.inc new file mode 100644 index 00000000..304d3876 --- /dev/null +++ b/src/ce_defines.inc @@ -0,0 +1,2 @@ +{$MODE OBJFPC}{$H+} +{$INTERFACES CORBA} \ No newline at end of file diff --git a/src/ce_dlang.pas b/src/ce_dlang.pas index ff66f4f9..1794b336 100644 --- a/src/ce_dlang.pas +++ b/src/ce_dlang.pas @@ -1,6 +1,6 @@ unit ce_dlang; -{$MODE OBJFPC}{$H+} +{$I ce_defines.inc} interface diff --git a/src/ce_dlangutils.pas b/src/ce_dlangutils.pas index 6537ee45..bb06a479 100644 --- a/src/ce_dlangutils.pas +++ b/src/ce_dlangutils.pas @@ -1,6 +1,6 @@ unit ce_dlangutils; -{$MODE OBJFPC}{$H+} +{$I ce_defines.inc} interface diff --git a/src/ce_dmdwrap.pas b/src/ce_dmdwrap.pas index 4297c1eb..6f9d8d71 100644 --- a/src/ce_dmdwrap.pas +++ b/src/ce_dmdwrap.pas @@ -1,6 +1,6 @@ unit ce_dmdwrap; -{$MODE OBJFPC}{$H+} +{$I ce_defines.inc} interface @@ -361,8 +361,8 @@ procedure TDocOpts.getOpts(const aList: TStrings); begin if fGenDoc then aList.Add('-D'); if fGenJson then aList.Add('-X'); - if fDocDir <> '' then aList.Add('-Dd' + fDocDir); - if fJsonFname <> '' then aList.Add('-Xf' + fJsonFname); + if fDocDir <> '' then aList.Add('-Dd' + CEMainForm.expandSymbolicString(fDocDir)); + if fJsonFname <> '' then aList.Add('-Xf' + CEMainForm.expandSymbolicString(fJsonFname)); end; procedure TDocOpts.assign(aValue: TPersistent); @@ -816,13 +816,13 @@ begin aList.Add(str); end; for str in fIncl do if str <> '' then - aList.Add('-I'+ str); + aList.Add('-I'+ CEMainForm.expandSymbolicString(str)); for str in fImpt do if str <> '' then - aList.Add('-J'+ str); + aList.Add('-J'+ CEMainForm.expandSymbolicString(str)); if fFname <> '' then - aList.Add('-of' + fFname); + aList.Add('-of' + CEMainForm.expandSymbolicString(fFname)); if fObjDir <> '' then - aList.Add('-od' + fObjDir); + aList.Add('-od' + CEMainForm.expandSymbolicString(fObjDir)); end; procedure TPathsOpts.assign(aValue: TPersistent); @@ -920,7 +920,7 @@ begin str2 := '-' + str1 else str2 := str1; - aList.Add(str2); + aList.Add(CEMainForm.expandSymbolicString(str2)); end; end; diff --git a/src/ce_editor.pas b/src/ce_editor.pas index 00fe4d88..25f0d27d 100644 --- a/src/ce_editor.pas +++ b/src/ce_editor.pas @@ -1,7 +1,6 @@ unit ce_editor; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface diff --git a/src/ce_interfaces.pas b/src/ce_interfaces.pas index af94cedc..1c67e250 100644 --- a/src/ce_interfaces.pas +++ b/src/ce_interfaces.pas @@ -1,27 +1,33 @@ unit ce_interfaces; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface uses Classes, SysUtils, actnList, menus, ce_synmemo, ce_project, ce_observer; - type (** * An implementer can save and load some stuffs on application start/quit *) - ICEWidgetPersist = interface + ICESessionOptionsObserver = interface ['ICEWidgetPersist'] - // Coedit options are about to be saved. - procedure beforeSave(sender: TObject); - // some custom properties can be declared to aFiler. - procedure declareProperties(aFiler: TFiler); - // Coedit options has just been reloaded. - procedure afterLoad(sender: TObject); + // persistent things are about to be saved. + procedure sesoptBeforeSave; + // persistent things can be declared to aFiler. + procedure sesoptDeclareProperties(aFiler: TFiler); + // persistent things have just been reloaded. + procedure sesoptAfterLoad; + end; + + (** + * An implementer gets and gives back some things + *) + TCESessionOptionsSubject = class(TCECustomSubject) + protected + function acceptObserver(aObject: TObject): boolean; override; end; (** @@ -73,10 +79,6 @@ type procedure projClosing(const aProject: TCEProject); // not used yet: the active project is now aProject procedure projFocused(const aProject: TCEProject); - // aProject is about to be compiled. - //procedure projCompile(const aProject: TCEProject); - // aProject is about to be executed. - //procedure projRun(const aProject: TCEProject); end; (** @@ -94,7 +96,7 @@ type ['ICEMainMenuProvider'] // item must contain the full items tree to be added procedure menuDeclare(out item: TMenuItem); - // the implementer can update the actions used in the menu declared before. + // the implementer can update the actions used in the menu declared previously. procedure menuActionsUpdate; end; @@ -102,7 +104,7 @@ type { subject Primitives: - A subject has not necessarly all the informations the observers expect. + A subject cannot necessarly provides all the informations the observers expect. It can compose using the following "primitives". } @@ -123,17 +125,19 @@ type procedure subjProjFocused(aSubject: TCEProjectSubject; aProj: TCEProject); {$IFDEF RELEASE}inline;{$ENDIF} procedure subjProjChanged(aSubject: TCEProjectSubject; aProj: TCEProject); {$IFDEF RELEASE}inline;{$ENDIF} - //procedure subjProjCompile(aSubject: TCEProjectSubject; aProj: TCEProject); //{$IFDEF RELEASE}inline;{$ENDIF} - //procedure subjProjRun(aSubject: TCEProjectSubject; aProj: TCEProject); //{$IFDEF RELEASE}inline;{$ENDIF} + (** + * TCESessionOptionsSubject primitives. + *) + procedure subjSesOptsBeforeSave(aSubject: TCESessionOptionsSubject); {$IFDEF RELEASE}inline;{$ENDIF} + procedure subjSesOptsDeclareProperties(aSubject: TCESessionOptionsSubject; aFiler: TFiler);{$IFDEF RELEASE}inline;{$ENDIF} + procedure subjSesOptsAfterLoad(aSubject: TCESessionOptionsSubject); {$IFDEF RELEASE}inline;{$ENDIF} implementation -uses - ce_main; - +{$REGION TCEMultiDocSubject-----------------------------------------------------} function TCEMultiDocSubject.acceptObserver(aObject: TObject): boolean; begin - result := (aObject is ICEMultiDocObserver); + exit(aObject is ICEMultiDocObserver); end; procedure subjDocNew(aSubject: TCEMultiDocSubject; aDoc: TCESynMemo); @@ -167,12 +171,12 @@ begin with aSubject do for i:= 0 to fObservers.Count-1 do (fObservers.Items[i] as ICEMultiDocObserver).docChanged(aDoc); end; +{$ENDREGION} - - +{$REGION TCEProjectSubject------------------------------------------------------} function TCEProjectSubject.acceptObserver(aObject: TObject): boolean; begin - result := (aObject is ICEProjectObserver); + exit(aObject is ICEProjectObserver); end; procedure subjProjNew(aSubject: TCEProjectSubject; aProj: TCEProject); @@ -206,24 +210,36 @@ begin with aSubject do for i:= 0 to fObservers.Count-1 do (fObservers.Items[i] as ICEProjectObserver).projChanged(aProj); end; +{$ENDREGION} +{$REGION TCESessionOptionsSubject------------------------------------------------------} +function TCESessionOptionsSubject.acceptObserver(aObject: TObject): boolean; +begin + exit(aObject is ICESessionOptionsObserver); +end; +procedure subjSesOptsBeforeSave(aSubject: TCESessionOptionsSubject); +var + i: Integer; +begin + with aSubject do for i:= 0 to fObservers.Count-1 do + (fObservers.Items[i] as ICESessionOptionsObserver).sesoptBeforeSave; +end; -//procedure subjProjCompile(aSubject: TCEProjectSubject; aProj: TCEProject); -//var -// i: Integer; -//begin -// with aSubject do for i:= 0 to fObservers.Count-1 do -// (fObservers.Items[i] as ICEProjectObserver).projCompile(aProj); -//end; -// -//procedure subjProjRun(aSubject: TCEProjectSubject; aProj: TCEProject); -//var -// i: Integer; -//begin -// with aSubject do for i:= 0 to fObservers.Count-1 do -// (fObservers.Items[i] as ICEProjectObserver).projRun(aProj); -//end; - +procedure subjSesOptsDeclareProperties(aSubject: TCESessionOptionsSubject; aFiler: TFiler); +var + i: Integer; +begin + with aSubject do for i:= 0 to fObservers.Count-1 do + (fObservers.Items[i] as ICESessionOptionsObserver).sesoptDeclareProperties(aFiler); +end; +procedure subjSesOptsAfterLoad(aSubject: TCESessionOptionsSubject); +var + i: Integer; +begin + with aSubject do for i:= 0 to fObservers.Count-1 do + (fObservers.Items[i] as ICESessionOptionsObserver).sesoptAfterLoad; +end; +{$ENDREGION} end. diff --git a/src/ce_libman.pas b/src/ce_libman.pas index 84e0ff76..ca5dcade 100644 --- a/src/ce_libman.pas +++ b/src/ce_libman.pas @@ -1,7 +1,6 @@ unit ce_libman; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -107,11 +106,7 @@ begin listFiles(lst, itm.libFile); for j:= 0 to lst.Count-1 do begin - {$IFDEF WINDOWS} - if extractFileExt(lst.Strings[j]) = '.lib' then - {$ELSE} - if extractFileExt(lst.Strings[j]) = '.a' then - {$ENDIF} + if extractFileExt(lst.Strings[j]) = libExt then if aList.IndexOf(lst.Strings[j]) = -1 then aList.Add(lst.Strings[j]); end; diff --git a/src/ce_libmaneditor.pas b/src/ce_libmaneditor.pas index acff2290..527dc5d9 100644 --- a/src/ce_libmaneditor.pas +++ b/src/ce_libmaneditor.pas @@ -1,7 +1,6 @@ unit ce_libmaneditor; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface diff --git a/src/ce_main.pas b/src/ce_main.pas index 7c6ecc84..eed86963 100644 --- a/src/ce_main.pas +++ b/src/ce_main.pas @@ -1,7 +1,6 @@ unit ce_main; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -16,40 +15,13 @@ uses type - TCEMainForm = class; - - // TODO-cfeature: input handling // TODO-cfeature: options // TODO-cwidget: options editor // TODO-cwidget: custom tools editor // TODO-cfeature: tools menu - (** - * Encapsulates the options in a writable component. - *) - TCEOptions = class(TWritableComponent) - private - fFileMru, fProjMru: TMruFileList; - fLeft, FTop, fWidth, fHeight: Integer; - procedure setFileMru(aValue: TMruFileList); - procedure setProjMru(aValue: TMruFileList); - published - property APP_Left: Integer read fLeft write fLeft; - property APP_Top: Integer read fTop write fTop; - property APP_Width: Integer read fWidth write fWidth; - property APP_Height: Integer read fHeight write fHeight; - // - property MRU_Files: TMruFileList read fFileMru write setFileMru; - property MRU_Projects: TMruFileList read fProjMru write setProjMru; - public - constructor create(aOwner: TComponent); override; - destructor destroy; override; - procedure defineProperties(Filer: TFiler); override; - procedure beforeSave; override; - procedure afterLoad; override; - end; { TCEMainForm } - TCEMainForm = class(TForm, ICEMultiDocObserver) + TCEMainForm = class(TForm, ICEMultiDocObserver, ICESessionOptionsObserver) actFileCompAndRun: TAction; actFileSaveAll: TAction; actFileClose: TAction; @@ -222,6 +194,19 @@ type procedure docFocused(const aDoc: TCESynMemo); procedure docChanged(const aDoc: TCESynMemo); + // ICESessionOptionsObserver + procedure sesoptBeforeSave; + procedure sesoptDeclareProperties(aFiler: TFiler); + procedure sesoptAfterLoad; + procedure optget_FileMRUItems(aWriter: TWriter); + procedure optset_FileMRUItems(aReader: TReader); + procedure optget_FileMRULimit(aWriter: TWriter); + procedure optset_FileMRULimit(aReader: TReader); + procedure optget_ProjMRUItems(aWriter: TWriter); + procedure optset_ProjMRUItems(aReader: TReader); + procedure optget_ProjMRULimit(aWriter: TWriter); + procedure optset_ProjMRULimit(aReader: TReader); + //Init - Fina procedure getCMdParams; procedure checkCompilo; @@ -290,7 +275,7 @@ implementation {$R *.lfm} uses - SynMacroRecorder, strutils; + SynMacroRecorder, strutils, ce_options; {$REGION Standard Comp/Obj------------------------------------------------------} constructor TCEMainForm.create(aOwner: TComponent); @@ -528,8 +513,8 @@ var fname2: string; opts: TCEOptions; begin - fname1 := getDocPath + 'options.txt'; - fname2 := getDocPath + 'options.bak'; + fname1 := getDocPath + 'options2.txt'; + fname2 := getDocPath + 'options2.bak'; opts := TCEOptions.create(nil); try if fileExists(fname1) then @@ -557,7 +542,7 @@ begin forceDirectory(getDocPath); fLibMan.saveToFile(getDocPath + 'libraryManager.txt'); fTools.saveToFile(getDocPath + 'tools.txt'); - opts.saveToFile(getDocPath + 'options.txt'); + opts.saveToFile(getDocPath + 'options2.txt'); finally opts.Free; end; @@ -667,7 +652,6 @@ end; destructor TCEMainForm.destroy; begin - EntitiesConnector.removeObserver(self); SaveSettings; // KillPlugs; @@ -677,6 +661,7 @@ begin fFileMru.Free; fProject.Free; // + EntitiesConnector.removeObserver(self); inherited; end; @@ -1241,7 +1226,7 @@ begin dmdproc.Parameters.Add(editor.fileName); dmdproc.Parameters.Add('-w'); dmdproc.Parameters.Add('-wi'); - dmdproc.Parameters.Add('-of' + fname {$IFDEF WINDOWS}+ '.exe'{$ENDIF}); + dmdproc.Parameters.Add('-of' + fname + exeExt); LibraryManager.getLibFiles(nil, dmdproc.Parameters); LibraryManager.getLibSources(nil, dmdproc.Parameters); dmdproc.Execute; @@ -1253,16 +1238,11 @@ begin runproc.Options := [poStderrToOutPut, poUsePipes]; runproc.CurrentDirectory := extractFilePath(runProc.Executable); runproc.Parameters.DelimitedText := expandSymbolicString(runArgs); - runproc.Executable := fname {$IFDEF WINDOWS}+ '.exe'{$ENDIF}; + runproc.Executable := fname + exeExt; runproc.Execute; repeat ProcessOutputToMsg(runproc, mcEditor) until not runproc.Running; - {$IFDEF MSWINDOWS} - sysutils.DeleteFile(fname + '.exe'); - sysutils.DeleteFile(fname + '.obj'); - {$ELSE} - sysutils.DeleteFile(fname); - sysutils.DeleteFile(fname + '.o'); - {$ENDIF} + sysutils.DeleteFile(fname + exeExt); + sysutils.DeleteFile(fname + objExt); end else begin ProcessOutputToMsg(dmdproc, mcEditor); @@ -1285,9 +1265,6 @@ begin fMesgWidg.ClearAllMessages; - //for i := 0 to fWidgList.Count-1 do - //fWidgList.widget[i].projCompile(aProject); - with fProject.currentConfiguration do begin if preBuildProcess.executable <> '' then @@ -1383,9 +1360,6 @@ begin if aProject.currentConfiguration.outputOptions.binaryKind <> executable then exit; - //for i := 0 to fWidgList.Count-1 do - //fWidgList.widget[i].projRun(aProject); - runproc := TProcess.Create(nil); try aProject.currentConfiguration.runOptions.setProcess(runProc); @@ -1675,80 +1649,61 @@ begin end; {$ENDREGION} -{$REGION options ---------------------------------------------------------------} -constructor TCEOptions.create(aOwner: TComponent); +{$REGION ICESessionOptionsObserver ----------------------------------------------------} +procedure TCEMainForm.sesoptBeforeSave; begin - inherited; - fFileMru := TMruFileList.Create; - fProjMru := TMruFileList.Create; - // - fLeft := 0; - fTop := 0; - fWidth := 800; - fHeight := 600; end; -destructor TCEOptions.destroy; +procedure TCEMainForm.sesoptDeclareProperties(aFiler: TFiler); begin - fFileMru.Free; - fProjMru.Free; - inherited; + aFiler.DefineProperty('Menu_FileMRU_Items', @optset_FileMRUItems, @optget_FileMRUItems, true); + aFiler.DefineProperty('Menu_FileMRU_Limit', @optset_FileMRULimit, @optget_FileMRULimit, true); + aFiler.DefineProperty('Menu_ProjMRU_Items', @optset_ProjMRUItems, @optget_ProjMRUItems, true); + aFiler.DefineProperty('Menu_ProjMRU_Limit', @optset_ProjMRULimit, @optget_ProjMRULimit, true); end; -procedure TCEOptions.setFileMru(aValue: TMruFileList); +procedure TCEMainForm.sesoptAfterLoad; begin - fFileMru.Assign(aValue); end; -procedure TCEOptions.setProjMru(aValue: TMruFileList); +procedure TCEMainForm.optget_FileMRUItems(aWriter: TWriter); begin - fProjMru.Assign(aValue); + aWriter.WriteString(fFileMru.DelimitedText); end; -procedure TCEOptions.defineProperties(Filer: TFiler); -var - i: NativeInt; +procedure TCEMainForm.optset_FileMRUItems(aReader: TReader); begin - inherited; - // Filer is either a TReader or a TWriter - for i := 0 to CEMainForm.WidgetList.Count-1 do - CEMainForm.WidgetList.widget[i].declareProperties(Filer); + fFileMru.DelimitedText := aReader.ReadString; end; -procedure TCEOptions.beforeSave; -var - i: NativeInt; +procedure TCEMainForm.optget_FileMRULimit(aWriter: TWriter); begin - fLeft := CEMainForm.Left; - fTop := CEMainForm.Top; - fWidth := CEMainForm.Width; - fHeight := CEMainForm.Height; - // - fFileMru.Assign(CEMainForm.fFileMru); - fProjMru.Assign(CEMainForm.fProjMru); - // - for i := 0 to CEMainForm.WidgetList.Count-1 do - CEMainForm.WidgetList.widget[i].beforeSave(nil); + aWriter.WriteInteger(fFileMru.maxCount); end; -procedure TCEOptions.afterLoad; -var - i: NativeInt; +procedure TCEMainForm.optset_FileMRULimit(aReader: TReader); begin - CEMainForm.Left := fLeft; - CEMainForm.Top := fTop; - CEMainForm.Width := fWidth; - CEMainForm.Height := fHeight; - if fLeft < 0 then fLeft := 0; - if fTop < 0 then fTop := 0; - if fWidth < 800 then fWidth := 800; - if fHeight < 600 then fWidth := 600; - // - CEMainForm.fFileMru.Assign(fFileMru); - CEMainForm.fProjMru.Assign(fProjMru); - // - for i := 0 to CEMainForm.WidgetList.Count-1 do - CEMainForm.WidgetList.widget[i].afterLoad(nil); + fFileMru.maxCount := aReader.ReadInteger; +end; + +procedure TCEMainForm.optget_ProjMRUItems(aWriter: TWriter); +begin + aWriter.WriteString(fProjMru.DelimitedText); +end; + +procedure TCEMainForm.optset_ProjMRUItems(aReader: TReader); +begin + fProjMru.DelimitedText := aReader.ReadString; +end; + +procedure TCEMainForm.optget_ProjMRULimit(aWriter: TWriter); +begin + aWriter.WriteInteger(fProjMru.maxCount); +end; + +procedure TCEMainForm.optset_ProjMRULimit(aReader: TReader); +begin + fProjMru.maxCount := aReader.ReadInteger; end; {$ENDREGION} @@ -1760,7 +1715,7 @@ var i: integer; begin if symString = '' then - exit(symString); + exit('``'); result := ''; elems := TStringList.Create; @@ -1798,62 +1753,55 @@ begin continue; 'CPF', 'CurrentProjectFile': begin - result += '`'; if fProject <> nil then if fileExists(fProject.fileName) then result += fProject.fileName; - result += '`'; end; 'CPP', 'CurrentProjectPath': begin - result += '`'; if fProject <> nil then if fileExists(fProject.fileName) then result += extractFilePath(fProject.fileName); - result += '`'; end; 'CPR', 'CurrentProjectRoot': begin - result += '`'; if fProject <> nil then if directoryExists(fProject.getAbsoluteFilename(fProject.RootFolder)) then result += fProject.getAbsoluteFilename(fProject.RootFolder) else if directoryExists(fProject.RootFolder) then result += fProject.RootFolder; - result += '`'; end; 'CFF', 'CurrentFileFile': begin - result += '`'; if fDoc <> nil then if fileExists(fDoc.fileName) then result += fDoc.fileName; - result += '`'; end; 'CFP', 'CurrentFilePath': begin - result += '`'; if fDoc <> nil then if fileExists(fDoc.fileName) then result += extractFilePath(fDoc.fileName); - result += '`'; end; 'CI', 'CurrentIdentifier': begin - result += '`'; if fDoc <> nil then result += fDoc.Identifier; - result += '`'; end; 'CAF', 'CoeditApplicationFile': - result += '`' + application.ExeName + '`'; + result += application.ExeName; 'CAP', 'CoeditApplicationPath': - result += '`' + extractFilePath(Application.ExeName) + '`'; + result += extractFilePath(Application.ExeName); end; end; finally elems.Free; end; + // as the result may be used in TProcess.Parameter, it has not to be empty + // otherwise next parameter switch can be considered as the parameter value, + // eg --a= --b --c, the program will think that --b is --a value if is empty. + if result = '' then + result += '``'; end; procedure PlugDispatchToHost(aPlugin: TCEPlugin; opCode: LongWord; data0: Integer; data1, data2: Pointer); cdecl; diff --git a/src/ce_messages.pas b/src/ce_messages.pas index 37b714bb..2ee6c264 100644 --- a/src/ce_messages.pas +++ b/src/ce_messages.pas @@ -1,7 +1,6 @@ unit ce_messages; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -62,7 +61,7 @@ type procedure addCeErr(const aMsg: string; aCtxt: TMessageContext = mcUnknown); procedure addCeWarn(const aMsg: string; aCtxt: TMessageContext = mcUnknown); // - procedure declareProperties(aFiler: TFiler); override; + procedure sesoptDeclareProperties(aFiler: TFiler); override; // function contextName: string; override; function contextActionCount: integer; override; @@ -154,7 +153,7 @@ begin end; {$ENDREGION} -{$REGION ICEWidgetPersist ------------------------------------------------------} +{$REGION ICESessionOptionsObserver ------------------------------------------------------} procedure TCEMessagesWidget.setMaxMessageCount(aValue: Integer); begin if aValue < 10 then aValue := 10; @@ -174,7 +173,7 @@ begin aWriter.WriteInteger(fMaxMessCnt); end; -procedure TCEMessagesWidget.declareProperties(aFiler: TFiler); +procedure TCEMessagesWidget.sesoptDeclareProperties(aFiler: TFiler); begin inherited; aFiler.DefineProperty(Name + '_MaxMessageCount', @optset_MaxMessageCount, @optget_MaxMessageCount, true); diff --git a/src/ce_miniexplorer.pas b/src/ce_miniexplorer.pas index 6a311533..9003ab58 100644 --- a/src/ce_miniexplorer.pas +++ b/src/ce_miniexplorer.pas @@ -1,7 +1,6 @@ unit ce_miniexplorer; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -55,7 +54,7 @@ type constructor create(aIwner: TComponent); override; destructor destroy; override; // - procedure declareProperties(aFiler: TFiler); override; + procedure sesoptDeclareProperties(aFiler: TFiler); override; // procedure expandPath(const aPath: string); end; @@ -100,7 +99,7 @@ end; {$ENDREGION} {$REGION ICEWidgetPersist ------------------------------------------------------} -procedure TCEMiniExplorerWidget.declareProperties(aFiler: TFiler); +procedure TCEMiniExplorerWidget.sesoptDeclareProperties(aFiler: TFiler); begin inherited; aFiler.DefineProperty(Name + '_LastFolder', @optset_LastFold, @optget_LastFold, true); diff --git a/src/ce_observer.pas b/src/ce_observer.pas index 3cc79928..953b8754 100644 --- a/src/ce_observer.pas +++ b/src/ce_observer.pas @@ -1,7 +1,6 @@ unit ce_observer; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -45,7 +44,7 @@ type end; (** - * Standard implementation of an ICESubject + * Standard implementation of an ICESubject *) TCECustomSubject = class(ICESubject) protected diff --git a/src/ce_options.pas b/src/ce_options.pas new file mode 100644 index 00000000..009f41e3 --- /dev/null +++ b/src/ce_options.pas @@ -0,0 +1,62 @@ +unit ce_options; + +{$I ce_defines.inc} + +interface + +uses + classes, sysutils, ce_common, ce_writableComponent, ce_observer; + +type + + TCEOptions = class(TWritableComponent) + private + fSubjPersObservers: TCECustomSubject; + protected + procedure defineProperties(Filer: TFiler); override; + procedure beforeSave; override; + procedure afterLoad; override; + public + constructor create(aOwner: TComponent); override; + destructor destroy; override; + end; + +implementation + +uses + ce_interfaces; + +constructor TCEOptions.create(aOwner: TComponent); +begin + inherited; + fSubjPersObservers := TCESessionOptionsSubject.create; + // + EntitiesConnector.addSubject(fSubjPersObservers); + EntitiesConnector.endUpdate; +end; + +destructor TCEOptions.destroy; +begin + EntitiesConnector.removeSubject(fSubjPersObservers); + EntitiesConnector.endUpdate; + // + fSubjPersObservers.Free; + inherited; +end; + +procedure TCEOptions.defineProperties(Filer: TFiler); +begin + subjSesOptsDeclareProperties(TCESessionOptionsSubject(fSubjPersObservers), Filer); +end; + +procedure TCEOptions.beforeSave; +begin + subjSesOptsBeforeSave(TCESessionOptionsSubject(fSubjPersObservers)); +end; + +procedure TCEOptions.afterLoad; +begin + subjSesOptsAfterLoad(TCESessionOptionsSubject(fSubjPersObservers)); +end; + +end. diff --git a/src/ce_plugin.pas b/src/ce_plugin.pas index 2f0dba29..18d1ef69 100644 --- a/src/ce_plugin.pas +++ b/src/ce_plugin.pas @@ -1,7 +1,6 @@ unit ce_plugin; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface diff --git a/src/ce_projconf.pas b/src/ce_projconf.pas index ca9b7237..e9937e8b 100644 --- a/src/ce_projconf.pas +++ b/src/ce_projconf.pas @@ -1,7 +1,6 @@ unit ce_projconf; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface diff --git a/src/ce_project.pas b/src/ce_project.pas index 8d470882..b1c7e64f 100644 --- a/src/ce_project.pas +++ b/src/ce_project.pas @@ -1,7 +1,6 @@ unit ce_project; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -77,7 +76,7 @@ type implementation uses - ce_interfaces; + ce_interfaces, controls, dialogs; constructor TCEProject.create(aOwner: TComponent); begin @@ -301,10 +300,7 @@ begin end; result := extractFilename(Sources.Strings[0]); result := result[1..length(result) - length(extractFileExt(result))]; - result := extractFilePath(fileName) + DirectorySeparator + result; - {$IFDEF MSWINDOWS} - result += '.exe'; - {$ENDIF} + result := extractFilePath(fileName) + DirectorySeparator + result + exeExt; end; procedure TCEProject.getOpts(const aList: TStrings); @@ -345,10 +341,52 @@ begin end; procedure TCEProject.afterLoad; +var + i, j: Integer; + src, ini, newdir: string; begin patchPlateformPaths(fSrcs); doChanged; fModified := false; + + // patch location: this only works when the project file is moved. + // if the source structure changes this doesn't help much. + // if both appends then the project must be restarted from scratch. + for i := 0 to fSrcs.Count-1 do + begin + src := getAbsoluteSourceName(i); + if fileExists(src) then + continue; + if ce_common.dlgOkCancel( + 'The project source(s) point to invalid file(s). ' + LineEnding + + 'This can be encountered if the project file has been moved from its original location.' + LineEnding + LineEnding + + 'Do you wish to select the new root folder ?') <> mrOk then + exit; + // hint for the common dir + src := fSrcs.Strings[i]; + while (src[1] = '.') or (src[1] = DirectorySeparator) do + src := src[2..length(src)]; + // prompt + ini := extractFilePath(fFilename); + if not selectDirectory( format('select the folder (which contains "%s")',[src]), ini, newdir) then + exit; + // patch + for j := i to fSrcs.Count-1 do + begin + src := fSrcs.Strings[j]; + while (src[1] = '.') or (src[1] = DirectorySeparator) do + src := src[2..length(src)]; + if fileExists(expandFilenameEx(fBasePath, newdir + DirectorySeparator + src)) then + fSrcs.Strings[j] := ExtractRelativepath(fBasePath, newdir + DirectorySeparator + src) + else break; // next pass: patch from another folder. + end; + end; + // + saveToFile(fFilename); + // warning for other relative paths + if fileExists(getAbsoluteSourceName(0)) then + ce_common.dlgOkInfo('the main sources paths has been patched, some others invalid ' + + 'paths may still exists (-of, -od, etc.) but cannot be automatically handled'); end; procedure TCEProject.readerPropNoFound(Reader: TReader; Instance: TPersistent; diff --git a/src/ce_projinspect.pas b/src/ce_projinspect.pas index cf15f348..20dc7968 100644 --- a/src/ce_projinspect.pas +++ b/src/ce_projinspect.pas @@ -1,7 +1,6 @@ unit ce_projinspect; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface diff --git a/src/ce_search.pas b/src/ce_search.pas index f6f38fad..53f9ab85 100644 --- a/src/ce_search.pas +++ b/src/ce_search.pas @@ -1,7 +1,6 @@ unit ce_search; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -62,7 +61,7 @@ type function contextActionCount: integer; override; function contextAction(index: integer): TAction; override; // - procedure declareProperties(aFiler: TFiler); override; + procedure sesoptDeclareProperties(aFiler: TFiler); override; // procedure actFindNextExecute(sender: TObject); procedure actReplaceNextExecute(sender: TObject); @@ -104,8 +103,8 @@ begin end; {$ENDREGION} -{$REGION ICEWidgetPersist ------------------------------------------------------} -procedure TCESearchWidget.declareProperties(aFiler: TFiler); +{$REGION ICESessionOptionsObserver ------------------------------------------------------} +procedure TCESearchWidget.sesoptDeclareProperties(aFiler: TFiler); begin inherited; aFiler.DefineProperty(Name + '_FindMRU', @optset_SearchMru, @optget_SearchMru, true); diff --git a/src/ce_staticexplorer.pas b/src/ce_staticexplorer.pas index 58bf9684..5492986f 100644 --- a/src/ce_staticexplorer.pas +++ b/src/ce_staticexplorer.pas @@ -1,7 +1,6 @@ unit ce_staticexplorer; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -74,12 +73,8 @@ type procedure projClosing(const aProject: TCEProject); procedure projFocused(const aProject: TCEProject); procedure projChanged(const aProject: TCEProject); - - procedure projCompile(const aProject: TCEProject); // warning: removed from itf - procedure projRun(const aProject: TCEProject); // warning: removed from itf - // - procedure declareProperties(aFiler: TFiler); override; + procedure sesoptDeclareProperties(aFiler: TFiler); override; end; implementation @@ -141,7 +136,7 @@ begin end; {$ENDREGION} -{$REGION ICEWidgetPersist ------------------------------------------------------} +{$REGION ICESessionOptionsObserver ------------------------------------------------------} procedure TCEStaticExplorerWidget.optget_AutoRefresh(aWriter: TWriter); begin aWriter.WriteBoolean(fAutoRefresh); @@ -175,7 +170,7 @@ begin fActRefreshOnFocus.Checked := fRefreshOnFocus; end; -procedure TCEStaticExplorerWidget.declareProperties(aFiler: TFiler); +procedure TCEStaticExplorerWidget.sesoptDeclareProperties(aFiler: TFiler); begin inherited; aFiler.DefineProperty(Name + '_AutoRefresh', @optset_AutoRefresh, @optget_AutoRefresh, true); @@ -278,16 +273,6 @@ procedure TCEStaticExplorerWidget.projChanged(const aProject: TCEProject); begin fProj := aProject; end; - -procedure TCEStaticExplorerWidget.projCompile(const aProject: TCEProject); -begin - stopUpdateByDelay; // warning: not triggered anymore -end; - -procedure TCEStaticExplorerWidget.projRun(const aProject: TCEProject); -begin - stopUpdateByDelay; // warning: not triggered anymore -end; {$ENDREGION} procedure TCEStaticExplorerWidget.UpdateByDelay; diff --git a/src/ce_synmemo.pas b/src/ce_synmemo.pas index 86ff5e6a..f95a7e2c 100644 --- a/src/ce_synmemo.pas +++ b/src/ce_synmemo.pas @@ -1,7 +1,6 @@ unit ce_synmemo; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface diff --git a/src/ce_widget.pas b/src/ce_widget.pas index ecf90a8c..eb720a6a 100644 --- a/src/ce_widget.pas +++ b/src/ce_widget.pas @@ -1,7 +1,6 @@ unit ce_widget; -{$MODE OBJFPC}{$H+} -{$INTERFACES CORBA} +{$I ce_defines.inc} interface @@ -15,7 +14,7 @@ type * Base type for an UI module. *) PTCEWidget = ^TCEWidget; - TCEWidget = class(TForm, ICEContextualActions, ICEWidgetPersist) + TCEWidget = class(TForm, ICEContextualActions, ICESessionOptionsObserver) Content: TPanel; Back: TPanel; contextMenu: TPopupMenu; @@ -70,9 +69,9 @@ type function contextActionCount: integer; virtual; function contextAction(index: integer): TAction; virtual; // - procedure beforeSave(sender: TObject); virtual; - procedure declareProperties(aFiler: TFiler); virtual; - procedure afterLoad(sender: TObject); virtual; + procedure sesoptBeforeSave; virtual; + procedure sesoptDeclareProperties(aFiler: TFiler); virtual; + procedure sesoptAfterLoad; virtual; // // returns true if one of the three updater is processing. property updating: boolean read fUpdating; @@ -102,6 +101,9 @@ type implementation {$R *.lfm} +uses + ce_observer; + {$REGION Standard Comp/Obj------------------------------------------------------} constructor TCEWidget.create(aOwner: TComponent); var @@ -123,29 +125,31 @@ begin itm.Action := contextAction(i); contextMenu.Items.Add(itm); end; - PopupMenu := contextMenu; + + EntitiesConnector.addObserver(self); end; destructor TCEWidget.destroy; begin + EntitiesConnector.removeObserver(self); inherited; end; {$ENDREGION} -{$REGION ICEWidgetPersist ------------------------------------------------------} -procedure TCEWidget.beforeSave(sender: TObject); +{$REGION ICESessionOptionsObserver ----------------------------------------------------} +procedure TCEWidget.sesoptBeforeSave; begin end; -procedure TCEWidget.declareProperties(aFiler: TFiler); +procedure TCEWidget.sesoptDeclareProperties(aFiler: TFiler); begin // override rules: inhertied must be called. No dots in the property name, property name prefixed with the widget Name aFiler.DefineProperty(Name + '_updaterByLoopInterval', @optset_LoopInterval, @optget_LoopInterval, true); aFiler.DefineProperty(Name + '_updaterByDelayDuration', @optset_UpdaterDelay, @optget_UpdaterDelay, true); end; -procedure TCEWidget.afterLoad(sender: TObject); +procedure TCEWidget.sesoptAfterLoad; begin end; diff --git a/src/ce_writablecomponent.pas b/src/ce_writablecomponent.pas index 0f96e46c..046fa49c 100644 --- a/src/ce_writablecomponent.pas +++ b/src/ce_writablecomponent.pas @@ -1,6 +1,6 @@ unit ce_writableComponent; -{$MODE OBJFPC}{$H+} +{$I ce_defines.inc} interface @@ -12,7 +12,8 @@ type (** * The ancestor of classes which can be saved or reloaded to/from * a text file. It's used each time some options or data have to - * persist from a cession to another. + * persist from a cession to another, independently from the centralized + * system provided by the ICESessionOptionObserver/Subject mechanism. *) TWritableComponent = class(TComponent) protected