diff --git a/icons/other/package_add.png b/icons/other/package_add.png
new file mode 100644
index 00000000..9c8a9da4
Binary files /dev/null and b/icons/other/package_add.png differ
diff --git a/icons/other/package_delete.png b/icons/other/package_delete.png
new file mode 100644
index 00000000..86f7fbc2
Binary files /dev/null and b/icons/other/package_delete.png differ
diff --git a/lazproj/coedit.lpi b/lazproj/coedit.lpi
index 5357d7f8..e841c4ac 100644
--- a/lazproj/coedit.lpi
+++ b/lazproj/coedit.lpi
@@ -140,7 +140,7 @@
-
+
@@ -345,6 +345,14 @@
+
+
+
+
+
+
+
+
diff --git a/resman/README.md b/resman/README.md
new file mode 100644
index 00000000..7eeb099b
--- /dev/null
+++ b/resman/README.md
@@ -0,0 +1,8 @@
+Coedit resman
+=============
+
+The interactive Coedit tool used to manage the resource of a project.
+This D program is based on the tool called resource.d
+
+Tool: the source code of the program used by Coedit.
+Library: the source code if the static library to import in a module using the resman.
\ No newline at end of file
diff --git a/resman/build/tool.coedit b/resman/build/tool.coedit
new file mode 100644
index 00000000..229b0ee3
--- /dev/null
+++ b/resman/build/tool.coedit
@@ -0,0 +1,23 @@
+object CurrentProject: TCEProject
+ OptionsCollection = <
+ item
+ name = 'default'
+ messagesOptions.showColumnsNumber = True
+ outputOptions.boundsCheck = onAlways
+ pathsOptions.outputFilename = 'C:\dev\sharedbin\resman.exe'
+ preBuildProcess.options = []
+ preBuildProcess.showWindow = swoNone
+ postBuildProcess.options = []
+ postBuildProcess.showWindow = swoNone
+ runOptions.options = []
+ runOptions.parameters.Strings = (
+ '-v '
+ )
+ runOptions.showWindow = swoNone
+ end>
+ Sources.Strings = (
+ '..\tool\resman.d'
+ '..\tool\utils.d'
+ )
+ ConfigurationIndex = 0
+end
diff --git a/resman/library/resman.d b/resman/library/resman.d
new file mode 100644
index 00000000..ba35a554
--- /dev/null
+++ b/resman/library/resman.d
@@ -0,0 +1,46 @@
+/**
+ * Coedit resource API
+ */
+module resman;
+
+/**
+ * Activate the resman mechanism. If the resources related to a
+ * module have been generated using the Coedit *Resman* widget
+ * then mixing this template will:
+ * - import this module, thus the method to access the to resources
+ * - import, at compile time, the resource data, located in the .res file
+ * Examples:
+ * ---
+ * mixin activateResman;
+ * ---
+ */
+mixin template activateResman()
+{
+ mixin("private import resman;");
+ enum f = (__FILE__.stripExtension.stripPath) ~ ".res";
+ mixin(import(f));
+}
+
+public enum ResFormat {
+ bytes, //
+ utf8, //
+ base16, //
+ base64 //
+}
+
+/**
+ *
+ * Params:
+ * identifiers = the array which holds the resource identifiers,
+ * always named *residententifiers*.
+ * identifier = the identifier to find.
+ *
+ * Return:
+ * a positive value if the resource is found otherwise -1.
+ */
+public ptrdiff_t resourceIndex(string[] identifiers, string identifier)
+{
+ return -1;
+}
+
+
diff --git a/resman/tool/resman.d b/resman/tool/resman.d
new file mode 100644
index 00000000..09268262
--- /dev/null
+++ b/resman/tool/resman.d
@@ -0,0 +1,142 @@
+module resman;
+
+import std.stdio, std.getopt, std.path;
+import std.json, std.file, std.conv;
+
+enum ResType {aFile, aFolder}
+enum ResFormat {bytes, utf8, base16, base64}
+
+struct ResourceItem{}
+alias ResourceItems = ResourceItem * [];
+
+void main(string[] args)
+{
+ string[] files;
+ string basePath;
+ bool verbose;
+ ResourceItems items;
+
+ getopt(args, config.passThrough, "v|verbose", &verbose);
+ getopt(args, config.passThrough, "b|basepath", &basePath);
+ files = args[1..$];
+
+ if (basePath.length && basePath.exists)
+ {/*setCUrrentDirectory(basePath) : use to solve relative resource path\name*/}
+
+ if (!files.length) return; // + verbose, msg
+ foreach(f; files)
+ {
+ json2Items(f, items);
+ Items2Module(f, items);
+ }
+ readln;
+}
+
+void json2Items(string fname, out ResourceItems items)
+{
+ if (!fname.exists) return; // + verbose, msg
+ size_t size = cast(size_t) getSize(fname);
+ if (size == 0) return; // + verbose, msg
+
+ auto json_string = cast(string) std.file.read(fname, size);
+ JSONValue root = parseJSON(json_string);
+ if (root.type != JSON_TYPE.OBJECT) return; // invalid format
+ JSONValue * itms = ("items" in root.object);
+ if (itms == null) return; // invalid format
+ if (itms.type != JSON_TYPE.ARRAY) return; // invalid format
+ foreach(itm; itms.array)
+ {
+ if (itm.type != JSON_TYPE.OBJECT) continue; // invalid format
+
+ JSONValue * itm_tpe = ("resourceType" in itm.object);
+ JSONValue * itm_nme = ("name" in itm.object);
+ JSONValue * itm_idt = ("identifier" in itm.object);
+ JSONValue * itm_fmt = ("format" in itm.object);
+ JSONValue * itm_mdt = ("metadata" in itm.object);
+
+ if (itm_tpe == null) continue; // invalid format
+ if (itm_nme == null) continue; // invalid format
+ if (itm_idt == null) continue; // invalid format
+ if (itm_fmt == null) continue; // invalid format
+ if (itm_mdt == null) continue; // invalid format
+
+ if (itm_tpe.type != JSON_TYPE.STRING) continue; // invalid format
+ if (itm_nme.type != JSON_TYPE.STRING) continue; // invalid format
+ if (itm_idt.type != JSON_TYPE.STRING) continue; // invalid format
+ if (itm_fmt.type != JSON_TYPE.STRING) continue; // invalid format
+ if (itm_mdt.type != JSON_TYPE.STRING) continue; // invalid format
+
+ string[] nme_vs;
+ string nme_v = itm_nme.str;
+ string idt_v = itm_idt.str;
+ string mdt_v = itm_mdt.str;
+ ResType tpe_v = to!ResType(itm_tpe.str);
+ ResFormat fmt_v = to!ResFormat(itm_fmt.str);
+
+ if (!nme_v.exists) continue; // path or filename must exists
+
+ if (nme_v.isDir)
+ foreach(e; dirEntries(nme_v, SpanMode.shallow))
+ nme_vs ~= e;
+ else nme_vs ~= nme_v;
+
+ foreach(n; nme_vs)
+ {
+ // creates item for the file n
+ }
+
+ }
+
+
+
+
+
+
+
+ void json_print(ref JSONValue value)
+ {
+
+ writeln("-------------");
+ JSON_TYPE tp = value.type;
+ final switch(tp){
+ case JSON_TYPE.ARRAY:
+ foreach(v; value.array)
+ json_print(v);
+ break;
+ case JSON_TYPE.FALSE:
+ writeln(value);
+ break;
+ case JSON_TYPE.FLOAT:
+ writeln(value);
+ break;
+ case JSON_TYPE.INTEGER:
+ writeln(value);
+ break;
+ case JSON_TYPE.NULL:
+ break;
+ case JSON_TYPE.OBJECT:
+ writeln(value);
+ writeln(("identifier" in value.object) != null);
+ foreach(v; value.object)
+ json_print(v);
+ break;
+ case JSON_TYPE.STRING:
+ writeln(value);
+ break;
+ case JSON_TYPE.TRUE:
+ writeln(value);
+ break;
+ case JSON_TYPE.UINTEGER:
+ writeln(value);
+ break;
+ }
+ }
+
+ json_print(root);
+}
+
+void Items2Module(string fname, ref ResourceItems items)
+{
+}
+
+
diff --git a/resman/tool/resman.resman b/resman/tool/resman.resman
new file mode 100644
index 00000000..38284265
--- /dev/null
+++ b/resman/tool/resman.resman
@@ -0,0 +1 @@
+{ "Name" : "", "Tag" : 0, "items" : [{ "format" : "bytes", "identifier" : "img1", "metadata" : "bla|oops", "name" : "C:\\Dev\\dproj\\Resource.d\\res\\res_img1.png", "resourceType" : "aFile" }, { "format" : "bytes", "identifier" : "", "metadata" : "", "name" : "", "resourceType" : "aFile" }] }
\ No newline at end of file
diff --git a/resman/tool/utils.d b/resman/tool/utils.d
new file mode 100644
index 00000000..4d05c1e4
--- /dev/null
+++ b/resman/tool/utils.d
@@ -0,0 +1 @@
+module utils;
diff --git a/src/ce_icons.inc b/src/ce_icons.inc
index 450aca6d..451c123d 100644
--- a/src/ce_icons.inc
+++ b/src/ce_icons.inc
@@ -1101,6 +1101,83 @@ LazarusResources.Add('link_break','PNG',[
+#142#128#210#246#219#231#227#228'1Mn'#21#249#19#226#13#224#128#234'd'#129#31
+#1#6#0'aG}'#171'6'#210#147','#0#0#0#0'IEND'#174'B`'#130
]);
+LazarusResources.Add('package_add','PNG',[
+ #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#16#0#0#0#16#8#6#0#0#0#31#243#255'a'
+ +#0#0#0#4'gAMA'#0#0#175#200'7'#5#138#233#0#0#0#25'tEXtSoftware'#0'Adobe Image'
+ +'Readyq'#201'e<'#0#0#3#21'IDAT8'#203'u'#147'Kh\u'#24'G'#207#157#153';s'''#205
+ +#144'IR'#210'4'#213'('#164'i'#19'c'#210#164#8'J'#31'*'#168#217#8'mJ'#21'"'#8
+ +#245#129'.'#20#221#20#165#20#151#22#20#170#8#6#197#138#136'n'#196#141#20#164
+ +'&V'#163'!M['#163'I'#11#161#182'y'#140'i'#30'M&'#205'$'#157#204#251#206#220
+ +#249#127#159#139#130#11#141#191#245#143#179'9'#28'KU'#217'lS'#231#14#181'!'
+ +#242#174#138'FT'#228#173#214'#'#253'W7'#251'Y'#255#6'L'#247#31#174'S'#209'7U'
+ +#228#245#218#166'#QSX'#227#206#220'`VE?S#'#31'>'#216';'#184#188')`f'#160#199
+ +'V'#213#23'T'#228#237#200#142#238#157#219'Z'#159#193''''#134'l|'#12';'#228'g'
+ +'56'#196#198#173#241'y'#21'9'#173'"_t'#30#27')'#252#3#152#30#232#233'F'#244
+ +#157'Pu'#231#193'm-Gq'#170#155'AK'#148#147'3'#220#28':'#197#189#15#236#199'W'
+ +'q'#31#197'\'#130#219#177'q2'#137#27#163'j'#204#169#189#175#140'~oM'#245#31
+ +#254#18#145'c[w'#246'ZNM;nf'#141'T'#226#6#166#148#197#203#197#9'YE'#234#27'w'
+ +#131#155#196'_'#217#136#207#137#144#142'Os;6N>9'#255'M@'#141'<'#191#235#201
+ +'>'#203#203#172#145#141#15'!'#165'4'#161'|'#130'|:Nzq'#137#189#189#31'c'#135
+ +#183'@n'#14#205#197#176#210#211#212'6tPU'#3'W~'#152'}6'#160'"9'#197#170#10':'
+ +'!"5'#141#20#18#215#169#168#142#18#173#14#19'&'#15#238#2#152' '#184'KPX$'#191
+ +'2Jv'#238#18#206#253'GQ#'#185#128#138#128#0'V'#16#187#170#25';'#20#193']'#29
+ +'C'#178#171#216'~'#176#242#127#129'O)'#173#252'L&'#189'A9'#28#133'`'#8#245
+ +#202#168#8#1'5'#194']'#15#6#180#8#129'-8u]'#148#131'a'#28'w'#29'Y'#249#137
+ +#148#151#161#236'TbB'#17'$_'#130#178'"'#165'2j'#12#1'5'#230#174'P5 '#30'H'#30
+ +#180'H'#160'r'#7#145#250']'#164#238#140#225#249#28'.,'#133#184#188#22'&'#237
+ +#186#148#220#20#7#147#19#180#24'!'#160'"a'#159'm'#3'a('#175#131#20'A\0Ep'#26
+ +'(+'#252#178#16'd'#202#174#229#241'G'#31#226#158#154'f~'#253#243','#231#175
+ +']`r'#195#173#244#169#145#225#217#225#143'($'#23' '#212#0#254#10'0%P'#15#16
+ +#212#19'~\'#240#232'l'#217#131#241#25#246'l'#127#10'cy<'#210#190#143#139'9'
+ +#207#242#137'1Og'#22'G^'#157#28'8'#25#155#255#253'k'#12'A'#136#236#6';'#12#10
+ +#166#232#145#200#172'c['#149#28'j}'#3#128#227'O'#156#161#169#174#3#163#248
+ +#252#159'~7o'#234';_'#186#18#31';'#243'm>1'#227#197''''#206#181'c'#249#157'H'
+ +#227#195'X'#246'VD'#194'|5:D'#245'v'#155#225#217#179#28'h'#234#225#189#243'/'
+ +#146')'#172'smr'#202#253'OL'#127#244'u'#181#169#145#147'*'#242'\'#211'c/['
+ +#133#213'9>'#191'8'#200#245'h'#153'}'#29#7'h'#174#239'bf'#229'*'#151'&F'#184
+ +'u3'#245#129#245#127'9_>'#221#214#173'"'''#16#9#170#200#251#199#151's'#251
+ +#129#215#128#8#144#1'>'#249#173'o'#249#196#223#15'#'#175')S'#227#135'-'#0#0#0
+ +#0'IEND'#174'B`'#130
+]);
+LazarusResources.Add('package_delete','PNG',[
+ #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#16#0#0#0#16#8#6#0#0#0#31#243#255'a'
+ +#0#0#0#4'gAMA'#0#0#175#200'7'#5#138#233#0#0#0#25'tEXtSoftware'#0'Adobe Image'
+ +'Readyq'#201'e<'#0#0#3#13'IDAT8'#203'u'#147'Kh\u'#24#197#127'w'#230#206#220
+ +';I'#198'L'#18'Icj'#211#150'$'#173'5Z'#211#138#136#136#232#162'D'#140'`'#19
+ +'j!'#133'B'#10'E'#4#5#5#193#162'""b'#21#161#136'`ADD\'#185't!5'#165#15#9'!'
+ +#180#132'&'#17#186#232'c:'#137#211#164#233'L2y'#206#243#206#220#251#255'>'#23
+ +#149',4'#158#245#225#199#129's'#142#165#170'l'#165#219#231'^'#235'A'#228's'
+ +#21#141#171#200#251#251#6'G'#254#220#202'g'#253#27#144#28'9'#220#170#162#239
+ +#168#200#219'-'#157#131#9'SYf5}'#185#168#162#223#171#145#175#159#24#186'|'
+ +#127'K'#192#157#243#3#17'U='#161'"'#167#226#219#251#186#182#237'{'#157#144#24
+ +#138#153'I"N'#152#165#212'('#235#247#166#238#170#200#25#21#249#177'wx'#188
+ +#178#9'H'#158#31#232'C'#244'c'#167#169#247#133'm'#143#29#193'm'#234#6#173#17
+ +#172#221#225#175#209#211#236'x'#252'yBu;'#169#150'r,'#166#166'('#228'nN'#168
+ +'1'#167#15#190'1'#241#155'u{'#228#240'O'#136#12'?'#220'5d'#185#205'O'#226#21
+ +#150#217#200#221#196#212#138#248#165#12#142'U'#165#173'c/xk'#132#27':'#8#185
+ +'q'#242#153'$'#139#169')'#202'kw'#127#177#213#200#241'='#135#206'Z~a'#153'bf'
+ +#20#169#229'q'#202'9'#202#249#12#249#249#5#14#14'}K$V'#15#165'4ZJa'#229#147
+ +#180#180#239#167#177#25#166#127#159'=j'#171'HI'#177#26#163#174'C'#188#185#131
+ +'J'#238#6'uM'#9#18'M1b'#148#193#155#3#19#5'o'#1'*'#243#148#179#19#20#211'Wpw'
+ +#29'A'#141#148'l'#21#1#1#172'('#145#198'n"N'#28'oi'#18').'#17#9#131'U'#158
+ +#129#144'R'#203'^'#162#144'_'''#136'% '#234#160'~'#128#138'`'#171#17#30#244
+ +'`@'#171'`'#215#227#182#30' '#136#198'p'#189#21'${'#145#13#191'@'#224'6`'#156
+ +'8R'#174'A'#160'H-@'#141#193'Vc'#30#20#170#6#196#7')'#131'V'#177#27#182#19'o'
+ +#219#195#198#234'$~'#200'ee"'#199#202#181'9'#188#197'uB'#15'Ei}'#142#127#18
+ +#136#196'B'#145#8#16#131'`'#5#164#10#226#129#169#130#219'N'#160#176':'#177'B'
+ +'-'#235#242#244#241#15'qv'#247'P'#185'~'#129#27#227#151#240#131#245#250#144
+ +#26#25#155#29#251#134#202#218#28'8'#237#16#174#3'S'#3#245#1'A}!s%I'#247'KGqg'
+ +'F'#177'~>F'#221#236#175#236#234'h'#166'6'#183#30#216'b'#204#171#133#249#241
+ +#225'['#233#177'S'#205#157#135#186#30#221#223'O8'#190#23#202')'#240'J'#152
+ +#170#143'Y'#171#225#182#237#134#254#247'6''l'#127#250#8'!O'#156#205')O'#255
+ +#240'l'#171#138#188#171'F'#222'j'#127'j '#209#222#219#15#190'G~'#254'*'#211
+ +#159'}'#201'3'#175#12'R?w'#142'j%K'#25'('#228#195#220#154'a'#225'?g'#186'v'
+ +#246'@'#143#26#249'HE'#142'u'#190'x'#210#170','#165#153#191#248#7'v'#222#211
+ +#29'-'#129'e'#135#238'Q'#200#5#164#23#195#198#247#244#19#235#255#238'|'#245
+ +'LO'#159#138'|'#128'HTE'#190#226'z'#181#167#180'z'#255#205#176#177'v'#154#176
+ +'.(|'#247#242#133#224#139#191#1't'#175#185#146#193#18'#X'#0#0#0#0'IEND'#174
+ +'B`'#130
+]);
LazarusResources.Add('page_white_copy','PNG',[
#137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#16#0#0#0#16#8#4#0#0#0#181#250'7'
+#234#0#0#0#4'gAMA'#0#0#175#200'7'#5#138#233#0#0#0#25'tEXtSoftware'#0'Adobe I'
diff --git a/src/ce_main.pas b/src/ce_main.pas
index 3057ec11..6a5b474c 100644
--- a/src/ce_main.pas
+++ b/src/ce_main.pas
@@ -10,7 +10,7 @@ uses
Dialogs, Menus, ActnList, ExtCtrls, process, XMLPropStorage, dynlibs, SynExportHTML,
ce_common, ce_dmdwrap, ce_project, ce_dcd, ce_plugin, ce_synmemo, ce_widget,
ce_messages, ce_interfaces, ce_editor, ce_projinspect, ce_projconf, ce_search,
- ce_staticexplorer, ce_miniexplorer, ce_libman, ce_libmaneditor,
+ ce_staticexplorer, ce_miniexplorer, ce_libman, ce_libmaneditor, ce_resman,
ce_observer, ce_writableComponent, ce_toolseditor, ce_procinput, ce_cdbcmd;
type
@@ -177,6 +177,8 @@ type
fDoc: TCESynMemo;
fUpdateCount: NativeInt;
fProject: TCEProject;
+ fProjMru: TMruFileList;
+ fFileMru: TMruFileList;
fPlugList: TCEPlugDescriptorList;
fWidgList: TCEWidgetList;
fMesgWidg: TCEMessagesWidget;
@@ -188,17 +190,15 @@ type
fExplWidg: TCEMiniExplorerWidget;
fLibMWidg: TCELibManEditorWidget;
fTlsEdWidg: TCEToolsEditorWidget;
- fProjMru: TMruFileList;
- fFileMru: TMruFileList;
fPrInpWidg: TCEProcInputWidget;
- fInitialized: boolean;
- fRunnableSw: string;
+ fResWidg: TCEResmanWidget;
{$IFDEF WIN32}
fCdbWidg: TCECdbWidget;
{$ENDIF}
+ fInitialized: boolean;
+ fRunnableSw: string;
fRunProc: TCheckedAsyncProcess;
-
fLogMessager: TCELogMessageSubject;
fMainMenuSubj: TCEMainMenuSubject;
procedure updateMainMenuProviders;
@@ -239,7 +239,6 @@ type
procedure FreeRunnableProc;
// widget interfaces subroutines
- procedure checkWidgetActions(const aWidget: TCEWidget);
procedure widgetShowFromAction(sender: TObject);
// run & exec sub routines
@@ -462,6 +461,7 @@ begin
fLibMWidg := TCELibManEditorWidget.create(self);
fTlsEdWidg:= TCEToolsEditorWidget.create(self);
fPrInpWidg:= TCEProcInputWidget.create(self);
+ fResWidg := TCEResmanWidget.create(self);
{$IFDEF WIN32}
fCdbWidg := TCECdbWidget.create(self);
@@ -477,6 +477,7 @@ begin
fWidgList.addWidget(@fLibMWidg);
fWidgList.addWidget(@fTlsEdWidg);
fWidgList.addWidget(@fPrInpWidg);
+ fWidgList.addWidget(@fResWidg);
{$IFDEF WIN32}
fWidgList.addWidget(@fCdbWidg);
@@ -860,28 +861,6 @@ begin
end;
end;
-procedure TCEMainForm.checkWidgetActions(const aWidget: TCEWidget);
-var
- tlt: string;
- cnt, i: NativeInt;
- prt, itm: TMenuItem;
-begin
- tlt := aWidget.contextName;
- if tlt = '' then exit;
- cnt := aWidget.contextActionCount;
- if cnt = 0 then exit;
- //
- prt := TMenuItem.Create(self);
- prt.Caption := tlt;
- mainMenu.Items.Add(prt);
- for i := 0 to cnt-1 do
- begin
- itm := TMenuItem.Create(prt);
- itm.Action := aWidget.contextAction(i);
- prt.Add(itm);
- end;
-end;
-
procedure TCEMainForm.mruChange(Sender: TObject);
var
srcLst: TMruFileList;
diff --git a/src/ce_resman.lfm b/src/ce_resman.lfm
new file mode 100644
index 00000000..5bb907f9
--- /dev/null
+++ b/src/ce_resman.lfm
@@ -0,0 +1,103 @@
+inherited CEResmanWidget: TCEResmanWidget
+ Left = 755
+ Height = 331
+ Top = 247
+ Width = 432
+ Caption = 'Resman'
+ ClientHeight = 331
+ ClientWidth = 432
+ inherited Back: TPanel
+ Height = 331
+ Width = 432
+ ClientHeight = 331
+ ClientWidth = 432
+ inherited Content: TPanel
+ Height = 331
+ Width = 432
+ ClientHeight = 331
+ ClientWidth = 432
+ object Panel1: TPanel[0]
+ Left = 4
+ Height = 24
+ Top = 4
+ Width = 424
+ Align = alTop
+ BorderSpacing.Around = 4
+ BevelOuter = bvNone
+ ClientHeight = 24
+ ClientWidth = 424
+ TabOrder = 0
+ object BtnAddItem: TBitBtn
+ Left = 0
+ Height = 24
+ Hint = 'add a resourcel'
+ Top = 0
+ Width = 28
+ Align = alLeft
+ Layout = blGlyphBottom
+ OnClick = BtnAddItemClick
+ Spacing = 0
+ TabOrder = 0
+ end
+ object btnRemItem: TBitBtn
+ Left = 28
+ Height = 24
+ Hint = 'remove selected resource'
+ Top = 0
+ Width = 28
+ Align = alLeft
+ Layout = blGlyphBottom
+ OnClick = btnRemItemClick
+ Spacing = 0
+ TabOrder = 1
+ end
+ end
+ object Panel2: TPanel[1]
+ Left = 4
+ Height = 295
+ Top = 32
+ Width = 424
+ Align = alClient
+ BorderSpacing.Around = 4
+ BevelOuter = bvNone
+ ClientHeight = 295
+ ClientWidth = 424
+ TabOrder = 1
+ object lstItems: TListBox
+ Left = 0
+ Height = 295
+ Top = 0
+ Width = 160
+ Align = alLeft
+ ItemHeight = 0
+ OnSelectionChange = lstItemsSelectionChange
+ TabOrder = 0
+ end
+ object Splitter1: TSplitter
+ Left = 160
+ Height = 295
+ Top = 0
+ Width = 5
+ AutoSnap = False
+ end
+ object propsEd: TTIPropertyGrid
+ Left = 165
+ Height = 295
+ Top = 0
+ Width = 259
+ Align = alClient
+ DefaultValueFont.Color = clWindowText
+ Filter = [tkInteger, tkChar, tkEnumeration, tkFloat, tkSet, tkMethod, tkSString, tkLString, tkAString, tkWString, tkVariant, tkArray, tkRecord, tkInterface, tkClass, tkObject, tkWChar, tkBool, tkInt64, tkQWord, tkDynArray, tkInterfaceRaw, tkProcVar, tkUString, tkUChar, tkHelper]
+ Indent = 16
+ NameFont.Color = clWindowText
+ OnModified = propsEdModified
+ ValueFont.Color = clMaroon
+ end
+ end
+ end
+ end
+ inherited contextMenu: TPopupMenu
+ left = 16
+ top = 8
+ end
+end
diff --git a/src/ce_resman.pas b/src/ce_resman.pas
new file mode 100644
index 00000000..a4c04f84
--- /dev/null
+++ b/src/ce_resman.pas
@@ -0,0 +1,373 @@
+unit ce_resman;
+
+{$I ce_defines.inc}
+
+interface
+
+uses
+ Classes, SysUtils, FileUtil, RTTIGrids, Forms, Controls, Graphics, Dialogs,
+ ExtCtrls, Menus, Buttons, StdCtrls, ce_widget, ce_writableComponent, fpjson,
+ ce_interfaces, ce_project, ce_synmemo, ce_common, process, fpjsonrtti, fpjsondataset;
+
+type
+
+
+ TResourceType = (aFile, aFolder);
+ TResourceFormat = (bytes, utf8, base16, base64);
+
+ TResourceItem = class(TCollectionItem)
+ private
+ fResType: TResourceType;
+ fIDentifier: string;
+ fName: string;
+ fFormat: TResourceFormat;
+ fMetadata: string;
+ published
+ property resourceType: TResourceType read fResType write fResType stored true;
+ property identifier: string read fIDentifier write fIDentifier stored true;
+ property name: string read fName write fName stored true;
+ property format: TResourceFormat read fFormat write fFormat stored true;
+ property metadata: string read fMetadata write fMetadata stored true;
+ end;
+
+ (**
+ * Represents a resource script. The resource script use the
+ * JSON format for a better compatibility with the tool.
+ *)
+ TResourceItems = class(TWritableComponent)
+ private
+ fItems: TCollection;
+ procedure setItems(aValue: TCollection);
+ published
+ property items: TCollection read fItems write setItems;
+ public
+ constructor create(aOwner: TComponent); override;
+ destructor destroy; override;
+ // overides the component streaming methods to use JSON instead of lfm
+ procedure saveToFile(const aFilename: string); override;
+ procedure loadFromFile(const aFilename: string); override;
+ end;
+
+ { TCEResmanWidget }
+ TCEResmanWidget = class(TCEWidget, ICEProjectObserver, ICEMultiDocObserver)
+ BtnAddItem: TBitBtn;
+ btnRemItem: TBitBtn;
+ lstItems: TListBox;
+ Panel1: TPanel;
+ Panel2: TPanel;
+ propsEd: TTIPropertyGrid;
+ Splitter1: TSplitter;
+ procedure BtnAddItemClick(Sender: TObject);
+ procedure btnRemItemClick(Sender: TObject);
+ procedure lstItemsSelectionChange(Sender: TObject; User: boolean);
+ procedure propsEdModified(Sender: TObject);
+ private
+ fProj: TCEProject;
+ fDoc: TCESynMemo;
+ fResourceItems: TResourceItems;
+ // try to load the json resource script for the current doc
+ procedure loadDocResJson;
+ // try to save the json resource script for the current doc
+ procedure saveDocResJson;
+ procedure refreshItemList;
+ procedure updateIdentifierList;
+ procedure genResources;
+ //
+ procedure projNew(aProject: TCEProject);
+ procedure projChanged(aProject: TCEProject);
+ procedure projClosing(aProject: TCEProject);
+ procedure projFocused(aProject: TCEProject);
+ procedure projCompiling(aProject: TCEProject);
+ //
+ procedure docNew(aDoc: TCESynMemo);
+ procedure docFocused(aDoc: TCESynMemo);
+ procedure docChanged(aDoc: TCESynMemo);
+ procedure docClosing(aDoc: TCESynMemo);
+ public
+ constructor create(aOwner: TComponent); override;
+ end;
+
+implementation
+{$R *.lfm}
+
+{$REGION TResourceItems --------------------------------------------------------}
+constructor TResourceItems.Create(aOwner: TCOmponent);
+begin
+ inherited;
+ fItems := TCollection.Create(TResourceItem);
+end;
+
+destructor TResourceItems.destroy;
+begin
+ fItems.Free;
+ inherited;
+end;
+
+procedure TResourceItems.saveToFile(const aFilename: string);
+var
+ json_streamer: TJSONStreamer;
+ json_string: TJSONStringType;
+ str: TMemoryStream;
+begin
+ fHasSaved := true;
+ beforeSave;
+ try
+ json_streamer := TJSONStreamer.Create(nil);
+ str := TMemoryStream.Create;
+ try
+ json_string := json_streamer.ObjectToJSONString(self);
+ str.Write(json_string[1], length(json_string));
+ str.SaveToFile(aFilename);
+ finally
+ json_streamer.Free;
+ str.Free;
+ end;
+ except
+ fHasSaved := false;
+ end;
+ setFilename(aFilename);
+ afterSave;
+end;
+
+procedure TResourceItems.loadFromFile(const aFilename: string);
+var
+ json_destreamer: TJSONDeStreamer;
+ json_string: TJSONStringType;
+ str: TMemoryStream;
+begin
+ fHasLoaded := true;
+ beforeLoad;
+ setFilename(aFilename);
+ json_destreamer := TJSONDeStreamer.Create(nil);
+ str := TMemoryStream.Create;
+ try
+ str.LoadFromFile(aFilename);
+ setLength(json_string, str.Size);
+ str.Read(json_string[1], str.Size);
+ json_destreamer.JSONToObject(json_string, self);
+ finally
+ json_destreamer.Free;
+ str.Free;
+ end;
+ afterLoad;
+end;
+
+procedure TResourceItems.setItems(aValue: TCollection);
+begin
+ fItems.Assign(aValue);
+end;
+{$ENDREGION}
+
+constructor TCEResmanWidget.create(aOwner: TComponent);
+var
+ png: TPortableNetworkGraphic;
+begin
+ inherited;
+ fResourceItems := TResourceItems.create(self);
+ //
+ png := TPortableNetworkGraphic.Create;
+ try
+ png.LoadFromLazarusResource('package_add');
+ BtnAddItem.Glyph.Assign(png);
+ png.LoadFromLazarusResource('package_delete');
+ btnRemItem.Glyph.Assign(png);
+ finally
+ png.Free;
+ end;
+end;
+
+{$REGION ICEProjectObserver ----------------------------------------------------}
+procedure TCEResmanWidget.projNew(aProject: TCEProject);
+begin
+ fProj := aProject;
+ loadDocResJson;
+end;
+
+procedure TCEResmanWidget.projChanged(aProject: TCEProject);
+begin
+ if fProj <> aProject then exit;
+ loadDocResJson;
+end;
+
+procedure TCEResmanWidget.projClosing(aProject: TCEProject);
+begin
+ if fProj <> aProject then exit;
+ fProj := nil;
+ propsEd.TIObject := nil;
+ propsEd.ItemIndex := -1;
+ fResourceItems.Items.Clear;
+ refreshItemList;
+end;
+
+procedure TCEResmanWidget.projFocused(aProject: TCEProject);
+begin
+ fProj := aProject;
+ loadDocResJson;
+end;
+
+procedure TCEResmanWidget.projCompiling(aProject: TCEProject);
+begin
+ if fProj <> aProject then exit;
+ saveDocResJson;
+ genResources;
+end;
+{$ENDREGION}
+
+{$REGION ICEMultiDocObserver ---------------------------------------------------}
+procedure TCEResmanWidget.docNew(aDoc: TCESynMemo);
+begin
+end;
+
+procedure TCEResmanWidget.docChanged(aDoc: TCESynMemo);
+begin
+ if fDoc <> aDoc then exit;
+end;
+
+procedure TCEResmanWidget.docClosing(aDoc: TCESynMemo);
+begin
+ if fDoc <> aDoc then exit;
+ //
+ saveDocResJson;
+ fDoc := nil;
+ refreshItemList;
+end;
+
+procedure TCEResmanWidget.docFocused(aDoc: TCESynMemo);
+begin
+ fDoc := aDoc;
+ loadDocResJson;
+end;
+{$ENDREGION}
+
+{$REGION Resources things -----------------------------------------------------}
+procedure TCEResmanWidget.lstItemsSelectionChange(Sender: TObject; User: boolean);
+begin
+ if lstItems.Count = 0 then exit;
+ if lstItems.ItemIndex = -1 then exit;
+ //
+ propsEd.TIObject := TPersistent(lstItems.Items.Objects[lstItems.ItemIndex]);
+end;
+
+procedure TCEResmanWidget.propsEdModified(Sender: TObject);
+begin
+ if propsEd.ItemIndex = -1 then
+ exit;
+ if propsEd.Rows[propsEd.ItemIndex].Name = 'identifier' then
+ updateIdentifierList;
+ saveDocResJson;
+end;
+
+procedure TCEResmanWidget.BtnAddItemClick(Sender: TObject);
+var
+ item: TResourceItem;
+begin
+ item := TResourceItem(fResourceItems.items.Add);
+ item.identifier := format('' ,[item.ID]);
+ refreshItemList;
+ saveDocResJson;
+end;
+
+procedure TCEResmanWidget.btnRemItemClick(Sender: TObject);
+begin
+ if lstItems.ItemIndex = -1 then
+ exit;
+ propsEd.TIObject := nil;
+ propsEd.ItemIndex := -1;
+ fResourceItems.items.Delete(lstItems.ItemIndex);
+ refreshItemList;
+ saveDocResJson;
+end;
+
+procedure TCEResmanWidget.loadDocResJson;
+var
+ fname: string;
+begin
+ if fDoc = nil then exit;
+ if fProj = nil then exit;
+ if not fProj.isProjectSource(fDoc.fileName) then exit;
+ //
+ fname := stripFileExt(fDoc.fileName) + '.resman';
+ propsEd.TIObject := nil;
+ propsEd.ItemIndex := -1;
+ fResourceItems.Items.Clear;
+ if fileExists(fname) then
+ fResourceItems.loadFromFile(fname);
+ refreshItemList;
+end;
+
+procedure TCEResmanWidget.saveDocResJson;
+var
+ fname: string;
+begin
+ if fDoc = nil then exit;
+ if fProj = nil then exit;
+ if not fProj.isProjectSource(fDoc.fileName) then exit;
+ //
+ fname := stripFileExt(fDoc.fileName) + '.resman';
+ if fResourceItems.Items.Count = 0 then exit;
+ //
+ fResourceItems.saveToFile(fname);
+end;
+
+procedure TCEResmanWidget.genResources;
+var
+ proc: TProcess;
+ fname: string;
+ i: Integer;
+begin
+ if fProj = nil then exit;
+ if not exeInSysPath('resman' + exeExt) then exit;
+ //
+ proc := Tprocess.Create(nil);
+ try
+ proc.Executable:= 'resman' + exeExt;
+ //proc.Options := [poUsePipes, poStderrToOutPut];
+ //proc.ShowWindow := swoHIDE;
+ proc.Parameters.Add('-v');
+ for i := 0 to fProj.Sources.Count-1 do
+ begin
+ fname := fProj.getAbsoluteSourceName(i);
+ fname := stripFileExt(fname) + '.resman';
+ if not FileExists(fname) then continue;
+ proc.Parameters.Add(fname);
+ end;
+ proc.Execute;
+ while proc.Running do;
+ // output to project message...
+ finally
+ proc.Free;
+ end;
+end;
+
+procedure TCEResmanWidget.refreshItemList;
+var
+ i: Integer;
+ item: TResourceItem;
+begin
+ propsEd.TIObject := nil;
+ propsEd.ItemIndex := -1;
+ lstItems.Items.Clear;
+ for i:= 0 to fResourceItems.items.Count-1 do
+ begin
+ item := TResourceItem(fResourceItems.items.Items[i]);
+ lstItems.Items.AddObject(item.identifier, item);
+ end;
+ if lstItems.Count > 0 then
+ lstItems.ItemIndex := 0;
+end;
+
+procedure TCEResmanWidget.updateIdentifierList;
+var
+ i: Integer;
+ item: TResourceItem;
+begin
+ for i:= 0 to fResourceItems.items.Count-1 do
+ begin
+ item := TResourceItem(fResourceItems.items.Items[i]);
+ lstItems.Items.Strings[i] := item.identifier;
+ end;
+end;
+{$ENDREGION}
+
+end.
+