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. +