mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-29 11:07:41 +00:00
[developer] Refactor of project delivery via internal web server instead of file system
This commit is contained in:
parent
57b07d8071
commit
bb0aaedb73
25 changed files with 458 additions and 150 deletions
|
|
@ -5,10 +5,15 @@ interface
|
|||
uses
|
||||
IdContext,
|
||||
IdCustomHTTPServer,
|
||||
IdHTTPServer;
|
||||
IdHTTPServer,
|
||||
|
||||
Keyman.Developer.System.HttpServer.Base;
|
||||
|
||||
type
|
||||
TAppHttpServer = class
|
||||
TAppHttpResponder = class(TBaseHttpResponder)
|
||||
private
|
||||
procedure RespondProject(doc: string; AContext: TIdContext;
|
||||
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
|
||||
public
|
||||
procedure ProcessRequest(AContext: TIdContext;
|
||||
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
|
||||
|
|
@ -16,12 +21,119 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
|
||||
ProjectFile;
|
||||
|
||||
{ TAppHttpServer }
|
||||
|
||||
procedure TAppHttpServer.ProcessRequest(AContext: TIdContext;
|
||||
procedure TAppHttpResponder.RespondProject(doc: string; AContext: TIdContext;
|
||||
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
|
||||
begin
|
||||
|
||||
procedure RespondProjectFile;
|
||||
var
|
||||
path: string;
|
||||
begin
|
||||
if ARequestInfo.Params.IndexOfName('path') < 0 then
|
||||
begin
|
||||
AResponseInfo.ResponseNo := 400;
|
||||
AResponseInfo.ResponseText := 'Missing parameter path';
|
||||
Exit;
|
||||
end;
|
||||
|
||||
path := ARequestInfo.Params.Values['path'];
|
||||
|
||||
if not FileExists(path) or not SameText(ExtractFileExt(path), '.kpj') then
|
||||
begin
|
||||
AResponseInfo.ResponseNo := 400;
|
||||
AResponseInfo.ResponseText := 'Project file '+path+' does not exist.';
|
||||
Exit;
|
||||
end;
|
||||
|
||||
// Transform the .kpj
|
||||
with TProject.Create(path) do
|
||||
try
|
||||
AResponseInfo.ContentType := 'text/html; charset=UTF-8';
|
||||
AResponseInfo.ContentText := Render;
|
||||
finally
|
||||
Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure RespondIco;
|
||||
var
|
||||
path: string;
|
||||
begin
|
||||
if ARequestInfo.Params.IndexOfName('path') < 0 then
|
||||
begin
|
||||
AResponseInfo.ResponseNo := 400;
|
||||
AResponseInfo.ResponseText := 'Missing parameter path';
|
||||
Exit;
|
||||
end;
|
||||
|
||||
path := ARequestInfo.Params.Values['path'];
|
||||
|
||||
if not FileExists(path) or (
|
||||
not SameText(ExtractFileExt(path), '.ico') and
|
||||
not SameText(ExtractFileExt(path), '.bmp')
|
||||
) then
|
||||
begin
|
||||
AResponseInfo.ResponseNo := 400;
|
||||
AResponseInfo.ResponseText := 'Image file '+path+' does not exist.';
|
||||
Exit;
|
||||
end;
|
||||
|
||||
RespondFile(path, AContext, ARequestInfo, AResponseInfo);
|
||||
end;
|
||||
|
||||
procedure RespondResource;
|
||||
begin
|
||||
Delete(doc, 1, 4);
|
||||
if (Pos('/', doc) > 0) or (Pos('\', doc) > 0) then
|
||||
Respond404(AContext, ARequestInfo, AResponseInfo);
|
||||
RespondFile(TProject.StandardTemplatePath + '/' + doc, AContext, ARequestInfo, AResponseInfo);
|
||||
end;
|
||||
begin
|
||||
//
|
||||
// http://localhost:8008/app/project/(index)?path=<fqp-to-.kpj>
|
||||
// http://localhost:8008/app/project/<resource>
|
||||
//
|
||||
Delete(doc, 1, 8);
|
||||
if (doc = '') or (doc = 'index') then
|
||||
RespondProjectFile
|
||||
else if doc = 'ico' then
|
||||
RespondIco
|
||||
else if Copy(doc, 1, 4) = 'res/' then
|
||||
RespondResource;
|
||||
end;
|
||||
|
||||
procedure TAppHttpResponder.ProcessRequest(AContext: TIdContext;
|
||||
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
|
||||
var
|
||||
doc: string;
|
||||
begin
|
||||
// We know the request starts with /app/
|
||||
doc := ARequestInfo.Document;
|
||||
Delete(doc, 1, 5); // /app/
|
||||
|
||||
// App requests are only accepted from local computer
|
||||
// So test that the request comes from localhost
|
||||
if (AContext.Binding.PeerIP <> '127.0.0.1') and
|
||||
(AContext.Binding.PeerIP <> '0:0:0:0:0:0:0:1') then
|
||||
begin
|
||||
AResponseInfo.ResponseNo := 403;
|
||||
AResponseInfo.ResponseText := 'Access denied';
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if Copy(doc, 1, 8) = 'project/' then
|
||||
begin
|
||||
RespondProject(doc, AContext, ARequestInfo, AResponseInfo);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Respond404(AContext, ARequestInfo, AResponseInfo);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -271,7 +271,8 @@ uses
|
|||
Keyman.System.CanonicalLanguageCodeUtils in '..\..\global\delphi\general\Keyman.System.CanonicalLanguageCodeUtils.pas',
|
||||
Keyman.Developer.System.InitializeCEF in 'main\Keyman.Developer.System.InitializeCEF.pas',
|
||||
Keyman.Developer.System.HttpServer.Debugger in 'http\Keyman.Developer.System.HttpServer.Debugger.pas',
|
||||
Keyman.Developer.System.HttpServer.App in 'Keyman.Developer.System.HttpServer.App.pas';
|
||||
Keyman.Developer.System.HttpServer.App in 'Keyman.Developer.System.HttpServer.App.pas',
|
||||
Keyman.Developer.System.HttpServer.Base in 'http\Keyman.Developer.System.HttpServer.Base.pas';
|
||||
|
||||
{$R *.RES}
|
||||
{$R ICONS.RES}
|
||||
|
|
|
|||
|
|
@ -511,6 +511,7 @@
|
|||
<DCCReference Include="main\Keyman.Developer.System.InitializeCEF.pas"/>
|
||||
<DCCReference Include="http\Keyman.Developer.System.HttpServer.Debugger.pas"/>
|
||||
<DCCReference Include="Keyman.Developer.System.HttpServer.App.pas"/>
|
||||
<DCCReference Include="http\Keyman.Developer.System.HttpServer.Base.pas"/>
|
||||
<None Include="Profiling\AQtimeModule1.aqt"/>
|
||||
<BuildConfiguration Include="Debug">
|
||||
<Key>Cfg_2</Key>
|
||||
|
|
|
|||
|
|
@ -482,7 +482,7 @@ object modActionsMain: TmodActionsMain
|
|||
Left = 304
|
||||
Top = 236
|
||||
Bitmap = {
|
||||
494C01010A000E00680010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||
494C01010A000E006C0010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||
0000000000003600000028000000400000003000000001002000000000000030
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
|
|
@ -891,7 +891,7 @@ object modActionsMain: TmodActionsMain
|
|||
Left = 124
|
||||
Top = 116
|
||||
Bitmap = {
|
||||
494C0101110013007C0020002000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||
494C010111001300800020002000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||
000000000000360000002800000080000000A000000001002000000000000040
|
||||
0100000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000A8CAFF00A8CA
|
||||
|
|
|
|||
|
|
@ -228,6 +228,7 @@ uses
|
|||
ProjectFile,
|
||||
ProjectFileType,
|
||||
ProjectFileUI,
|
||||
ProjectUI,
|
||||
GlobalProxySettings,
|
||||
RegistryKeys,
|
||||
TextFileFormat,
|
||||
|
|
@ -276,7 +277,7 @@ begin
|
|||
if FileName <> '' then
|
||||
begin
|
||||
if AddToProject then
|
||||
FEditor.ProjectFile := CreateProjectFile(FileName, nil);
|
||||
FEditor.ProjectFile := CreateProjectFile(FGlobalProject, FileName, nil);
|
||||
FEditor.OpenFile(FileName);
|
||||
end;
|
||||
end;
|
||||
|
|
@ -450,7 +451,7 @@ begin
|
|||
begin
|
||||
if not Assigned(ActiveEditor) or ActiveEditor.Untitled then Exit;
|
||||
if FGlobalProject.Files.IndexOfFileName(ActiveEditor.FileName) >= 0 then Exit;
|
||||
ActiveEditor.ProjectFile := CreateProjectFile(ActiveEditor.FileName, nil);
|
||||
ActiveEditor.ProjectFile := CreateProjectFile(FGlobalProject, ActiveEditor.FileName, nil);
|
||||
ShowProject;
|
||||
end;
|
||||
end;
|
||||
|
|
@ -470,7 +471,7 @@ var
|
|||
begin
|
||||
for i := 0 to actProjectAddFiles.Dialog.Files.Count - 1 do
|
||||
if FGlobalProject.Files.IndexOfFileName(actProjectAddFiles.Dialog.Files[i]) < 0 then
|
||||
CreateProjectFile(actProjectAddFiles.Dialog.Files[i], nil);
|
||||
CreateProjectFile(FGlobalProject, actProjectAddFiles.Dialog.Files[i], nil);
|
||||
frmKeymanDeveloper.ShowProject;
|
||||
end;
|
||||
|
||||
|
|
@ -480,8 +481,8 @@ begin
|
|||
begin
|
||||
FGlobalProject.Save;
|
||||
ProjectForm.Free;
|
||||
FreeAndNil(FGlobalProject);
|
||||
FGlobalProject := TProjectUI.Create(''); // I4687
|
||||
FreeGlobalProjectUI;
|
||||
LoadGlobalProjectUI('');
|
||||
ShowProject;
|
||||
end;
|
||||
end;
|
||||
|
|
@ -500,8 +501,8 @@ begin
|
|||
end;
|
||||
|
||||
FGlobalProject.Save;
|
||||
FreeAndNil(FGlobalProject);
|
||||
FGlobalProject := TProjectUI.Create(FileName); // I4687
|
||||
FreeGlobalProjectUI;
|
||||
LoadGlobalProjectUI(FileName); // I4687
|
||||
frmKeymanDeveloper.ProjectMRU.Add(FGlobalProject.FileName);
|
||||
frmKeymanDeveloper.ShowProject;
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -193,11 +193,10 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
TabWidth = 60
|
||||
OnChange = pagesChange
|
||||
OnChanging = pagesChanging
|
||||
ExplicitWidth = 681
|
||||
ExplicitHeight = 447
|
||||
object pageFiles: TTabSheet
|
||||
Caption = 'Files'
|
||||
ImageIndex = 3
|
||||
ExplicitLeft = 0
|
||||
ExplicitWidth = 588
|
||||
ExplicitHeight = 447
|
||||
object Panel1: TPanel
|
||||
|
|
@ -280,7 +279,6 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
ItemHeight = 13
|
||||
TabOrder = 0
|
||||
OnClick = lbFilesClick
|
||||
ExplicitHeight = 329
|
||||
end
|
||||
object cmdAddFile: TButton
|
||||
Left = 14
|
||||
|
|
@ -364,6 +362,7 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
object pageKeyboards: TTabSheet
|
||||
Caption = 'Keyboards'
|
||||
ImageIndex = 5
|
||||
ExplicitLeft = 0
|
||||
ExplicitWidth = 588
|
||||
ExplicitHeight = 447
|
||||
object Panel5: TPanel
|
||||
|
|
@ -469,7 +468,6 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
ItemHeight = 13
|
||||
TabOrder = 0
|
||||
OnClick = lbKeyboardsClick
|
||||
ExplicitHeight = 233
|
||||
end
|
||||
object editKeyboardDescription: TEdit
|
||||
Left = 96
|
||||
|
|
@ -593,6 +591,7 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
object pageDetails: TTabSheet
|
||||
Caption = 'Details'
|
||||
ImageIndex = 2
|
||||
ExplicitLeft = 0
|
||||
ExplicitWidth = 588
|
||||
ExplicitHeight = 447
|
||||
object Panel2: TPanel
|
||||
|
|
@ -732,7 +731,6 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
Anchors = [akLeft, akTop, akRight]
|
||||
TabOrder = 0
|
||||
OnClick = cbReadMeClick
|
||||
ExplicitWidth = 215
|
||||
end
|
||||
object editInfoName: TEdit
|
||||
Left = 108
|
||||
|
|
@ -815,7 +813,6 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
Anchors = [akLeft, akTop, akRight]
|
||||
TabOrder = 9
|
||||
OnClick = cbKMPImageFileClick
|
||||
ExplicitWidth = 215
|
||||
end
|
||||
object panKMPImageSample: TPanel
|
||||
Left = 694
|
||||
|
|
@ -847,6 +844,7 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
object pageShortcuts: TTabSheet
|
||||
Caption = 'Shortcuts'
|
||||
ImageIndex = 8
|
||||
ExplicitLeft = 0
|
||||
ExplicitWidth = 588
|
||||
ExplicitHeight = 447
|
||||
object Panel3: TPanel
|
||||
|
|
@ -963,7 +961,6 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
ItemHeight = 13
|
||||
TabOrder = 3
|
||||
OnClick = lbStartMenuEntriesClick
|
||||
ExplicitHeight = 254
|
||||
end
|
||||
object cmdNewStartMenuEntry: TButton
|
||||
Left = 14
|
||||
|
|
@ -1017,12 +1014,14 @@ inherited frmPackageEditor: TfrmPackageEditor
|
|||
object pageSource: TTabSheet
|
||||
Caption = 'Source'
|
||||
ImageIndex = 9
|
||||
ExplicitLeft = 0
|
||||
ExplicitWidth = 588
|
||||
ExplicitHeight = 447
|
||||
end
|
||||
object pageCompile: TTabSheet
|
||||
Caption = 'Compile'
|
||||
ImageIndex = 1
|
||||
ExplicitLeft = 0
|
||||
ExplicitWidth = 588
|
||||
ExplicitHeight = 447
|
||||
object Panel4: TPanel
|
||||
|
|
|
|||
|
|
@ -1004,7 +1004,7 @@ begin
|
|||
end
|
||||
else
|
||||
begin
|
||||
f := CreateProjectFile(s, ProjectFile);
|
||||
f := CreateProjectFile(FGlobalProject, s, ProjectFile);
|
||||
try
|
||||
fui := f.UI as TProjectFileUI;
|
||||
fui.DefaultEvent(Self);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
unit Keyman.Developer.System.HttpServer.Base;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
IdContext,
|
||||
IdCustomHTTPServer,
|
||||
IdHTTPServer;
|
||||
|
||||
type
|
||||
TBaseHttpResponder = class
|
||||
protected
|
||||
procedure RespondFile(const AFileName: string; AContext: TIdContext;
|
||||
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
|
||||
procedure Respond404(AContext: TIdContext;
|
||||
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
IdGlobalProtocols,
|
||||
|
||||
System.SysUtils;
|
||||
|
||||
{ TBaseHttpResponder }
|
||||
|
||||
procedure TBaseHttpResponder.Respond404(
|
||||
AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
|
||||
AResponseInfo: TIdHTTPResponseInfo);
|
||||
begin
|
||||
AResponseInfo.ResponseNo := 404;
|
||||
AResponseInfo.ResponseText := 'File not found';
|
||||
end;
|
||||
|
||||
procedure TBaseHttpResponder.RespondFile(const AFileName: string;
|
||||
AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
|
||||
AResponseInfo: TIdHTTPResponseInfo);
|
||||
begin
|
||||
// Serve the file
|
||||
|
||||
if not FileExists(AFileName) then
|
||||
begin
|
||||
AResponseInfo.ResponseNo := 404;
|
||||
AResponseInfo.ResponseText := 'File not found';
|
||||
Exit;
|
||||
end;
|
||||
|
||||
AResponseInfo.ContentType := AResponseInfo.HTTPServer.MIMETable.GetFileMIMEType(AFileName);
|
||||
AResponseInfo.CharSet := 'UTF-8';
|
||||
AResponseInfo.ContentLength := FileSizeByName(AFileName);
|
||||
//AResponseInfo.LastModified := GetFileDate(doc);
|
||||
AResponseInfo.WriteHeader;
|
||||
|
||||
AContext.Connection.IOHandler.WriteFile(AFileName);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -12,6 +12,8 @@ uses
|
|||
IdCustomHTTPServer,
|
||||
IdHTTPServer,
|
||||
|
||||
Keyman.Developer.System.HttpServer.Base,
|
||||
|
||||
KeyboardFonts;
|
||||
|
||||
type
|
||||
|
|
@ -52,7 +54,7 @@ type
|
|||
property Name: string read FName;
|
||||
end;
|
||||
|
||||
TDebuggerHttpServer = class
|
||||
TDebuggerHttpResponder = class(TBaseHttpResponder)
|
||||
private
|
||||
FKeyboardsCS, FPackagesCS: TCriticalSection; // I4036
|
||||
FKeyboards: TObjectDictionary<string,TWebDebugKeyboardInfo>; // I4063
|
||||
|
|
@ -105,7 +107,7 @@ end;
|
|||
|
||||
{ TDebuggerHttpServer }
|
||||
|
||||
constructor TDebuggerHttpServer.Create;
|
||||
constructor TDebuggerHttpResponder.Create;
|
||||
begin
|
||||
FKeyboardsCS := TCriticalSection.Create; // I4036
|
||||
FKeyboards := TObjectDictionary<string,TWebDebugKeyboardInfo>.Create; // I4063
|
||||
|
|
@ -114,7 +116,7 @@ begin
|
|||
FPackages := TObjectDictionary<string,TWebDebugPackageInfo>.Create; // I4063
|
||||
end;
|
||||
|
||||
destructor TDebuggerHttpServer.Destroy;
|
||||
destructor TDebuggerHttpResponder.Destroy;
|
||||
begin
|
||||
FreeAndNil(FKeyboards);
|
||||
FreeAndNil(FKeyboardsCS); // I4036
|
||||
|
|
@ -125,7 +127,7 @@ begin
|
|||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TDebuggerHttpServer.GetKeyboardStoredFileName(
|
||||
function TDebuggerHttpResponder.GetKeyboardStoredFileName(
|
||||
const WebFilename: string): string;
|
||||
begin
|
||||
FKeyboardsCS.Enter; // I4036
|
||||
|
|
@ -137,7 +139,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function TDebuggerHttpServer.GetPackageStoredFileName(
|
||||
function TDebuggerHttpResponder.GetPackageStoredFileName(
|
||||
const WebFilename: string): string;
|
||||
begin
|
||||
FPackagesCS.Enter; // I4036
|
||||
|
|
@ -149,7 +151,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TDebuggerHttpServer.ProcessRequest(AContext: TIdContext;
|
||||
procedure TDebuggerHttpResponder.ProcessRequest(AContext: TIdContext;
|
||||
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
|
||||
|
||||
procedure Respond404;
|
||||
|
|
@ -491,18 +493,14 @@ var
|
|||
FFileRegExp: TRegExpr;
|
||||
FResourceFileRegExp: TRegExpr;
|
||||
begin
|
||||
// /keyboard/###.js -> looks up the list of currently testing keyboards
|
||||
// everything else retrieved from xml/kmw/
|
||||
doc := ARequestInfo.Document;
|
||||
Delete(doc, 1, 1);
|
||||
|
||||
if doc = '' then
|
||||
doc := 'index.html';
|
||||
|
||||
if Copy(doc, 1, 8) = 'project/' then
|
||||
begin
|
||||
RespondProject;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if doc = 'inc/keyboards.js' then
|
||||
begin
|
||||
RespondKeyboardsJS;
|
||||
|
|
@ -639,7 +637,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TDebuggerHttpServer.RegisterKeyboard(const Filename, Version: string; FontInfo: TKeyboardFontArray); // I4063 // I4409
|
||||
procedure TDebuggerHttpResponder.RegisterKeyboard(const Filename, Version: string; FontInfo: TKeyboardFontArray); // I4063 // I4409
|
||||
var
|
||||
k: TWebDebugKeyboardInfo;
|
||||
begin
|
||||
|
|
@ -652,7 +650,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TDebuggerHttpServer.RegisterPackage(const Filename, Name: string);
|
||||
procedure TDebuggerHttpResponder.RegisterPackage(const Filename, Name: string);
|
||||
var
|
||||
p: TWebDebugPackageInfo;
|
||||
begin
|
||||
|
|
@ -665,7 +663,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TDebuggerHttpServer.UnregisterKeyboard(const Filename: string);
|
||||
procedure TDebuggerHttpResponder.UnregisterKeyboard(const Filename: string);
|
||||
begin
|
||||
FKeyboardsCS.Enter; // I4036
|
||||
try
|
||||
|
|
@ -675,7 +673,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TDebuggerHttpServer.UnregisterPackage(const Filename: string);
|
||||
procedure TDebuggerHttpResponder.UnregisterPackage(const Filename: string);
|
||||
begin
|
||||
FPackagesCS.Enter; // I4036
|
||||
try
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ inherited frmKeymanDeveloper: TfrmKeymanDeveloper
|
|||
Left = 304
|
||||
Top = 236
|
||||
Bitmap = {
|
||||
494C01010A000E00F00010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||
494C01010A000E00F40010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600
|
||||
0000000000003600000028000000400000003000000001002000000000000030
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
|
|
@ -710,7 +710,7 @@ inherited frmKeymanDeveloper: TfrmKeymanDeveloper
|
|||
Left = 236
|
||||
Top = 236
|
||||
Bitmap = {
|
||||
494C01013B004000F00010001000C0C0C000FF10FFFFFFFFFFFFFFFF424D3600
|
||||
494C01013B004000F40010001000C0C0C000FF10FFFFFFFFFFFFFFFF424D3600
|
||||
000000000000360000002800000040000000F0000000010020000000000000F0
|
||||
000000000000000000000000000000000000C0C0C000C0C0C000C0C0C000C0C0
|
||||
C000C0C0C000C6C6C600F7730000CE5A0000CE5A0000F7730000C6C6C600C0C0
|
||||
|
|
|
|||
|
|
@ -412,6 +412,7 @@ uses
|
|||
OnlineUpdateCheck,
|
||||
GlobalProxySettings,
|
||||
ProjectFileUI,
|
||||
ProjectUI,
|
||||
TextFileFormat,
|
||||
RedistFiles,
|
||||
ErrorControlledRegistry,
|
||||
|
|
@ -504,8 +505,8 @@ begin
|
|||
|
||||
if (FActiveProject <> '') and not FileExists(FActiveProject) then
|
||||
FActiveProject := '';
|
||||
TProjectUI.Create(FActiveProject, True); // I4687
|
||||
|
||||
LoadGlobalProjectUI(FActiveProject, True);
|
||||
|
||||
InitDock;
|
||||
|
||||
|
|
@ -627,7 +628,7 @@ begin
|
|||
FreeAndNil(FCharMapSettings);
|
||||
Application.OnActivate := nil;
|
||||
|
||||
FreeAndNil(FGlobalProject);
|
||||
FreeGlobalProjectUI;
|
||||
FreeAndNil(FChildWindows);
|
||||
FreeAndNil(FProjectMRU);
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ type
|
|||
property State: TProjectState read FState;
|
||||
|
||||
public
|
||||
procedure Log(AState: TProjectLogState; Filename, Msg: string); virtual; abstract; // I4706
|
||||
procedure Log(AState: TProjectLogState; Filename, Msg: string); virtual;
|
||||
|
||||
constructor Create(AFileName: string; ALoadPersistedUntitledProject: Boolean = False); virtual;
|
||||
destructor Destroy; override;
|
||||
|
|
@ -139,6 +139,8 @@ type
|
|||
|
||||
procedure PersistUntitledProject;
|
||||
|
||||
function Render: WideString;
|
||||
|
||||
function Load: Boolean; virtual; // I4694
|
||||
function Save: Boolean; virtual; // I4694
|
||||
|
||||
|
|
@ -317,6 +319,7 @@ uses
|
|||
UMD5Hash,
|
||||
Unicode,
|
||||
utildir,
|
||||
utilhttp,
|
||||
utilsystem;
|
||||
|
||||
{ TProjectFileList }
|
||||
|
|
@ -671,7 +674,6 @@ var
|
|||
begin
|
||||
FOptions := TProjectOptions.Create; // I4688
|
||||
FState := psCreating;
|
||||
FGlobalProject := Self;
|
||||
inherited Create;
|
||||
FMRU := TMRUList.Create('');
|
||||
FMRU.OnChange := MRUChange;
|
||||
|
|
@ -790,6 +792,11 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TProject.Log(AState: TProjectLogState; Filename, Msg: string);
|
||||
begin
|
||||
// Do nothing
|
||||
end;
|
||||
|
||||
procedure TProject.PersistUntitledProject;
|
||||
var
|
||||
path: string;
|
||||
|
|
@ -811,6 +818,83 @@ begin
|
|||
DoRefresh;
|
||||
end;
|
||||
|
||||
function TProject.Render: WideString;
|
||||
var
|
||||
doc, userdoc, xsl: IXMLDomDocument;
|
||||
FLastDir: string;
|
||||
i: Integer;
|
||||
node: IXMLDOMElement;
|
||||
nodes: IXMLDOMNodeList;
|
||||
begin
|
||||
if not FileExists(SavedFileName) then Save;
|
||||
|
||||
Result := '';
|
||||
FLastDir := GetCurrentDir;
|
||||
SetCurrentDir(StringsTemplatePath);
|
||||
try
|
||||
doc := MSXMLDOMDocumentFactory.CreateDOMDocument;
|
||||
try
|
||||
doc.async := False;
|
||||
doc.load(SavedFileName);
|
||||
|
||||
//
|
||||
// Inject the user settings to the loaded file
|
||||
//
|
||||
|
||||
if FileExists(SavedFileName + '.user') then // I4698
|
||||
begin
|
||||
userdoc := MSXMLDOMDocumentFactory.CreateDOMDocument;
|
||||
try
|
||||
userdoc.async := False;
|
||||
userdoc.load(SavedFileName + '.user');
|
||||
for i := 0 to userdoc.documentElement.childNodes.length - 1 do
|
||||
doc.documentElement.appendChild(userdoc.documentElement.childNodes.item[i].cloneNode(true));
|
||||
finally
|
||||
userdoc := nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
//
|
||||
// Remove existing path references from the saved .user file and append the
|
||||
// correct ones for this computer
|
||||
//
|
||||
|
||||
// TODO: refactor with similar code in ProjectLoader.LoadUser and ProjectSaver.SaveUser
|
||||
nodes := doc.documentElement.getElementsByTagName('templatepath');
|
||||
for i := 0 to nodes.length - 1 do
|
||||
doc.documentElement.removeChild(nodes[i]);
|
||||
|
||||
nodes := doc.documentElement.getElementsByTagName('stringspath');
|
||||
for i := 0 to nodes.length - 1 do
|
||||
doc.documentElement.removeChild(nodes[i]);
|
||||
|
||||
node := doc.createElement('templatepath');
|
||||
node.appendChild(doc.createTextNode(ConvertPathToFileURL(TProject.StandardTemplatePath)));
|
||||
doc.documentElement.appendChild(node);
|
||||
|
||||
node := doc.createElement('stringspath');
|
||||
node.appendChild(doc.createTextNode(ConvertPathToFileURL(TProject.StringsTemplatePath)));
|
||||
doc.documentElement.appendChild(node);
|
||||
// end TODO
|
||||
|
||||
xsl := MSXMLDOMDocumentFactory.CreateDOMDocument;
|
||||
try
|
||||
xsl.async := False;
|
||||
xsl.resolveExternals := True;
|
||||
xsl.validateOnParse := False;
|
||||
xsl.load(StringsTemplatePath + 'project.xsl');
|
||||
Result := doc.transformNode(xsl);
|
||||
finally
|
||||
xsl := nil;
|
||||
end;
|
||||
finally
|
||||
doc := nil;
|
||||
end;
|
||||
finally
|
||||
SetCurrentDir(FLastDir);
|
||||
end;
|
||||
end;
|
||||
|
||||
// I1010: Persist untitled project - end
|
||||
|
||||
function TProject.LoadFromXML(FileName: string): Boolean;
|
||||
|
|
@ -866,7 +950,7 @@ begin
|
|||
|
||||
ReadSection('Files', s);
|
||||
for i := 0 to s.Count - 1 do
|
||||
CreateProjectFile(ExpandMemberFileName(FileName, s[i]), nil);
|
||||
CreateProjectFile(Self, ExpandMemberFileName(FileName, s[i]), nil);
|
||||
|
||||
ReadSection('MRU', s);
|
||||
for i := 0 to s.Count - 1 do
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ type
|
|||
function Add(Item: TProjectFileType): Integer;
|
||||
end;
|
||||
|
||||
function CreateProjectFile(AFileName: string; AParent: TProjectFile): TProjectFile;
|
||||
function CreateProjectFile(AProject: TProject; AFileName: string; AParent: TProjectFile): TProjectFile;
|
||||
procedure RegisterProjectFileType(AExtension: string; AProjectFileClass: TProjectFileClass);
|
||||
|
||||
type
|
||||
|
|
@ -69,14 +69,14 @@ var
|
|||
FRegisteredFileTypes: TProjectFileTypeList = nil;
|
||||
FInit: Boolean = True;
|
||||
|
||||
function CreateProjectFile(AFileName: string; AParent: TProjectFile): TProjectFile;
|
||||
function CreateProjectFile(AProject: TProject; AFileName: string; AParent: TProjectFile): TProjectFile;
|
||||
var
|
||||
Ext: string;
|
||||
ni, i: Integer;
|
||||
begin
|
||||
if not FInit then
|
||||
begin
|
||||
Result := TShellProjectFile.Create(FGlobalProject, AFileName, AParent);
|
||||
Result := TShellProjectFile.Create(AProject, AFileName, AParent);
|
||||
if Assigned(FDoCreateProjectFileUI) then
|
||||
FDoCreateProjectFileUI(Result); // I4687
|
||||
Exit;
|
||||
|
|
@ -86,10 +86,10 @@ begin
|
|||
{ Do not allow top-level files to be added more than once }
|
||||
if not Assigned(AParent) then
|
||||
begin
|
||||
i := FGlobalProject.Files.IndexOfFileNameAndParent(AFileName, nil);
|
||||
i := AProject.Files.IndexOfFileNameAndParent(AFileName, nil);
|
||||
if i >= 0 then
|
||||
begin
|
||||
Result := FGlobalProject.Files[i];
|
||||
Result := AProject.Files[i];
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
|
@ -99,7 +99,7 @@ begin
|
|||
if FRegisteredFileTypes[i].Extension = '*' then ni := i
|
||||
else if FRegisteredFileTypes[i].Extension = Ext then
|
||||
begin
|
||||
Result := FRegisteredFileTypes[i].ProjectFileClass.Create(FGlobalProject, AFileName, AParent);
|
||||
Result := FRegisteredFileTypes[i].ProjectFileClass.Create(AProject, AFileName, AParent);
|
||||
|
||||
if Assigned(AParent) then
|
||||
begin
|
||||
|
|
@ -107,14 +107,14 @@ begin
|
|||
AParent.Project.Files.Add(Result);
|
||||
end
|
||||
else
|
||||
FGlobalProject.Files.Add(Result);
|
||||
AProject.Files.Add(Result);
|
||||
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if ni = -1 then
|
||||
raise Exception.Create('Could not find appropriate TProjectFile for file '''+AFileName+'''.');
|
||||
Result := FRegisteredFileTypes[ni].ProjectFileClass.Create(FGlobalProject, AFileName, AParent);
|
||||
Result := FRegisteredFileTypes[ni].ProjectFileClass.Create(AProject, AFileName, AParent);
|
||||
|
||||
if Assigned(AParent) then
|
||||
begin
|
||||
|
|
@ -122,7 +122,7 @@ begin
|
|||
AParent.Project.Files.Add(Result);
|
||||
end
|
||||
else
|
||||
FGlobalProject.Files.Add(Result);
|
||||
AProject.Files.Add(Result);
|
||||
end;
|
||||
|
||||
procedure RegisterProjectFileType(AExtension: string; AProjectFileClass: TProjectFileClass);
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ begin
|
|||
if not VarIsNull(node.ChildValues['Filepath']) then
|
||||
begin
|
||||
// I1152 - Avoid crashes when .kpj file is invalid
|
||||
pf := CreateProjectFile(ExpandFileNameClean(FFileName, node.ChildValues['Filepath']), nil);
|
||||
pf := CreateProjectFile(FProject, ExpandFileNameClean(FFileName, node.ChildValues['Filepath']), nil);
|
||||
pf.Load(node, True);
|
||||
end;
|
||||
end;
|
||||
|
|
@ -134,7 +134,7 @@ begin
|
|||
begin
|
||||
n := FProject.Files.IndexOfID(node.ChildValues['ParentFileID']);
|
||||
if n < 0 then Continue;
|
||||
pf := CreateProjectFile(ExpandFileNameClean(FFileName, node.ChildValues['Filepath']), FProject.Files[n]);
|
||||
pf := CreateProjectFile(FProject, ExpandFileNameClean(FFileName, node.ChildValues['Filepath']), FProject.Files[n]);
|
||||
pf.Load(node, True);
|
||||
end;
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -24,10 +24,14 @@ uses
|
|||
ProjectFileUI;
|
||||
|
||||
function GetGlobalProjectUI: TProjectUI;
|
||||
function LoadGlobalProjectUI(AFilename: string; ALoadPersistedUntitledProject: Boolean = False): TProjectUI;
|
||||
procedure FreeGlobalProjectUI;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
|
||||
Project;
|
||||
|
||||
function GetGlobalProjectUI: TProjectUI;
|
||||
|
|
@ -35,4 +39,16 @@ begin
|
|||
Result := FGlobalProject as TProjectUI;
|
||||
end;
|
||||
|
||||
procedure FreeGlobalProjectUI;
|
||||
begin
|
||||
FreeAndNil(FGlobalProject);
|
||||
end;
|
||||
|
||||
function LoadGlobalProjectUI(AFilename: string; ALoadPersistedUntitledProject: Boolean = False): TProjectUI;
|
||||
begin
|
||||
Assert(not Assigned(FGlobalProject));
|
||||
Result := TProjectUI.Create(AFilename, ALoadPersistedUntitledProject); // I4687
|
||||
FGlobalProject := Result;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ type
|
|||
procedure RefreshHTML(RefreshState: Boolean);
|
||||
procedure WMUserWebCommand(var Message: TMessage); message WM_USER_WEBCOMMAND;
|
||||
procedure EditFileExternal(FileName: WideString);
|
||||
function DoNavigate(URL: WideString): Boolean;
|
||||
function DoNavigate(URL: string): Boolean;
|
||||
procedure ClearMessages;
|
||||
procedure CreateBrowser;
|
||||
protected
|
||||
|
|
@ -140,10 +140,12 @@ type
|
|||
implementation
|
||||
|
||||
uses
|
||||
System.StrUtils,
|
||||
Winapi.ShellApi,
|
||||
|
||||
Keyman.Developer.System.HelpTopics,
|
||||
|
||||
|
||||
dmActionsMain,
|
||||
KeymanDeveloperOptions,
|
||||
kmnProjectFile,
|
||||
|
|
@ -165,7 +167,7 @@ uses
|
|||
utilexecute,
|
||||
utilxml,
|
||||
mrulist,
|
||||
//UmodWebHttpServer,
|
||||
UmodWebHttpServer,
|
||||
uCEFApplication;
|
||||
|
||||
{$R *.DFM}
|
||||
|
|
@ -257,9 +259,9 @@ begin
|
|||
tmrRefresh.Enabled := True
|
||||
else
|
||||
begin
|
||||
GetGlobalProjectUI.Refreshing := True; // I4687
|
||||
// GetGlobalProjectUI.Refreshing := True; // I4687
|
||||
if RefreshState then SaveCurrentTab;
|
||||
cef.LoadURL(GetGlobalProjectUI.Render);
|
||||
cef.LoadURL(modWebHttpServer.GetLocalhostURL + '/app/project/?path='+URLEncode(GetGlobalProjectUI.FileName));
|
||||
end;
|
||||
RefreshCaption;
|
||||
end;
|
||||
|
|
@ -399,7 +401,7 @@ begin
|
|||
frmMessages.Clear;
|
||||
end;
|
||||
|
||||
function TfrmProject.DoNavigate(URL: WideString): Boolean;
|
||||
function TfrmProject.DoNavigate(URL: string): Boolean;
|
||||
var
|
||||
n: Integer;
|
||||
s, command: WideString;
|
||||
|
|
@ -432,7 +434,7 @@ begin
|
|||
FNextCommand := LowerCase(s);
|
||||
PostMessage(Handle, WM_USER_WebCommand, WC_HELP, 0);
|
||||
end
|
||||
else if Copy(URL, 1, 4) = 'http' then
|
||||
else if not URL.StartsWith(modWebHttpServer.GetLocalhostURL) and (Copy(URL, 1, 4) = 'http') then
|
||||
begin
|
||||
Result := True;
|
||||
FNextCommand := URL;
|
||||
|
|
@ -490,7 +492,7 @@ begin
|
|||
|
||||
if ShowModal = mrOk then
|
||||
begin
|
||||
pf := CreateProjectFile(FileName, nil);
|
||||
pf := CreateProjectFile(FGlobalProject, FileName, nil);
|
||||
end;
|
||||
finally
|
||||
Free;
|
||||
|
|
@ -507,7 +509,7 @@ begin
|
|||
dlgOpenFile.DefaultExt := FDefaultExtension;
|
||||
if dlgOpenFile.Execute then
|
||||
begin
|
||||
CreateProjectFile(dlgOpenFile.FileName, nil);
|
||||
CreateProjectFile(FGlobalProject, dlgOpenFile.FileName, nil);
|
||||
end;
|
||||
end
|
||||
else if (Command = 'editfile') or (Command = 'openfile') then
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ begin
|
|||
if Project.Files[j].Parent = Self then
|
||||
Project.Files.Delete(j);
|
||||
|
||||
CreateProjectFile(value, Self);
|
||||
CreateProjectFile(Project, value, Self);
|
||||
end
|
||||
end
|
||||
else
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ begin
|
|||
pack.LoadXML;
|
||||
for i := 0 to pack.Files.Count - 1 do
|
||||
if Project.Files.IndexOfFileName(pack.Files[i].FileName) < 0 then
|
||||
CreateProjectFile(pack.Files[i].FileName, Self);
|
||||
CreateProjectFile(Project, pack.Files[i].FileName, Self);
|
||||
FHeader_Name := pack.Info.Desc[PackageInfo_Name];
|
||||
FHeader_Copyright := pack.Info.Desc[PackageInfo_Copyright];
|
||||
FHeader_Version := pack.Info.Desc[PackageInfo_Version];
|
||||
|
|
|
|||
|
|
@ -55,16 +55,17 @@ type
|
|||
procedure DataModuleCreate(Sender: TObject);
|
||||
procedure DataModuleDestroy(Sender: TObject);
|
||||
private
|
||||
FApp: TAppHttpServer;
|
||||
FDebugger: TDebuggerHttpServer;
|
||||
function GetApp: TAppHttpServer;
|
||||
function GetDebugger: TDebuggerHttpServer;
|
||||
FApp: TAppHttpResponder;
|
||||
FDebugger: TDebuggerHttpResponder;
|
||||
function GetApp: TAppHttpResponder;
|
||||
function GetDebugger: TDebuggerHttpResponder;
|
||||
public
|
||||
function GetURL: string;
|
||||
function GetLocalhostURL: string;
|
||||
procedure GetURLs(v: TStrings);
|
||||
|
||||
property Debugger: TDebuggerHttpServer read GetDebugger;
|
||||
property App: TAppHttpServer read GetApp;
|
||||
property Debugger: TDebuggerHttpResponder read GetDebugger;
|
||||
property App: TAppHttpResponder read GetApp;
|
||||
end;
|
||||
|
||||
var
|
||||
|
|
@ -128,20 +129,25 @@ begin
|
|||
http.Active := False; // I4036
|
||||
end;
|
||||
|
||||
function TmodWebHttpServer.GetApp: TAppHttpServer;
|
||||
function TmodWebHttpServer.GetApp: TAppHttpResponder;
|
||||
begin
|
||||
if not Assigned(FApp) then
|
||||
FApp := TAppHttpServer.Create;
|
||||
FApp := TAppHttpResponder.Create;
|
||||
Result := FApp;
|
||||
end;
|
||||
|
||||
function TmodWebHttpServer.GetDebugger: TDebuggerHttpServer;
|
||||
function TmodWebHttpServer.GetDebugger: TDebuggerHttpResponder;
|
||||
begin
|
||||
if not Assigned(FDebugger) then
|
||||
FDebugger := TDebuggerHttpServer.Create;
|
||||
FDebugger := TDebuggerHttpResponder.Create;
|
||||
Result := FDebugger;
|
||||
end;
|
||||
|
||||
function TmodWebHttpServer.GetLocalhostURL: string;
|
||||
begin
|
||||
Result := 'http://127.0.0.1:'+IntToStr(http.DefaultPort);
|
||||
end;
|
||||
|
||||
function TmodWebHttpServer.GetURL: string;
|
||||
var
|
||||
str: TStringList;
|
||||
|
|
@ -214,8 +220,6 @@ procedure TmodWebHttpServer.httpCommandGet(AContext: TIdContext;
|
|||
var
|
||||
doc: string;
|
||||
begin
|
||||
// /keyboard/###.js -> looks up the list of currently testing keyboards
|
||||
// everything else retrieved from xml/kmw/
|
||||
doc := ARequestInfo.Document;
|
||||
Delete(doc, 1, 1);
|
||||
|
||||
|
|
@ -228,5 +232,4 @@ begin
|
|||
FDebugger.ProcessRequest(AContext, ARequestInfo, AResponseInfo);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@
|
|||
<div class="page" id="page-3">
|
||||
<div class="help-container" id="uppertext3">
|
||||
<div class="headerimage">
|
||||
<p><img alt="Distribution">
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>header_distrib.png</xsl:attribute>
|
||||
</img></p>
|
||||
<p><img alt="Distribution" src='res/header_distrib.png' /></p>
|
||||
<div class='quicklinks'>
|
||||
<h3>Quick Links</h3>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
<xsl:template name="head">
|
||||
<head>
|
||||
<link rel='stylesheet' type='text/css'><xsl:attribute name='href'><xsl:value-of select='/KeymanDeveloperProject/templatepath' />project.css</xsl:attribute></link>
|
||||
<script><xsl:attribute name='src'><xsl:value-of select='/KeymanDeveloperProject/templatepath' />project.js</xsl:attribute><xsl:text> </xsl:text></script>
|
||||
<link rel='stylesheet' type='text/css' href='res/project.css' />
|
||||
<script src='res/project.js'><xsl:text> </xsl:text></script>
|
||||
</head>
|
||||
|
||||
</xsl:template>
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
<xsl:param name="FileType" />
|
||||
<img>
|
||||
<xsl:attribute name="alt"><xsl:value-of select="$FileType" /></xsl:attribute>
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath' />icon_<xsl:choose>
|
||||
<xsl:attribute name="src">res/icon_<xsl:choose>
|
||||
<xsl:when test='contains(".kmn.kmp.kmx.kps.kvk", $FileType)'><xsl:value-of select="substring($FileType,2)" />.png</xsl:when>
|
||||
<xsl:otherwise>file.gif</xsl:otherwise>
|
||||
</xsl:choose></xsl:attribute>
|
||||
|
|
@ -118,7 +118,7 @@
|
|||
|
||||
<!-- xsl:template name="menu_style">
|
||||
/* Popup menu styles */
|
||||
.menu { position: absolute; display: block; background: white; visibility: hidden; border: solid 1px #ACA899; font: 8pt Tahoma; padding: 0px; background-image: url("<xsl:value-of select='/KeymanDeveloperProject/templatepath'/>menugrey.gif"); background-repeat: repeat-y; background-position: 0 0; }
|
||||
.menu { position: absolute; display: block; background: white; visibility: hidden; border: solid 1px #ACA899; font: 8pt Tahoma; padding: 0px; background-image: url("menugrey.gif"); background-repeat: repeat-y; background-position: 0 0; }
|
||||
k\:menuitem { width: 100%; padding: 4 4 4 28; margin: 1px; }
|
||||
k\:menuitem.hover { color: #000000; background: #FFE3DB; border: solid 1px #DB704F; padding: 3 3 3 27; }
|
||||
k\:menuitem.down { background: #DB704F; }
|
||||
|
|
@ -220,15 +220,11 @@
|
|||
<!-- Filetype Generic Templates -->
|
||||
|
||||
<xsl:template name="filetype__arrow">
|
||||
<td class="ioarrow"><img>
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>icon32_arrow.gif</xsl:attribute>
|
||||
</img></td>
|
||||
<td class="ioarrow"><img src='res/icon32_arrow.gif' /></td>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="filetype__plus">
|
||||
<td class="ioarrow"><img>
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>icon32_plus.gif</xsl:attribute>
|
||||
</img></td>
|
||||
<td class="ioarrow"><img src='res/icon32_plus.gif' /></td>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="filetype">
|
||||
|
|
@ -239,7 +235,7 @@
|
|||
<xsl:attribute name="href">help:reference/file-types/<xsl:value-of select="$type" /></xsl:attribute>
|
||||
<xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
|
||||
<img>
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>icon32_<xsl:value-of select="$type" />.<xsl:value-of select="$icontype" /></xsl:attribute>
|
||||
<xsl:attribute name="src">res/icon32_<xsl:value-of select="$type" />.<xsl:value-of select="$icontype" /></xsl:attribute>
|
||||
</img><br />.<xsl:value-of select="translate($type,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
|
||||
</a></td>
|
||||
</xsl:template>
|
||||
|
|
|
|||
|
|
@ -6,44 +6,56 @@
|
|||
<div class="page" id="page-1">
|
||||
<div class="help-container" id="uppertext1">
|
||||
<div class="headerimage">
|
||||
<p><img alt="Keyboards">
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>header_keyboard.png</xsl:attribute>
|
||||
</img></p>
|
||||
<p>
|
||||
<img alt="Keyboards" src='res/header_keyboard.png' />
|
||||
</p>
|
||||
<div class='quicklinks'>
|
||||
<h3>Quick Links</h3>
|
||||
|
||||
|
||||
<ul>
|
||||
<li><a href="help:guides/develop/tutorial">Keyboard Tutorial</a></li>
|
||||
<li><a href="help:language/">Keyman Keyboard Language Reference</a></li>
|
||||
<li>
|
||||
<a href="help:guides/develop/tutorial">Keyboard Tutorial</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="help:language/">Keyman Keyboard Language Reference</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pagetext">
|
||||
<h2>Creating your first keyboard</h2>
|
||||
|
||||
<p>Click the New Keyboard button to create a keyboard for any device. This will open the Keyboard Editor, where you can
|
||||
create a keyboard visually, or programatically with the Keyman Keyboard Language.</p>
|
||||
|
||||
<p>
|
||||
Click the New Keyboard button to create a keyboard for any device. This will open the Keyboard Editor, where you can
|
||||
create a keyboard visually, or programatically with the Keyman Keyboard Language.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
The <a href="help:context/keyboard-editor#toc-layout-tab">Layout page</a> in the Keyboard Editor lets you quickly create
|
||||
a keyboard using a visual representation of a computer keyboard. You can drag and drop characters from the
|
||||
The <a href="help:context/keyboard-editor#toc-layout-tab">Layout page</a> in the Keyboard Editor lets you quickly create
|
||||
a keyboard using a visual representation of a computer keyboard. You can drag and drop characters from the
|
||||
<a href="help:context/character-map">Character Map</a> to create Unicode keyboard layouts.
|
||||
</li>
|
||||
<li>
|
||||
The <a href="help:context/keyboard-editor#toc-layout-tab">Source tab</a> of the layout page shows the keyboard's design in the
|
||||
The <a href="help:context/keyboard-editor#toc-layout-tab">Source tab</a> of the layout page shows the keyboard's design in the
|
||||
<a href="help:reference">Keyman Keyboard Language</a>. From here, you can enhance keyboards with input management features
|
||||
such as constraints, dead keys, character reordering, and more. Read the <a href="help:guides/develop/tutorial">Tutorial</a>
|
||||
for an introduction to these features.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>It's a good idea to read <a href='https://keyman.com/go/developer/10.0/help-keyboards'>Developing Open Source Keyboards</a> for guidelines
|
||||
on preparing open source keyboards for sharing through the Keyman keyboard repositories. Also see the Distribution tab for more on
|
||||
distributing your completed keyboards.</p>
|
||||
|
||||
|
||||
<p>
|
||||
It's a good idea to read <a href='https://keyman.com/go/developer/10.0/help-keyboards'>Developing Open Source Keyboards</a> for guidelines
|
||||
on preparing open source keyboards for sharing through the Keyman keyboard repositories. Also see the Distribution tab for more on
|
||||
distributing your completed keyboards.
|
||||
</p>
|
||||
|
||||
<table class="io">
|
||||
<tr><th colspan="3">Source Files</th><th> </th><th colspan="2">Outputs</th></tr>
|
||||
<tr>
|
||||
<th colspan="3">Source Files</th>
|
||||
<th> </th>
|
||||
<th colspan="2">Outputs</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<xsl:call-template name="filetype_kmn" />
|
||||
<xsl:call-template name="filetype_kvks" />
|
||||
|
|
@ -107,7 +119,11 @@
|
|||
<xsl:for-each select="KeymanDeveloperProject/Files/File[FileType='.kmn' and not(ParentFileID)]">
|
||||
<xsl:variable name="FileState" select="/KeymanDeveloperProject/FileStates/FileState[ID=current()/ID]" />
|
||||
<xsl:call-template name="file">
|
||||
<xsl:with-param name="file_description"><xsl:if test="string-length(Details/Name) > 0">(<xsl:value-of select="Details/Name" />)</xsl:if></xsl:with-param>
|
||||
<xsl:with-param name="file_description">
|
||||
<xsl:if test="string-length(Details/Name) > 0">
|
||||
(<xsl:value-of select="Details/Name" />)
|
||||
</xsl:if>
|
||||
</xsl:with-param>
|
||||
<xsl:with-param name="file_has_details">
|
||||
<xsl:if test="$FileState/FullPath or (Details/Copyright) or (Details/Message) or (/KeymanDeveloperProject/Files/File[ParentFileID=current()/ID])">true</xsl:if>
|
||||
</xsl:with-param>
|
||||
|
|
@ -117,68 +133,96 @@
|
|||
</div>
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template mode="options_menu" match="/KeymanDeveloperProject/Files/File[FileType='.kmn']" >
|
||||
<div class="menu">
|
||||
<xsl:attribute name="id">menu_options_<xsl:value-of select="ID"/></xsl:attribute>
|
||||
<xsl:attribute name="id">
|
||||
menu_options_<xsl:value-of select="ID"/>
|
||||
</xsl:attribute>
|
||||
<xsl:call-template name="menuitem">
|
||||
<xsl:with-param name="caption">Open</xsl:with-param>
|
||||
<xsl:with-param name="command">keyman:openfile?id=<xsl:value-of select="ID" /></xsl:with-param>
|
||||
<xsl:with-param name="command">
|
||||
keyman:openfile?id=<xsl:value-of select="ID" />
|
||||
</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="menuitem">
|
||||
<xsl:with-param name="caption">Build</xsl:with-param>
|
||||
<xsl:with-param name="command">keyman:compilefile?id=<xsl:value-of select="ID" /></xsl:with-param>
|
||||
<xsl:with-param name="command">
|
||||
keyman:compilefile?id=<xsl:value-of select="ID" />
|
||||
</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="menuitem">
|
||||
<xsl:with-param name="caption">Clean</xsl:with-param>
|
||||
<xsl:with-param name="command">keyman:cleanfile?id=<xsl:value-of select="ID" /></xsl:with-param>
|
||||
<xsl:with-param name="command">
|
||||
keyman:cleanfile?id=<xsl:value-of select="ID" />
|
||||
</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="menuitem">
|
||||
<xsl:with-param name="caption">View Source</xsl:with-param>
|
||||
<xsl:with-param name="command">keyman:viewfilesource?id=<xsl:value-of select="ID" /></xsl:with-param>
|
||||
<xsl:with-param name="command">
|
||||
keyman:viewfilesource?id=<xsl:value-of select="ID" />
|
||||
</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="menuitem">
|
||||
<xsl:with-param name="caption">Open in External Editor</xsl:with-param>
|
||||
<xsl:with-param name="command">keyman:editfileexternal?id=<xsl:value-of select="ID" /></xsl:with-param>
|
||||
<xsl:with-param name="command">
|
||||
keyman:editfileexternal?id=<xsl:value-of select="ID" />
|
||||
</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="menuitem">
|
||||
<xsl:with-param name="caption">Open Containing Folder</xsl:with-param>
|
||||
<xsl:with-param name="command">keyman:opencontainingfolder?id=<xsl:value-of select="ID" /></xsl:with-param>
|
||||
<xsl:with-param name="command">
|
||||
keyman:opencontainingfolder?id=<xsl:value-of select="ID" />
|
||||
</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
<xsl:call-template name="menuitem">
|
||||
<xsl:with-param name="caption">Remove from Project</xsl:with-param>
|
||||
<xsl:with-param name="command">keyman:removefile?id=<xsl:value-of select="ID" /></xsl:with-param>
|
||||
<xsl:with-param name="command">
|
||||
keyman:removefile?id=<xsl:value-of select="ID" />
|
||||
</xsl:with-param>
|
||||
</xsl:call-template>
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template mode="filedetails" match="/KeymanDeveloperProject/Files/File[FileType='.kmn']">
|
||||
<xsl:variable name="FileState" select="/KeymanDeveloperProject/FileStates/FileState[ID=current()/ID]" />
|
||||
<table class="filedetailtext">
|
||||
<xsl:if test="$FileState/FullPath">
|
||||
<tr>
|
||||
<th>Full path:</th><td><xsl:value-of select="$FileState/FullPath"/></td>
|
||||
<th>Full path:</th>
|
||||
<td>
|
||||
<xsl:value-of select="$FileState/FullPath"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates mode="keyboardbitmap" select="/KeymanDeveloperProject/Files/File[ParentFileID=current()/ID]" />
|
||||
<xsl:if test="Details/Copyright">
|
||||
<tr>
|
||||
<th>Copyright:</th><td><xsl:value-of select="Details/Copyright"/></td>
|
||||
<th>Copyright:</th>
|
||||
<td>
|
||||
<xsl:value-of select="Details/Copyright"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:if>
|
||||
<xsl:if test="FileVersion">
|
||||
<tr>
|
||||
<th>Version:</th><td><xsl:value-of select="FileVersion"/></td>
|
||||
<th>Version:</th>
|
||||
<td>
|
||||
<xsl:value-of select="FileVersion"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:if>
|
||||
<xsl:if test="Details/Message">
|
||||
<tr>
|
||||
<th>Message:</th><td><xsl:value-of select="Details/Message"/></td>
|
||||
<th>Message:</th>
|
||||
<td>
|
||||
<xsl:value-of select="Details/Message"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:if>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
<xsl:template mode="keyboardbitmap" match="/KeymanDeveloperProject/Files/File">
|
||||
<xsl:variable name="FileState" select="/KeymanDeveloperProject/FileStates/FileState[ID=current()/ID]" />
|
||||
<tr>
|
||||
|
|
@ -186,16 +230,22 @@
|
|||
<td>
|
||||
<img style="vertical-align: middle; width: 16px; height: 16px;">
|
||||
<xsl:attribute name="src">
|
||||
<xsl:value-of select="$FileState/FullPath"/>
|
||||
<xsl:value-of select="'ico?path='+$FileState/FullPath"/>
|
||||
</xsl:attribute>
|
||||
</img>
|
||||
<xsl:text> </xsl:text><span tabindex="1">
|
||||
<xsl:attribute name="id">kbdbitmap<xsl:value-of select="ID"/></xsl:attribute>
|
||||
<xsl:text> </xsl:text>
|
||||
<span tabindex="1">
|
||||
<xsl:attribute name="id">
|
||||
kbdbitmap<xsl:value-of select="ID"/>
|
||||
</xsl:attribute>
|
||||
<a>
|
||||
<xsl:attribute name="href">keyman:editfile?id=<xsl:value-of select="ID"/></xsl:attribute>
|
||||
<xsl:attribute name="href">
|
||||
keyman:editfile?id=<xsl:value-of select="ID"/>
|
||||
</xsl:attribute>
|
||||
<xsl:value-of select="Filename" />
|
||||
</a>
|
||||
</span><br />
|
||||
</span>
|
||||
<br />
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:template>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@
|
|||
<div class="page" id="page-2">
|
||||
<div class="help-container" id="uppertext2">
|
||||
<div class="headerimage">
|
||||
<p><img>
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>header_package.png</xsl:attribute>
|
||||
</img></p>
|
||||
<p><img src='res/header_package.png' /></p>
|
||||
<div class='quicklinks'>
|
||||
<h3>Quick Links</h3>
|
||||
|
||||
|
|
|
|||
|
|
@ -27,30 +27,22 @@
|
|||
<div class="tabbspacer"></div>
|
||||
|
||||
<div id="tabb0" class="tabb" onmousedown="javascript:selecttabb(0);">
|
||||
<img alt="Welcome">
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>tab_welcome.gif</xsl:attribute>
|
||||
</img>
|
||||
<img alt="Welcome" src='res/tab_welcome.gif' />
|
||||
Welcome
|
||||
</div>
|
||||
|
||||
<div id="tabb1" class="tabb" onmousedown="javascript:selecttabb(1);">
|
||||
<img alt="Keyboards">
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>tab_keyboard.png</xsl:attribute>
|
||||
</img>
|
||||
<img alt="Keyboards" src='res/tab_keyboard.png' />
|
||||
Keyboards
|
||||
</div>
|
||||
|
||||
<div id="tabb2" class="tabb" onmousedown="javascript:selecttabb(2);">
|
||||
<img alt="Packaging">
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>tab_package.png</xsl:attribute>
|
||||
</img>
|
||||
<img alt="Packaging" src='res/tab_package.png' />
|
||||
Packaging
|
||||
</div>
|
||||
|
||||
<div id="tabb3" class="tabb" onmousedown="javascript:selecttabb(3);">
|
||||
<img alt="Distribution">
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>tab_distrib.png</xsl:attribute>
|
||||
</img>
|
||||
<img alt="Distribution" src='res/tab_distrib.png' />
|
||||
Distribution
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@
|
|||
<div class="page" id="page-0">
|
||||
<div class="help-container" id="uppertext0" >
|
||||
<div class="headerimage">
|
||||
<p><img alt="Welcome">
|
||||
<xsl:attribute name="src"><xsl:value-of select='/KeymanDeveloperProject/templatepath'/>header_welcome.gif</xsl:attribute>
|
||||
</img></p>
|
||||
<p><img alt="Welcome" src='res/header_welcome.gif' /></p>
|
||||
<div class='quicklinks'>
|
||||
<h3>Quick Links</h3>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue