I would like to define descendants of TPath class for SVG shapes that I use regularly, to register them on the palette instead of having to paste in the object inspector long SVG path strings.
this would also have the advantage of being able to, by changing a single string somewhere, impact all my objects of type TSVGxxx without having to run after them and edit each one after the other.
The first step was done:
TSVGsomeshape = class (TPath)
and in the Loaded:
Data.Data := 'an SVG path';
The problem:
when this path gets too long, Delphi's string literal limitation forces me to break it down and concatenate its bits. Which I would like to avoid.
Resources come to mind. the idea: put all SVG paths into some RC file (simple paste of path as-is, no concatenation hassle), which in turns compiles and generates a RES file.
However, it seems that with paths being loaded as resources, I lose the ability to see those paths when dropping my TSVGxxx at design-time, which sort of defeats the purpose.
Am i over-complicating things, and is there a more simple approach to all of this?
edit 1:
In VCL, the main way of using resources was to add the {$R } rc or res file (I still prefer using the RC one and have it compiled every time, and ignore the generated res from version control).
I would consider for this a single resource file "svg.rc / res", which contains multiple text-only entries describing the path section only of each SVG.
LoadFromStream the resource into a string list, and feed it to the "Data" property, pretty straightforward.
Admittedly, I don't recall ever having done so in a control at design time. This is the first difficulty encountered using resources approach: getting "resource not found".
Is doing so at design time generally doable?
The second difficulty I suppose is more related to FMX, due to the multitude approaches to add resources (deployment menu, resources and images menu, standard {$R } approach). All of which also yielded resource not found at runtime.
I've used the Deployment menu for loading custom fonts, but not multi-key rc files.
How could this be done?
Thanks
PS: i also had a look at SKIA4Delphi, and while great, may be overkill for the simple SVG paths that I am interested in (and also a Google dependency)
Note on tags: while the question does mention SVG and TPath, the problem at its core concerns neither of them, and as such, SVG and FMX tags are omitted.
it ended up working out as per the comments above.
placed the res file in the package's directory (previously had it in search path).
pointed to the res file using {$R myres.res}
loaded the resource string as RC_DATA, not sure if i should have used plain text instead (the resource editor restricted namings to numbers when using string tables)
function TSVGPlus.LoadSVGpath: string;
var RS: TResourceStream;
SL: TSTringList;
begin
RS := TResourceStream.Create(FindClassHInstance(Self.ClassType), 'PLUS', RT_RCDATA);
SL := TStringList.Create;
try
SL.LoadFromStream(RS);
Result := SL.Text;
finally
SL.Free;
RS.Free;
end;
end;
rebuilding the package, the plus shows up at design time
no additional deployment was needed to get the plus sign showing at runtime, be it on Windows or on Android
Related
I have inherited a legacy Delphi project for which I have to do a code modification. But to start with, I have problem compiling the project into a "correct" application. As I am not familiar with Delphi, I am not sure what is missing.
So far, the only changes I have done are around PATH for references and packages wherever I could find an absolute path that was different on my PC. It is compiling and running. However, one of the variable remains "incorrectly" initialized at run-time (value is nil). I am a bit surprised because I have not changed anything in the code, that is from an application otherwise running.
The type of this variable is defined in a package library (for which I have the code as well, that compiles, and that is in the list of "Packages" checked in the Project Options.
Some code sample:
Unit Unit3
interface
uses
VCLZ;
TFrmCCB = class(TForm)
vz: TVCLZ;
implementation
procedure TFrmCCB.FormCreate(Sender: TObject)
var
[...]
begin
vz.SetPath := 'C:\'; //at this point vz is nil
Do I have to add some code to get vz to not be nil, or does it look like a configuration problem with the definition not found at run time or something like that? I would seem strange that something is missing in the code though, as I have not done any change there yet and the program is otherwise running.
It sounds like vz: TVCLZ is a component on a form.
When you first open the form if you don't have the component (package) installed in the IDE it asks you if you want to remove the component (because the IDE doesn't know how to render/instantiate it at design-time). If you choose to remove it the code associated with that component remains.
So you remain with something similar to what you have. The form doesn't create the component at run-time (because it doesn't have it anymore) the vz member remains uninitialized.
So, you have to get the sources of the project with the component on the form and be careful not to delete it.
Or, you care create the instance yourself. At FormCreate just create it: vz := TVCLZ.Create(Self); before you use it. But keep in mind that all the properties set in design will be lost and you have to set them manually in code.
I have a Delphi app with localized resource DLLs.
I would like to do a search and replace on a resource string once it is loaded from the DLL.
Is there any internal procedure to hook into to do this?
I need a way to just do search and replace for any resource string loaded, and not just change a specific resource string.
Raoul.
Yes you have to hook the LoadResString() procedure defined in System.pas.
See for instance how we do in http://synopse.info/fossil/finfo?name=SQLite3/SQLite3i18n.pas
There is everything in this unit code:
For extracting all resource strings (but you may also compile the executable with the "detailed map" option to get the same list);
For replacing all resource strings on the fly, to your expected language;
For caching all resource string, since default LoadResString API can be slow.
You have also similar code around, but this is one included in our Open Source mORMot framework, working from Delphi 6 up to XE2. There is also code to change on the fly all .dfm content (i.e. your forms), from the same translated text file. There is a chapter about that in the framework documentation at http://mormot.net
I think you will get here every code pattern needed for your task.
With Delphi XE I've installed THTMLViewer. I see plenty of examples on how to read an HTM file (LoardFromFile) and display it in the Viewer. What I want to do is create the HTM at runtime in my program. If I had a RichEdit component I would have several SelText := lines.
Of course I can add lines to a stringlist, then SavetoFile the stringlist and finally LoadFromFile into htmlViewer, but that seems like an unnecessary extra step.
Is there a Memo.Lines or some other property of THtmlViewer where I can Add the desired lines of HTML coding?
If you are using a version of the component that supports it then LoadFromString would probably be the simplest function to use.
I am surprised that you didn't find that in the source. As I seem to recall it being available for some time.
If you need a newer version of the component source you can find it here: THTMLViewer Project
I am using Delphi 2010 and GExperts stable release 1.35
I am testing the "Replace Components" expert.
I add a main form and a secondary form. Each has three TEdits on it.
I use the Replace Components Expert to replace all TEdits with TRzEdit. I Check the Replace all components on all forms of the project.
It works, it replaces them all. However, it doesn't do anything to the DFM file. How do i make it replace those instances.
If i save, compile, or rebuild, i get errors.
If i try to view the form as text after replacing, i get errors.
Can someone explain to me the steps to make this work?
Thanks
I just tried it with Delphi XE and GExperts 1.35 and it does indeed crash now even after a single "Replace Selected". (It used to work fine in the past).
It seems that using the hidden menu Project | Clear Unit Cache just after Replace Components then doing a full build before trying any Save/Compile/View As Text... fixes the problem.
I think this menu is surfaced with cnPack. I don't have it and cannot guarantee, but you can easily do it yourself by adding the following unit in one of your installed packages:
unit FGEnableHiddenMenus;
interface
procedure Register;
implementation
uses
Classes, Forms, Menus;
procedure Register;
var
Comp: TComponent;
begin
//Make a hidden menu item visible
Comp := Application.MainForm.FindComponent('ProjectClearUnitCacheItem');
if Comp is TMenuItem then
TMenuItem(Comp).Visible := True;
end;
end.
Source: Brian Long's old goodies
Update:
I had to replace a couple of TEdit by TDBEdit on our main project at work and this trick worked.
But on a new Project with 3 Forms, it failed consistently to write/commit/save the changes on the last Form (same environment).
FYI, I tried with and without DDevExtensions 2.5 and IDEFixPack 4.6.1
Update2:
Went digging in the GExperts forum as suggested by Ulrich and finally found a possible explanation. The new property Touch does not like being copied from one component to another when the source is destroyed (causing the AV).
The suggested workaround is to do a bidirectional mapping in the Expert Settings to disable the copy for this property:
You might be running into this bug while trying to copy the Touch property from the old to the new component, but it has a workaround you can fairly easily test:
http://tech.groups.yahoo.com/group/GExpertsDiscuss/message/3994
Details:
There is a limitation/bug in Delphi 2010 and XE where if you assign a
Component.Touch property from one component to another and destroy the
original component that the new component becomes corrupt (it isn't
like component/interface references, where they either auto-correct
themselves or are reference counted).
For the moment, you can assign a bi-directional replace component
property map from TPanel.Touch to TGroupBox.Touch (use the two components being replaced in your specific case) that is marked as
a disabled property map, and that will work around this problem. Our next release
will not try to assign that property any longer.
GExperts 1.36 is also now available and includes a workaround for this issue. The workaround has been in the GExperts version control system and in testing for several weeks already.
As we know Build compiles all used units and Compile compiles only changed used units. But when you are working with ifdefs and need to change it a lot of times you must Build the project a lot of times.
If you have to much libraries that you know that this IFDEF will not change the behavior, there is no reason to build this library again.
What I want to do discovery is a way to say to the IDE that some files was changed and call the Compile and not the Build.
How to get the units and every else I know, but some know how to set the unit as modified?
Tks
Couldn't find a why to solve my problem yet. I found a way to set it as modified the problem is that it doesn't force the IDE to build it as I thought it'll.
Some one know what checks is made to an archive be sent to compile ?
Solved in a simpler way. I just deleted the DCU of the file and ok, it'll compile it again. :D
http://paste.ideaslabs.com/show/KCB9cq2Z8c
Let us assume that what you want is to mark editor buffers modified. That assumes there is an editor buffer to modify, which in most cases, there isn't, for most items in your Project. Let's suppose that you ALWAYS have every file in your project open at all times, then perhaps you can tweak those open units, this way.
My thinking is that you would actually need not only to mark editor buffers modified, but also TOUCH all the files on disk that would need to be rebuilt because of the #define change.
To know which files are affected, you would need to read all the files. I think, that the odds of you doing this reliably, over and over again, and faster than just doing a BUILD are pretty slim.
But if you did decide to go for it, you need not only to modify buffer Modified flags, but also, for any other file in the current project group, or which is in a folder included in the search or library path, find any file which might be affected.
You can see perhaps that this is more complex than you first thought.
And, as far as OTA goes, here is IOTAEditor property, Modified, it is read only.
The OTA doesn't want you changing it:
IOTAEditor = interface(IUnknown)
['{F17A7BD0-E07D-11D1-AB0B-00C04FB16FB3}']
...
function GetModified: Boolean;
..
property Modified: Boolean read GetModified;
..
end;
But you could actually modify the buffer CONTENT itself, and that would set the flag.
Use IOTAEditorContent, get the content (IStream), modify it (add a space), then modify it again (take away the space). You have now set the dirty bit (call GetModified just for luck).
I found some sample code from JCL JclStackTraceViewerStackCodeUtils.pas that shows you how to read editor content using IOTAEditorContent:
function GetFileEditorContent(const AFileName: string): IStream;
var
I: Integer;
Module: IOTAModule;
EditorContent: IOTAEditorContent;
begin
Result := nil;
Module := (BorlandIDEServices as IOTAModuleServices).FindModule(AFileName);
if Assigned(Module) then
begin
for I := 0 to Module.ModuleFileCount - 1 do
if Supports(Module.ModuleFileEditors[I], IOTAEditorContent, EditorContent) then
begin
Result := EditorContent.Content;
Break;
end;
end;
end;
After re-reading your question, it seems to me you only want to mark open editor buffers as all changed, and then do a compile, for speed. So you might want to do this: Take the code above, get the IOTAEditorContent using Supports(), and tweak each one.
Update: Short and sweet version: Modifying the buffer isn't sufficient. Plus, you won't have buffers for the files you need to change, plus Touching the file on the disk doesn't do what you want. So no, you can't do exactly what you wanted to do. And even if you could somehow modify the compiler's ability to do Make-style dependency and modification checks, you would probably cause a lot of problems inside the IDE.
On IOTAModule70 = interface(IOTAModule50) I'll find MarkModified:
Description:
{ MarkModifed will mark this module as "modified" without actually indicating
why is it modified (which will cause internal file dates to remain constant).
This will force the IDE to ask to save this module when the user attempts
to close it. It will also clear the "discardability" of a new unnamed
module such as when File|New|Application is selected. }