From ff5a366c457644814385b06339ac6fb04f91ed12 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 4 Dec 2023 08:47:36 +0700 Subject: [PATCH] feat(developer): Open files in per-project instances Refactor the file-opening code so it can be used by Keyman Developer UI as well as command-line, and then add support for opening files in per-project instances from File|Open and friends. Also moves the initialization code out of tike.dpr into Keyman.Developer.System.Main.pas, which makes it easier to maintain and read. --- developer/src/tike/actions/dmActionsMain.pas | 5 +- developer/src/tike/main/DropTarget.pas | 4 +- ...Keyman.Developer.System.LaunchProjects.pas | 183 ++++++++++++++ .../main/Keyman.Developer.System.Main.pas | 87 +++++++ ...eyman.Developer.System.TikeCommandLine.pas | 224 ++++++------------ ...yman.Developer.System.TikeMultiProcess.pas | 7 - developer/src/tike/main/UfrmMain.pas | 75 ++++-- ...eyman.Developer.UI.Project.UfrmProject.pas | 4 +- developer/src/tike/tike.dpr | 46 +--- developer/src/tike/tike.dproj | 2 + 10 files changed, 420 insertions(+), 217 deletions(-) create mode 100644 developer/src/tike/main/Keyman.Developer.System.LaunchProjects.pas create mode 100644 developer/src/tike/main/Keyman.Developer.System.Main.pas diff --git a/developer/src/tike/actions/dmActionsMain.pas b/developer/src/tike/actions/dmActionsMain.pas index 4dea6bf471b..06d1ecd7770 100644 --- a/developer/src/tike/actions/dmActionsMain.pas +++ b/developer/src/tike/actions/dmActionsMain.pas @@ -334,11 +334,8 @@ procedure TmodActionsMain.actFileNewUpdate(Sender: TObject); end; procedure TmodActionsMain.actFileOpenAccept(Sender: TObject); -var - i: Integer; begin - for i := 0 to actFileOpen.Dialog.Files.Count - 1 do - frmKeymanDeveloper.OpenFileInProject(actFileOpen.Dialog.Files[i]); + frmKeymanDeveloper.OpenFilesInProject(actFileOpen.Dialog.Files.ToStringArray); end; procedure TmodActionsMain.actFileOpenUpdate(Sender: TObject); diff --git a/developer/src/tike/main/DropTarget.pas b/developer/src/tike/main/DropTarget.pas index 7edc770524c..e7236ad98d7 100644 --- a/developer/src/tike/main/DropTarget.pas +++ b/developer/src/tike/main/DropTarget.pas @@ -15,8 +15,8 @@ interface type IDragDrop = interface - function DropAllowed(const FileNames: array of string): Boolean; - procedure Drop(const FileNames: array of string); + function DropAllowed(const FileNames: TArray): Boolean; + procedure Drop(const FileNames: TArray); end; TDropTarget = class(TObject, IInterface, IDropTarget) diff --git a/developer/src/tike/main/Keyman.Developer.System.LaunchProjects.pas b/developer/src/tike/main/Keyman.Developer.System.LaunchProjects.pas new file mode 100644 index 00000000000..376ac4fb8e0 --- /dev/null +++ b/developer/src/tike/main/Keyman.Developer.System.LaunchProjects.pas @@ -0,0 +1,183 @@ +unit Keyman.Developer.System.LaunchProjects; + +interface + +uses + System.Classes, + System.Generics.Collections, + System.SysUtils, + + Keyman.Developer.System.TikeMultiProcess; + +type + TLaunchProjectStatus = (lpsWaiting, lpsCurrentInstance, lpsOtherInstance, lpsNewInstance, lpsError); + TLaunchProject = class + private + FProjectFilename: string; + FFilenames: TStringList; + FStatus: TLaunchProjectStatus; + public + constructor Create(const AProjectFilename: string); + destructor Destroy; override; + function LaunchAsNewInstance: Boolean; + function PassToRunningProcess(AProcesses: TTikeProcessList): Boolean; + property ProjectFilename: string read FProjectFilename; + property Filenames: TStringList read FFilenames; + property Status: TLaunchProjectStatus read FStatus; + end; + + TLaunchProjects = class(TObjectList) + private + FReserveStartupProject: Boolean; + function GetStartupProject: TLaunchProject; + public + constructor Create(AReserveStartupProject: Boolean); + procedure GroupFilenamesIntoProjects(const Filenames: TStringList); + function LaunchAll(AProcesses: TTikeProcessList): Boolean; + function Find(const ProjectFilename: string): TLaunchProject; + property ReserveStartupProject: Boolean read FReserveStartupProject; + property StartupProject: TLaunchProject read GetStartupProject; + end; + +implementation + +uses + Winapi.Windows, + + Keyman.Developer.System.ProjectOwningFile, + utilexecute; + +{ TLaunchProject } + +constructor TLaunchProject.Create(const AProjectFilename: string); +begin + inherited Create; + FProjectFilename := AProjectFilename; + FFilenames := TStringList.Create; + FStatus := lpsWaiting; +end; + +destructor TLaunchProject.Destroy; +begin + FFilenames.Free; + inherited Destroy; +end; + +function TLaunchProject.LaunchAsNewInstance: Boolean; +var + filename, cmdline: string; +begin + cmdline := '"'+ParamStr(0)+'" --sub-process "'+Self.ProjectFilename+'"'; + for filename in Self.Filenames do + cmdline := cmdline + ' "'+filename+'"'; + + Result := TUtilExecute.Execute(cmdline, GetCurrentDir, SW_SHOWNORMAL); + + if Result + then FStatus := lpsNewInstance + else FStatus := lpsError; +end; + +function TLaunchProject.PassToRunningProcess(AProcesses: TTikeProcessList): Boolean; +var + tp: TTikeProcess; + filename: string; +begin + for tp in AProcesses do + begin + if tp.OwnsProject(Self.ProjectFilename) then + begin + FStatus := lpsOtherInstance; + + if Self.Filenames.Count = 0 then + begin + // Ensure that the project file tab is opened + tp.OpenFile(Self.ProjectFilename); + end; + + for filename in Self.Filenames do + begin + tp.OpenFile(filename); + end; + Exit(True); + end; + end; + Result := False; +end; + +{ TLaunchProjects } + +constructor TLaunchProjects.Create(AReserveStartupProject: Boolean); +begin + inherited Create(True); + FReserveStartupProject := AReserveStartupProject; +end; + +function TLaunchProjects.Find(const ProjectFilename: string): TLaunchProject; +begin + for Result in Self do + begin + if Result.ProjectFilename = ProjectFilename then + begin + Exit; + end; + end; + Result := nil; +end; + +function TLaunchProjects.GetStartupProject: TLaunchProject; +begin + for Result in Self do + if Result.Status = lpsCurrentInstance then + Exit; + Result := nil; +end; + +procedure TLaunchProjects.GroupFilenamesIntoProjects(const Filenames: TStringList); +var + projectFilename, filename: string; + p: TLaunchProject; +begin + for filename in filenames do + begin + projectFilename := FindOwnerProjectForFile(filename); + + p := Self.Find(projectFilename); + if p = nil then + begin + p := TLaunchProject.Create(projectFilename); + Self.Add(p); + end; + p.Filenames.Add(filename); + end; +end; + +function TLaunchProjects.LaunchAll(AProcesses: TTikeProcessList): Boolean; +var + p: TLaunchProject; +begin + Result := False; + + // Hand off files to existing processes, based on project folder + for p in Self do + begin + if not p.PassToRunningProcess(AProcesses) then + begin + // If there isn't an existing process, then we need to launch a new + // process for the project + if not Result and ReserveStartupProject then + begin + // For performance, we'll take the first project for the current process + // if requested + p.FStatus := lpsCurrentInstance; + Result := True; + end + else + begin + p.LaunchAsNewInstance; + end; + end; + end; +end; + +end. diff --git a/developer/src/tike/main/Keyman.Developer.System.Main.pas b/developer/src/tike/main/Keyman.Developer.System.Main.pas new file mode 100644 index 00000000000..43cb5c674da --- /dev/null +++ b/developer/src/tike/main/Keyman.Developer.System.Main.pas @@ -0,0 +1,87 @@ +unit Keyman.Developer.System.Main; + +interface + +uses + System.SysUtils, + System.Win.ComObj, + Vcl.Forms, + Winapi.ActiveX, + Winapi.UxTheme, + + uCEFApplication, + uCEFTypes, + + Keyman.Developer.System.TikeCommandLine, + Keyman.System.CEFManager, + Keyman.System.KeymanSentryClient, + Sentry.Client, + Sentry.Client.Vcl, + UfrmMain, + UmodWebHttpServer; + +procedure RunKeymanDeveloper; + +implementation + +const + LOGGER_DEVELOPER_IDE_TIKE = TKeymanSentryClient.LOGGER_DEVELOPER_IDE + '.tike'; + +procedure RunWithExceptionsHandled; forward; + +procedure RunKeymanDeveloper; +begin + CoInitFlags := COINIT_APARTMENTTHREADED; + Application.MainFormOnTaskBar := True; + Application.Initialize; + Application.Title := 'Keyman Developer'; + + TKeymanSentryClient.Start(TSentryClientVcl, kscpDeveloper, LOGGER_DEVELOPER_IDE_TIKE); + try + try + RunWithExceptionsHandled; + except + on E:Exception do + SentryHandleException(E); + end; + finally + TKeymanSentryClient.Stop; + end; +end; + +procedure RunWithExceptionsHandled; +begin + FInitializeCEF := TCEFManager.Create; + try + if GlobalCEFApp.ProcessType = ptBrowser then + begin + // We want to process the command line only if we are not a CEF + // sub-process, because otherwise we lose the benefit of + if TikeCommandLine.Process = pclExit then + begin + Exit; + end; + end; + + if FInitializeCEF.Start then + begin + InitThemeLibrary; + SetThemeAppProperties(STAP_ALLOW_NONCLIENT or STAP_ALLOW_CONTROLS or STAP_ALLOW_WEBCONTENT); + Application.CreateForm(TmodWebHttpServer, modWebHttpServer); + try + Application.CreateForm(TfrmKeymanDeveloper, frmKeymanDeveloper); + try + Application.Run; + finally + FreeAndNil(frmKeymanDeveloper); + end; + finally + FreeAndNil(modWebHttpServer); + end; + end; + finally + FInitializeCEF.Free; + end; +end; + +end. diff --git a/developer/src/tike/main/Keyman.Developer.System.TikeCommandLine.pas b/developer/src/tike/main/Keyman.Developer.System.TikeCommandLine.pas index 9a48062c7f2..ab05833e4f5 100644 --- a/developer/src/tike/main/Keyman.Developer.System.TikeCommandLine.pas +++ b/developer/src/tike/main/Keyman.Developer.System.TikeCommandLine.pas @@ -11,23 +11,6 @@ interface type TProcessCommandLine = (pclRun, pclExit); -type - TLaunchProject = class - strict private - FProjectFilename: string; - FFilenames: TStringList; - public - constructor Create(const AProjectFilename: string); - destructor Destroy; override; - property ProjectFilename: string read FProjectFilename; - property Filenames: TStringList read FFilenames; - end; - - TLaunchProjects = class(TObjectList) - public - function Find(const ProjectFilename: string): TLaunchProject; - end; - type TTikeCommandLine = class strict private @@ -35,9 +18,8 @@ TTikeCommandLine = class private FStartupProjectPath: string; FStartupFilenames: TArray; - function PassProjectToRunningProcess(const project: TLaunchProject): Boolean; - function LaunchNewInstance(const project: TLaunchProject): Boolean; function ProcessSubProcess: Boolean; + function GetFilenamesFromCommandLine(filenames: TStringList): Boolean; public constructor Create; destructor Destroy; override; @@ -53,29 +35,14 @@ implementation uses System.SysUtils, + Vcl.Dialogs, Winapi.Windows, ErrorControlledRegistry, - Keyman.Developer.System.ProjectOwningFile, + Keyman.Developer.System.LaunchProjects, RegistryKeys, utilexecute; -{ TLaunchProject } - -constructor TLaunchProject.Create(const AProjectFilename: string); -begin - inherited Create; - FProjectFilename := AProjectFilename; - FFilenames := TStringList.Create; -end; - -destructor TLaunchProject.Destroy; -begin - FFilenames.Free; - inherited Destroy; -end; - - { TTikeCommandLine } constructor TTikeCommandLine.Create; @@ -112,101 +79,43 @@ destructor TTikeCommandLine.Destroy; inherited Destroy; end; -/// Reads filenames passed on command line, and determines if they should be -/// opened in an existing instance of Keyman Developer or in this instance, or -/// even in multiple new instances -function TTikeCommandLine.Process: TProcessCommandLine; +function TTikeCommandLine.GetFilenamesFromCommandLine(filenames: TStringList): Boolean; var + filename, missingFilenames: string; + NoMoreSwitches: Boolean; i: Integer; - filename: string; - filenames: TStringList; - newProjects, launchProjects: TLaunchProjects; - p: TLaunchProject; - projectFilename: string; begin - // If launched by LaunchNewInstance then we'll process that and continue - if ProcessSubProcess then - begin - Exit(pclRun); - end; - - filenames := TStringList.Create; - launchProjects := TLaunchProjects.Create; - newProjects := TLaunchProjects.Create(False); - try - // TODO: are there any other parameters passed to TIKE? - for i := 1 to ParamCount do - begin - filenames.Add(ExpandFileName(ParamStr(i))); - end; - - if filenames.Count = 0 then - begin - // No filenames were passed on the command line - if FProcesses.Count > 0 then - begin - // Because this is a new instance and there are already running instances, - // we'll start Keyman Developer without opening a project, assuming that - // the last opened project has already been opened. - FStartupProjectPath := ''; - end; - Exit(pclRun); - end; - - for filename in filenames do - begin - projectFilename := FindOwnerProjectForFile(filename); - - p := launchProjects.Find(projectFilename); - if p = nil then - begin - p := TLaunchProject.Create(projectFilename); - launchProjects.Add(p); - end; - p.Filenames.Add(filename); - end; - - // First, hand off files to existing processes, based on project folder - for p in launchProjects do - begin - if not PassProjectToRunningProcess(p) then - begin - newProjects.Add(p); - end; - end; + missingFilenames := ''; + NoMoreSwitches := False; - // If we've already passed all filenames off, then we're done - if newProjects.Count = 0 then + for i := 1 to ParamCount do + begin + if not NoMoreSwitches and ParamStr(i).StartsWith('-') then begin - Exit(pclExit); + // We will treat all `-x` and `--x` parameters as command-line switches, + // which provides forward compatibility for when we want to support + // additional switches. A single `--` parameter tells us that remaining + // parameters are filenames, even if they start with `-`. + if ParamStr(i) = '--' then + NoMoreSwitches := True; + Continue; end; - // If there's more than one project left, then we need to launch new - // processes for each one; we'll take the first for ourselves - FStartupFilenames := newProjects[0].Filenames.ToStringArray; - FStartupProjectPath := newProjects[0].ProjectFilename; - - newProjects.Delete(0); - for p in newProjects do - begin - LaunchNewInstance(p); - end; - finally - filenames.Free; + filename := ExpandFileName(ParamStr(i)); + if FileExists(filename) + then filenames.Add(filename) + else missingFilenames := missingFilenames + '• ' + filename + #13#10; end; - Result := pclRun; -end; - -function TTikeCommandLine.LaunchNewInstance(const project: TLaunchProject): Boolean; -var - filename, cmdline: string; -begin - cmdline := '"'+ParamStr(0)+'" --sub-process "'+project.ProjectFilename+'"'; - for filename in project.Filenames do - cmdline := cmdline + ' "'+filename+'"'; + if missingFilenames <> '' then + begin + ShowMessage('The following file(s) could not be found:'#13#10+missingFilenames); + if filenames.Count = 0 then + // If we only had bogus filenames passed in then we should abort + Exit(False); + end; - Result := TUtilExecute.Execute(cmdline, GetCurrentDir, SW_SHOWNORMAL); + Result := True; end; function TTikeCommandLine.ProcessSubProcess: Boolean; @@ -218,7 +127,7 @@ function TTikeCommandLine.ProcessSubProcess: Boolean; Exit(False); end; - // TODO: Consider error handling + // TODO(lowpri): Consider error handling FStartupProjectPath := ParamStr(2); SetLength(FStartupFilenames, ParamCount - 2); @@ -230,24 +139,61 @@ function TTikeCommandLine.ProcessSubProcess: Boolean; Result := True; end; -function TTikeCommandLine.PassProjectToRunningProcess(const project: TLaunchProject): Boolean; +/// Reads filenames passed on command line, and determines if they should be +/// opened in an existing instance of Keyman Developer or in this instance, or +/// even in multiple new instances +function TTikeCommandLine.Process: TProcessCommandLine; var - tp: TTikeProcess; - filename: string; + filenames: TStringList; + projects: TLaunchProjects; begin - for tp in FProcesses do + // If launched by LaunchNewInstance then we'll process that and continue + if ProcessSubProcess then begin - if tp.OwnsProject(project.ProjectFilename) then + Exit(pclRun); + end; + + filenames := TStringList.Create; + projects := TLaunchProjects.Create(True); + try + // Collect filenames passed in on command line + if not GetFilenamesFromCommandLine(filenames) then + Exit(pclExit); + + if filenames.Count = 0 then begin - Result := True; - for filename in project.Filenames do + // No filenames were passed on the command line + if FProcesses.Count > 0 then begin - tp.OpenFile(filename); + // Because this is a new instance and there are already running instances, + // we'll start Keyman Developer without opening a project, assuming that + // the last opened project has already been opened. + FStartupProjectPath := ''; end; - Exit; + Exit(pclRun); + end; + + // Sort filenames into project groupings + + projects.GroupFilenamesIntoProjects(filenames); + + // Launch new processes or load files into existing processes + + if not projects.LaunchAll(FProcesses) + then Result := pclExit + else Result := pclRun; + + if Assigned(projects.StartupProject) then + begin + // LaunchAll will leave one startup project for this current process + // instance to load + FStartupFilenames := projects.StartupProject.Filenames.ToStringArray; + FStartupProjectPath := projects.StartupProject.ProjectFilename; end; + finally + projects.Free; + filenames.Free; end; - Result := False; end; var @@ -260,20 +206,6 @@ function TikeCommandLine: TTikeCommandLine; Result := FInstance; end; -{ TLaunchProjects } - -function TLaunchProjects.Find(const ProjectFilename: string): TLaunchProject; -begin - for Result in Self do - begin - if Result.ProjectFilename = ProjectFilename then - begin - Exit; - end; - end; - Result := nil; -end; - initialization finalization FreeAndNil(FInstance); diff --git a/developer/src/tike/main/Keyman.Developer.System.TikeMultiProcess.pas b/developer/src/tike/main/Keyman.Developer.System.TikeMultiProcess.pas index d56cc008f9e..2cd35d8204b 100644 --- a/developer/src/tike/main/Keyman.Developer.System.TikeMultiProcess.pas +++ b/developer/src/tike/main/Keyman.Developer.System.TikeMultiProcess.pas @@ -133,13 +133,6 @@ function TTikeProcess.FocusProcess: Boolean; function TTikeProcess.OpenFile(const filename: string): Boolean; begin - if SameFileName(filename, ProjectFilename) then - begin - // We always accept opening the project that is already open, - // so just bring the process to the front - Exit(FocusProcess); - end; - if not TCopyDataHelper.SendData(0, WindowHandle, TCopyDataCommand.CD_OPENFILE, filename) then begin // The target app did not receive the data diff --git a/developer/src/tike/main/UfrmMain.pas b/developer/src/tike/main/UfrmMain.pas index 7ab3ea2b37d..70720146a68 100644 --- a/developer/src/tike/main/UfrmMain.pas +++ b/developer/src/tike/main/UfrmMain.pas @@ -387,8 +387,8 @@ TfrmKeymanDeveloper = class(TTikeForm, IUnicodeDataUIManager, IDragDrop) { IDropTarget } - procedure Drop(const FileNames: array of string); - function DropAllowed(const FileNames: array of string): Boolean; + procedure Drop(const FileNames: TArray); + function DropAllowed(const FileNames: TArray): Boolean; procedure InitDock; procedure LoadDockLayout; procedure SaveDockLayout; @@ -445,7 +445,7 @@ TfrmKeymanDeveloper = class(TTikeForm, IUnicodeDataUIManager, IDragDrop) function OpenEditor(FFileName: string; frmClass: TfrmTikeEditorClass): TfrmTikeEditor; function OpenFile(FFileName: string; FCloseNewFile: Boolean): TfrmTikeChild; - procedure OpenFileInProject(FFileName: string); + procedure OpenFilesInProject(FFileNames: TArray); procedure HelpTopic(s: string); overload; procedure HelpTopic(Sender: TTIKEForm); overload; @@ -481,6 +481,7 @@ implementation Keyman.System.KeymanSentryClient, Keyman.Developer.UI.TikeOnlineUpdateCheck, GlobalProxySettings, + Keyman.Developer.System.LaunchProjects, Keyman.Developer.System.Project.ProjectFile, Keyman.Developer.System.Project.ProjectFileType, Keyman.Developer.System.Project.WelcomeRenderer, @@ -488,6 +489,7 @@ implementation Keyman.Developer.System.Project.ProjectLog, Keyman.Developer.System.Project.XmlLdmlProjectFile, Keyman.Developer.System.TikeCommandLine, + Keyman.Developer.System.TikeMultiProcess, Keyman.Developer.UI.Project.ProjectFileUI, Keyman.Developer.UI.Project.ProjectUI, Keyman.Developer.UI.UfrmLdmlKeyboardEditor, @@ -1207,12 +1209,55 @@ procedure TfrmKeymanDeveloper.pagesCloseTab(Sender: TObject; Index: Integer); PostMessage(TfrmTikeChild(pages.Pages[Index].Tag).Handle, WM_CLOSE, 0, 0); end; -procedure TfrmKeymanDeveloper.OpenFileInProject(FFileName: string); +procedure TfrmKeymanDeveloper.OpenFilesInProject(FFileNames: TArray); +var + filename: string; + filenames: TStringList; + projects: TLaunchProjects; + p: TLaunchProject; + FProcesses: TTikeProcessList; begin + if Length(FFileNames) = 0 then + Exit; + // Uses the TTikeCommandLine pattern to open the resulting file either locally // or in a remote process - // TODO - OpenFile(FFileName, True); + FProcesses := TikeMultiProcess.Enumerate; + projects := TLaunchProjects.Create(False); + filenames := TStringList.Create; + try + filenames.AddStrings(FFileNames); + FFileNames := []; + + // Sort filenames into project groupings + projects.GroupFilenamesIntoProjects(filenames); + + // Look for the current instance's project in the list of projects, and + // capture its list of filenames separately + if IsGlobalProjectUIReady then + begin + for p in projects do + begin + if SameFileName(p.ProjectFilename, FGlobalProject.FileName) then + begin + FFileNames := p.Filenames.ToStringArray; + projects.Remove(p); + Break; + end; + end; + end; + + // Launch new processes or load files into existing processes + projects.LaunchAll(FProcesses); + finally + filenames.Free; + projects.Free; + FProcesses.Free; + end; + + // Finally, open the remaining files which belong to this instance's project + for filename in FFileNames do + OpenFile(filename, True); end; function TfrmKeymanDeveloper.OpenFile(FFileName: string; FCloseNewFile: Boolean): TfrmTikeChild; @@ -1253,6 +1298,12 @@ function TfrmKeymanDeveloper.OpenFile(FFileName: string; FCloseNewFile: Boolean) if ext = Ext_ProjectSource then begin + if SameFileName(GetGlobalProjectUI.FileName, FFileName) then + begin + ShowProject; + Exit; + end; + if BeforeOpenProject then modActionsMain.OpenProject(FFileName); end @@ -1420,23 +1471,19 @@ procedure TfrmKeymanDeveloper.UpdateFileMRU; procedure TfrmKeymanDeveloper.mnuFileRecentFileClick(Sender: TObject); begin with Sender as TMenuItem do - OpenFileInProject(Hint); + OpenFilesInProject([Hint]); end; {------------------------------------------------------------------------------- - Drag and Drop functionality - -------------------------------------------------------------------------------} -procedure TfrmKeymanDeveloper.Drop(const FileNames: array of string); -var - i: Integer; +procedure TfrmKeymanDeveloper.Drop(const FileNames: TArray); begin - for i := Low(Filenames) to High(Filenames) do - OpenFileInProject(Filenames[i]); + OpenFilesInProject(FileNames); end; -function TfrmKeymanDeveloper.DropAllowed( - const FileNames: array of string): Boolean; +function TfrmKeymanDeveloper.DropAllowed(const FileNames: TArray): Boolean; begin Result := True; end; diff --git a/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProject.pas b/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProject.pas index 643742968d7..e1c5a73621e 100644 --- a/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProject.pas +++ b/developer/src/tike/project/Keyman.Developer.UI.Project.UfrmProject.pas @@ -440,9 +440,9 @@ procedure TfrmProject.WebCommandProject(Command: WideString; Params: TStringList pf := SelectedProjectFile; if Assigned(pf) then (pf.UI as TProjectFileUI).DefaultEvent(Self) // I4687 else if SelectedMRUFileName <> '' then - frmKeymanDeveloper.OpenFileInProject(SelectedMRUFileName) + frmKeymanDeveloper.OpenFilesInProject([SelectedMRUFileName]) else if Params.Values['name'] <> '' then - frmKeymanDeveloper.OpenFileInProject(Params.Values['name']); + frmKeymanDeveloper.OpenFilesInProject([Params.Values['name']]); end else if Command = 'viewfilesource' then begin diff --git a/developer/src/tike/tike.dpr b/developer/src/tike/tike.dpr index df59fa64ac2..63900f50af0 100644 --- a/developer/src/tike/tike.dpr +++ b/developer/src/tike/tike.dpr @@ -293,7 +293,9 @@ uses Keyman.Developer.System.MultiProcess in 'main\Keyman.Developer.System.MultiProcess.pas', Keyman.Developer.System.TikeMultiProcess in 'main\Keyman.Developer.System.TikeMultiProcess.pas', Keyman.System.CopyDataHelper in 'main\Keyman.System.CopyDataHelper.pas', - Keyman.Developer.System.ProjectOwningFile in 'main\Keyman.Developer.System.ProjectOwningFile.pas'; + Keyman.Developer.System.ProjectOwningFile in 'main\Keyman.Developer.System.ProjectOwningFile.pas', + Keyman.Developer.System.Main in 'main\Keyman.Developer.System.Main.pas', + Keyman.Developer.System.LaunchProjects in 'main\Keyman.Developer.System.LaunchProjects.pas'; {$R *.RES} {$R ICONS.RES} @@ -304,46 +306,6 @@ uses // If you don't add this flag the rederer process will crash when you try to load large images. {$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE} -const - LOGGER_DEVELOPER_IDE_TIKE = TKeymanSentryClient.LOGGER_DEVELOPER_IDE + '.tike'; begin - TKeymanSentryClient.Start(TSentryClientVcl, kscpDeveloper, LOGGER_DEVELOPER_IDE_TIKE); - try - - case TikeCommandLine.Process of - pclExit: - Exit; - end; - - try - CoInitFlags := COINIT_APARTMENTTHREADED; - - FInitializeCEF := TCEFManager.Create; - try - if FInitializeCEF.Start then - begin - InitThemeLibrary; - SetThemeAppProperties(STAP_ALLOW_NONCLIENT or STAP_ALLOW_CONTROLS or STAP_ALLOW_WEBCONTENT); - Application.MainFormOnTaskBar := True; - Application.Initialize; - Application.Title := 'Keyman Developer'; - Application.CreateForm(TmodWebHttpServer, modWebHttpServer); - try - Application.CreateForm(TfrmKeymanDeveloper, frmKeymanDeveloper); - Application.Run; - finally - FreeAndNil(frmKeymanDeveloper); - FreeAndNil(modWebHttpServer); - end; - end; - finally - FInitializeCEF.Free; - end; - except - on E:Exception do - SentryHandleException(E); - end; - finally - TKeymanSentryClient.Stop; - end; + RunKeymanDeveloper; end. diff --git a/developer/src/tike/tike.dproj b/developer/src/tike/tike.dproj index 012de624012..ea847fbc406 100644 --- a/developer/src/tike/tike.dproj +++ b/developer/src/tike/tike.dproj @@ -582,6 +582,8 @@ + + Cfg_2